const { app } = require('electron');
-const { FiltersEngine } = require('@cliqz/adblocker');
+const { FiltersEngine, makeRequest } = require('@cliqz/adblocker');
const Axios = require('axios');
const fs = require('fs');
const path = require('path');
const lists = require('./Lists.json');
+const Config = require('electron-store');
+const config = new Config();
+
let engine;
if (!fs.existsSync(path.join(app.getPath('userData'), 'Files'))) {
return engine != undefined;
}
+module.exports.runAdblockService = (window, windowId, id, session) => {
+ session.webRequest.onBeforeRequest({ urls: ['<all_urls>'] }, async (details, callback) => {
+ if (engine && config.get('adBlocker')) {
+ const { match, redirect } = engine.match(makeRequest({ type: details.resourceType, url: details.url }, tldts.parse));
+
+ if (match || redirect) {
+ window.webContents.send(`blocked-ad-${windowId}`, { id });
+
+ if (redirect) {
+ callback({ redirectURL: redirect });
+ } else {
+ callback({ cancel: true });
+ }
+
+ return;
+ }
+ }
+
+ callback({ cancel: false });
+ });
+}
+
module.exports.removeAds = (url, webContents) => {
if (engine == undefined) return;
const { styles, scripts } = engine.getCosmeticsFilters({
const url = require('url');
const os = require('os');
+const { autoUpdater } = require('electron-updater');
+
const { extensionsMain } = require('electron-extensions');
const config = require('./Config');
module.exports = class Application {
loadWindow = () => {
- // app.disableHardwareAcceleration();
protocol.registerSchemesAsPrivileged([
{ scheme: protocolStr, privileges: { standard: true, bypassCSP: true, secure: true } },
{ scheme: fileProtocolStr, privileges: { standard: false, bypassCSP: true, secure: true } }
]);
+ autoUpdater.on('checking-for-update', () => {
+ console.log('Checking for update...')
+ })
+ autoUpdater.on('update-available', (info) => {
+ console.log('Update available.');
+ })
+ autoUpdater.on('update-not-available', (info) => {
+ console.log('Update not available.');
+ })
+ autoUpdater.on('error', (err) => {
+ console.log('Error in auto-updater. ' + err);
+ })
+ autoUpdater.on('download-progress', (progressObj) => {
+ console.log('Download speed: ' + progressObj.bytesPerSecond + ' - Downloaded ' + progressObj.percent + '% (' + progressObj.transferred + "/" + progressObj.total + ')');
+ })
+ autoUpdater.on('update-downloaded', (info) => {
+ console.log('Update downloaded.');
+ });
+
app.on('ready', () => {
process.env.GOOGLE_API_KEY = config.googleAPIKey;
-
+
session.defaultSession.setUserAgent(session.defaultSession.getUserAgent().replace(/ Electron\/[0-9\.]*/g, ''));
+ autoUpdater.checkForUpdatesAndNotify();
Menu.setApplicationMenu(null);
windowManager.addWindow();
subWindow.setMinimizable(false);
subWindow.setMaximizable(false);
const startUrl = process.env.ELECTRON_START_URL || url.format({
- pathname: path.join(__dirname, '/../build/index.html'), // 警告:このファイルを移動する場合ここの相対パスの指定に注意してください
+ pathname: path.join(__dirname, '/../build/index.html'),
protocol: 'file:',
slashes: true,
hash: '/authentication',
return package.version;
}
+global.getAppChannel = () => {
+ if (!(location.protocol !== `${protocolStr}://` || location.protocol !== `${fileProtocolStr}://`)) return;
+
+ return package.flast_channel;
+}
+
/*
// ====================================================================== //
// ====================================================================== //
url: 'https://search.goo.ne.jp/web.jsp?MT=%s'
},
{
- name: 'Baido',
+ name: 'Baidu',
url: 'https://www.baidu.com/s?wd=%s'
},
{
timestampData: true
});
-const { loadFilters, updateFilters, removeAds } = require('./AdBlocker');
+const { loadFilters, updateFilters, runAdblockService, removeAds } = require('./AdBlocker');
let floatingWindows = [];
let views = [];
-let tabCount = 0;
getBaseWindow = (width = 1100, height = 680, minWidth = 320, minHeight = 200, x, y, frame = false) => {
return new BrowserWindow({
this.windows.delete(id);
});
- /*
- ['resize', 'move'].forEach(ev => {
- window.on(ev, () => {
- config.set('window.isMaximized', window.isMaximized());
- config.set('window.bounds', window.getBounds());
- })
- });
- */
-
window.on('close', (e) => {
delete views[id];
for (var i = 0; i < views[id].length; i++) {
const url = views[id][i].view.webContents.getURL();
- datas.push({ id: views[id][i].id, title: views[id][i].view.webContents.getTitle(), url: url, icon: this.getFavicon(url) });
+ datas.push({ id: views[id][i].view.webContents.id, title: views[id][i].view.webContents.getTitle(), url: url, icon: this.getFavicon(url) });
}
e.sender.send(`browserview-get-${id}`, { views: datas });
});
ipcMain.on(`browserview-goBack-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
if (webContents.canGoBack())
webContents.goBack();
ipcMain.on(`browserview-goForward-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
if (webContents.canGoForward())
webContents.goForward();
ipcMain.on(`browserview-reload-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
webContents.reload();
}
ipcMain.on(`browserview-stop-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
webContents.stop();
}
ipcMain.on(`browserview-goHome-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
webContents.loadURL(config.get('homePage.defaultPage'));
}
ipcMain.on(`browserview-loadURL-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
webContents.loadURL(args.url);
}
ipcMain.on(`browserview-loadFile-${id}`, (e, args) => {
views[id].filter(function (view, i) {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let webContents = views[id][i].view.webContents;
webContents.loadFile(args.url);
}
ipcMain.on(`data-bookmark-add-${id}`, (e, args) => {
views[id].filter((view, i) => {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let v = views[id][i].view;
db.bookmarks.insert({ title: v.webContents.getTitle(), url: v.webContents.getURL(), isPrivate: args.isPrivate });
- this.updateBookmarkState(id, args.id, v);
+ this.updateBookmarkState(id, v);
}
});
});
ipcMain.on(`data-bookmark-remove-${id}`, (e, args) => {
views[id].filter((view, i) => {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let v = views[id][i].view;
db.bookmarks.remove({ url: v.webContents.getURL(), isPrivate: args.isPrivate }, {});
- this.updateBookmarkState(id, args.id, v);
+ this.updateBookmarkState(id, v);
}
});
});
ipcMain.on(`data-bookmark-has-${id}`, (e, args) => {
views[id].filter((view, i) => {
- if (view.id == args.id) {
+ if (view.view.webContents.id == args.id) {
let v = views[id][i].view;
db.bookmarks.find({ url: v.webContents.getURL(), isPrivate: args.isPrivate }, (err, docs) => {
e.sender.send(`data-bookmark-has-${id}`, { isBookmarked: (docs.length > 0 ? true : false) });
return url.startsWith(`${protocolStr}://`) || url.startsWith(`${fileProtocolStr}://`) ? undefined : `https://www.google.com/s2/favicons?domain=${parsed.protocol}//${parsed.hostname}`;
}
- updateNavigationState = (windowId, id, view) => {
- const window = this.windows.get(windowId);
- window.webContents.send(`update-navigation-state-${windowId}`, {
- id: id,
+ updateNavigationState = (id, view) => {
+ const window = this.windows.get(id);
+ window.webContents.send(`update-navigation-state-${id}`, {
+ id: view.webContents.id,
canGoBack: view.webContents.canGoBack(),
canGoForward: view.webContents.canGoForward(),
});
}
- updateBookmarkState = (windowId, id, view) => {
- const window = this.windows.get(windowId);
- db.bookmarks.find({ url: view.webContents.getURL(), isPrivate: (String(windowId).startsWith('private')) }, (err, docs) => {
+ updateBookmarkState = (id, view) => {
+ const window = this.windows.get(id);
+ db.bookmarks.find({ url: view.webContents.getURL(), isPrivate: (String(id).startsWith('private')) }, (err, docs) => {
const url = view.webContents.getURL();
- window.webContents.send(`browserview-load-${windowId}`, { id: id, title: view.webContents.getTitle(), url: url, icon: this.getFavicon(url), isBookmarked: (docs.length > 0 ? true : false) });
+ window.webContents.send(`browserview-load-${id}`, { id: view.webContents.id, title: view.webContents.getTitle(), url: url, icon: this.getFavicon(url), isBookmarked: (docs.length > 0 ? true : false) });
});
}
view.setAutoResize({ width: true, height: true });
}
- addView = (windowId, url, isActive) => {
- if (String(windowId).startsWith('private')) {
- loadSessionAndProtocolWithPrivateMode(windowId);
+ addView = (id, url, isActive) => {
+ if (String(id).startsWith('private')) {
+ loadSessionAndProtocolWithPrivateMode(id);
}
- const id = tabCount++;
- this.addTab(windowId, id, url, isActive);
+ this.addTab(id, url, isActive);
}
removeView = (windowId, id) => {
views[windowId].filter((view, i) => {
- if (view.id == id) {
+ if (view.view.webContents.id == id) {
const index = i;
if (index + 1 < views[windowId].length) {
selectView = (windowId, id) => {
const window = this.windows.get(windowId);
views[windowId].filter((view, i) => {
- if (id == view.id) {
+ if (id == view.view.webContents.id) {
window.setBrowserView(views[windowId][i].view);
window.setTitle(views[windowId][i].view.webContents.getTitle());
window.webContents.send(`browserview-set-${windowId}`, { id: id });
for (var i = 0; i < views[windowId].length; i++) {
const url = views[windowId][i].view.webContents.getURL();
- datas.push({ id: views[windowId][i].id, title: views[windowId][i].view.webContents.getTitle(), url: url, icon: this.getFavicon(url) });
+ datas.push({ id: views[windowId][i].view.webContents.id, title: views[windowId][i].view.webContents.getTitle(), url: url, icon: this.getFavicon(url) });
}
const window = this.windows.get(windowId);
window.webContents.send(`browserview-get-${windowId}`, { views: datas });
}
- addTab = (windowId, id, url = config.get('homePage.defaultPage'), isActive = true) => {
- const window = this.windows.get(windowId);
-
+ addTab = (windowId, url = config.get('homePage.defaultPage'), isActive = true) => {
const view = new BrowserView({
webPreferences: {
nodeIntegration: false,
}
});
+ const window = this.windows.get(windowId);
+ const id = view.webContents.id;
+
+ runAdblockService(window, windowId, id, view.webContents.session);
+
view.webContents.on('did-start-loading', () => {
if (view.isDestroyed()) return;
if (view.isDestroyed()) return;
window.setTitle(`${view.webContents.getTitle()} - ${pkg.name}`);
- this.updateBookmarkState(windowId, id, view);
+ this.updateBookmarkState(windowId, view);
+
+ this.updateNavigationState(windowId, view);
+ });
+ view.webContents.on('did-fail-load', (e, code, description, url, isMainFrame, processId, routingId) => {
+ if (view.isDestroyed()) return;
- this.updateNavigationState(windowId, id, view);
+ dialog.showMessageBox({message: `${code}: ${description}`});
});
view.webContents.on('did-start-navigation', (e) => {
if (config.get('adBlocker') && !(view.webContents.getURL().startsWith(`${protocolStr}://`) || view.webContents.getURL().startsWith(`${fileProtocolStr}://`)))
removeAds(url, view.webContents);
- this.updateNavigationState(windowId, id, view);
+ this.updateNavigationState(windowId, view);
});
view.webContents.on('page-title-updated', (e) => {
if (view.isDestroyed()) return;
window.setTitle(`${view.webContents.getTitle()} - ${pkg.name}`);
- this.updateBookmarkState(windowId, id, view);
+ this.updateBookmarkState(windowId, view);
if (!String(windowId).startsWith('private') && !(view.webContents.getURL().startsWith(`${protocolStr}://`) || view.webContents.getURL().startsWith(`${fileProtocolStr}://`)))
db.historys.insert({ title: view.webContents.getTitle(), url: view.webContents.getURL() });
- this.updateNavigationState(windowId, id, view);
+ this.updateNavigationState(windowId, view);
});
view.webContents.on('page-favicon-updated', (e, favicons) => {
window.webContents.send(`browserview-load-${windowId}`, { id: id, title: view.webContents.getTitle(), url: url, icon: this.getFavicon(url), isBookmarked: (docs.length > 0 ? true : false) });
});
- this.updateNavigationState(windowId, id, view);
+ this.updateNavigationState(windowId, view);
});
view.webContents.on('did-change-theme-color', (e, color) => {
{
"name": "Flast",
"description": "Cross-platform browser based on Chromium.",
- "version": "1.6.9",
+ "version": "1.7.6",
+ "flast_channel": "Stable",
"private": true,
"main": "electron/Starter.js",
"homepage": "./",
"electron-packager": "13.1.1",
"electron-platform": "^1.2.0",
"electron-store": "3.2.0",
+ "electron-updater": "^4.0.6",
"nedb": "^1.8.0",
"react": "16.8.6",
"react-dom": "16.8.6",
<div class="panel-heading">プライベート ブックマーク</div>
<div class="panel-body">
<div class="table-responsive">
- <table class="table table-striped table-hover table-style" id="privMarkList">
+ <table class="table table-striped table-hover table-style" id="privMarksList">
<thead>
<tr>
<th class="table-icon"></th>
<div class="panel-heading">ブックマーク</div>
<div class="panel-body">
<div class="table-responsive">
- <table class="table table-striped table-hover table-style" id="markList">
+ <table class="table table-striped table-hover table-style" id="marksList">
<thead>
<tr>
<th class="table-icon"></th>
if (navigator.userAgent.indexOf('PrivMode') != -1) {
getBookmarks(true).then((data) => {
data.forEach((item, i) => {
- $('#markList').append(
+ $('#privMarksList').append(
$('<tr></tr>')
.append($('<td class="table-icon"></td>').append($(`<img src="http://www.google.com/s2/favicons?domain=${new URL(item.url).origin}" />`)))
.append($('<td class="table-title"></td>').append($(`<a href="${item.url}"></a>`).text(item.title)))
});
getBookmarks(false).then((data) => {
data.forEach((item, i) => {
- $('#markList').append(
+ $('#marksList').append(
$('<tr></tr>')
.append($('<td class="table-icon"></td>').append($(`<img src="http://www.google.com/s2/favicons?domain=${new URL(item.url).origin}" />`)))
.append($('<td class="table-title"></td>').append($(`<a href="${item.url}"></a>`).text(item.title)))
} else {
getBookmarks(false).then((data) => {
data.forEach((item, i) => {
- $('#markList').append(
+ $('#marksList').append(
$('<tr></tr>')
.append($('<td class="table-icon"></td>').append($(`<img src="http://www.google.com/s2/favicons?domain=${new URL(item.url).origin}" />`)))
.append($('<td class="table-title"></td>').append($(`<a href="${item.url}"></a>`).text(item.title)))
</div>
</div>
</div>
- <div class="panel panel-default" id="normal">
+ <div class="panel panel-default">
<div class="panel-heading">
- 最近の履歴
- <a href="flast://history" target="_blank" class="text-muted"
+ アプリ
+ <a href="flast://bookmarks" target="_blank" class="text-muted"
style="float: right;">すべての履歴を表示</a>
</div>
<div class="panel-body">
<div class="table-responsive">
- <table class="table table-striped table-hover table-style" id="historyList">
+ <table class="table table-striped table-hover table-style" id="appsList">
<thead>
<tr>
<th class="table-icon"></th>
</div>
</div>
</div>
- <div class="panel panel-default" id="normal">
+ <div class="panel panel-default">
<div class="panel-heading">
- 最近の履歴
- <a href="flast://history" target="_blank" class="text-muted"
- style="float: right;">すべての履歴を表示</a>
+ 最近追加したブックマーク
+ <a href="flast://bookmarks" target="_blank" class="text-muted"
+ style="float: right;">すべてのブックマークを表示</a>
</div>
<div class="panel-body">
<div class="table-responsive">
- <table class="table table-striped table-hover table-style" id="historyList">
+ <table class="table table-striped table-hover table-style" id="marksList">
<thead>
<tr>
<th class="table-icon"></th>
<th class="table-title">タイトル</th>
<th class="table-url">URL</th>
- <th class="table-date">閲覧日時</th>
+ <th class="table-date">追加日時</th>
</tr>
</thead>
<tbody>
v++;
});
});
+ getBookmarks(false).then((data) => {
+ let v = 0;
+ data.forEach((item, i) => {
+ if (v > 9) return;
+ $('#marksList').append(
+ $('<tr></tr>')
+ .append($('<td class="table-icon"></td>').append($(`<img src="http://www.google.com/s2/favicons?domain=${new URL(item.url).origin}" />`)))
+ .append($('<td class="table-title"></td>').append($(`<a href="${item.url}"></a>`).text(item.title)))
+ .append($(`<td class="table-url" title="${item.url}"></td>`).text(item.url))
+ .append($('<td></td>').text(moment(item.createdAt).format('YYYY/MM/DD HH:mm')))
+ );
+ v++;
+ });
+ });
- var now = new Date();
- var start = new Date(now.getFullYear(), 0, 0);
- var diff = now - start;
- var oneDay = 1000 * 60 * 60 * 24;
- var day = Math.floor(diff / oneDay);
- if (day > 100 && day) {
- day = day.toString().slice(1, 3);
- day = parseInt(day);
- }
+ if (navigator.userAgent.indexOf('PrivMode') == -1) {
+ var now = new Date();
+ var start = new Date(now.getFullYear(), 0, 0);
+ var diff = now - start;
+ var oneDay = 1000 * 60 * 60 * 24;
+ var day = Math.floor(diff / oneDay);
+ if (day > 100 && day) {
+ day = day.toString().slice(1, 3);
+ day = parseInt(day);
+ }
- // Set background image
+ $('.test').css({
+ 'background': `linear-gradient(to bottom, #00000000 65%, #FFF), url(flast-file:///photos/${day}.jpg)`,
+ 'background-repeat': 'no-repeat',
+ 'background-size': 'cover'
+ });
- // Set background image
- $('.test').css({
- 'background': `linear-gradient(to bottom, #00000000 65%, #FFF), url(flast-file:///photos/${day}.jpg)`,
- 'background-repeat': 'no-repeat',
- 'background-size': 'cover'
- });
+ if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition((position) => {
+ const positionData = position.coords;
- //現在位置の取得ができるかどうか
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition((position) => {
- const positionData = position.coords;
+ const lat = positionData.latitude;
+ const lon = positionData.longitude;
+ $('.location').text('現在の位置(' + Math.floor(lat * 100) / 100 + ',' + Math.floor(lon * 100) / 100 + ')');
- const lat = positionData.latitude;
- const lon = positionData.longitude;
- $('.location').text('現在の位置(' + Math.floor(lat * 100) / 100 + ',' + Math.floor(lon * 100) / 100 + ')');
+ //現在の天気データを呼び出し
+ $.ajax({
+ url: 'http://api.openweathermap.org/data/2.5/weather',
+ dataType: 'jsonp',
+ data: `lat=${lat}&lon=${lon}&appid=${APIKEY}`,
+ //天気データ呼び出し成功時の挙動
+ success: function (data) {
+ $('.location').text(data.name);
- //現在の天気データを呼び出し
- $.ajax({
- url: 'http://api.openweathermap.org/data/2.5/weather',
- dataType: 'jsonp',
- data: `lat=${lat}&lon=${lon}&appid=${APIKEY}`,
- //天気データ呼び出し成功時の挙動
- success: function (data) {
- console.log(data);
- if (data.weather[0].main === "Clear") {
- $('body').css('background-image', 'url(Sunny.jpg)');
- $('.dayWeather').text("晴れ");
- } else if (data.weather[0].main === "Rain") {
- $('body').css('background-image', 'url(Rain.jpg)');
- $('.dayWeather').text("雨");
- } else if (data.weather[0].main === "Clouds") {
- $('body').css('background-image', 'url(Cloudy.jpg)');
- $('.dayWeather').text("くもり");
- } else if (data.weather[0].main === "Snow") {
- $('body').css('background-image', 'url(Snowy.jpg)');
- $('.dayWeather').text("雪");
+ if (data.weather[0].main === "Clear") {
+ $('body').css('background-image', 'url(Sunny.jpg)');
+ $('.dayWeather').text("晴れ");
+ } else if (data.weather[0].main === "Rain") {
+ $('body').css('background-image', 'url(Rain.jpg)');
+ $('.dayWeather').text("雨");
+ } else if (data.weather[0].main === "Clouds") {
+ $('body').css('background-image', 'url(Cloudy.jpg)');
+ $('.dayWeather').text("くもり");
+ } else if (data.weather[0].main === "Snow") {
+ $('body').css('background-image', 'url(Snowy.jpg)');
+ $('.dayWeather').text("雪");
+ }
+
+ //各データの表示
+ $('.nowTemp').text(Math.floor((data.main.temp - 273.15) * 10) / 10);
+ $('.dayWeatherIcon').attr('src', 'http://openweathermap.org/img/w/' + data.weather[0].icon + '.png ');
}
+ });
+ }, (error) => {
+ $('.location').text('東京');
- //各データの表示
- $('.nowTemp').text(Math.floor((data.main.temp - 273.15) * 10) / 10);
- $('.dayWeatherIcon').attr('src', 'http://openweathermap.org/img/w/' + data.weather[0].icon + '.png ');
- }
+ TokyoWeather();
});
- }, (error) => {
+ } else {
$('.location').text('東京');
TokyoWeather();
- });
+ }
} else {
$('.location').text('東京');
<p class="text-muted">
<script>document.write(getAppDescription());</script><br>
バージョン:
- <script>document.write(getAppVersion());</script> (Stable)
+ <script>document.write(getAppVersion());</script> (<script>document.write(getAppChannel());</script>)
(73.0.36831.121)
</p>
</p>
location.href = '#';
});
- $('#startPageUrl').on('input', function () {
+ $('#startPageUrl').on('keydown', function (e) {
+ if (e.key != 'Enter') return;
+
const text = $(this).val();
if (isURL(text) && !text.includes('://')) {
setStartPage(text);
+ $(this).val(text);
$('#startPageIcon').prop('src', `http://www.google.com/s2/favicons?domain=${new URL(getSearchEngine().url).origin}`);
- } else if (!this.state.barText.includes('://')) {
+ } else if (!text.includes('://')) {
setStartPage('flast://home');
- $('#startPageIcon').prop('src', `http://www.google.com/s2/favicons?domain=${new URL(getSearchEngine().url).origin}`);
+ $(this).val('flast://home');
+ $('#startPageIcon').prop('src', `http://www.google.com/s2/favicons?domain=${new URL('flast://home').origin}`);
} else {
setStartPage(text);
+ $(this).val(text);
$('#startPageIcon').prop('src', `http://www.google.com/s2/favicons?domain=${new URL(getSearchEngine().url).origin}`);
}
});
$('#adBlock').on('change', function () {
setAdBlocker($(this).prop('checked'));
+ location.href = '#';
});
$('#customTitlebar').on('change', function () {
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
-import Tooltip from 'react-tooltip';
import Tippy from '@tippy.js/react';
import Window from './Components/Window';
import TabButton from './Components/TabButton';
import TabContent from './Components/TabContent';
import Toolbar from './Components/Toolbar';
-import ToolbarButton from './Components/ToolbarButton';
+import { ToolbarButton, ToolbarButtonBadge } from './Components/ToolbarButton';
import ToolbarDivider from './Components/ToolbarDivider';
import { ToolbarTextBoxWrapper, ToolbarTextBox } from './Components/ToolbarTextBox';
import BookmarkBar from './Components/BookmarkBar';
import LightBackIcon from './Resources/light/arrow_back.svg';
import LightForwardIcon from './Resources/light/arrow_forward.svg';
-import BackInActiveIcon from './Resources/arrow_back_inactive.svg';
-import ForwardInActiveIcon from './Resources/arrow_forward_inactive.svg';
+import BackInActiveIcon from './Resources/inactive/arrow_back.svg';
+import ForwardInActiveIcon from './Resources/inactive/arrow_forward.svg';
import DarkReloadIcon from './Resources/dark/reload.svg';
import DarkHomeIcon from './Resources/dark/home.svg';
import LightAddIcon from './Resources/light/add.svg';
import LightCloseIcon from './Resources/light/close.svg';
+import 'tippy.js/themes/light.css';
+import 'tippy.js/themes/light-border.css';
+import 'tippy.js/themes/google.css';
+import 'tippy.js/themes/translucent.css';
+
import isURL from './Utils/isURL';
const protocolStr = 'flast';
const { app, systemPreferences, Menu, MenuItem, dialog } = remote;
const platform = window.require('electron-platform');
+const { parse, format } = window.require('url');
const path = window.require('path');
const process = window.require('process');
class BrowserView extends Component {
constructor(props) {
super(props);
+
+ this.blockCount = 0;
+
this.state = {
barText: '',
findText: '',
ipcRenderer.on(`browserview-start-loading-${this.props.windowId}`, (e, args) => {
if (args.id == this.props.index) {
this.setState({ isLoading: true });
+ this.blockCount = 0;
}
});
}
});
+ ipcRenderer.on(`blocked-ad-${this.props.windowId}`, (e, args) => {
+ if (args.id == this.props.index) {
+ this.blockCount++
+ }
+ });
+
ipcRenderer.on(`update-navigation-state-${this.props.windowId}`, (e, args) => {
if (args.id == this.props.index) {
this.setState({ canGoBack: args.canGoBack, canGoForward: args.canGoForward });
<ToolbarButton isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} src={this.isDarkModeOrPrivateMode.bind(this, LightHomeIcon, DarkHomeIcon)} size={24}
isShowing={config.get('design.homeButton')} isRight={false} isMarginLeft={false} isEnabled={true} title="ホームページに移動" onClick={() => { this.goHome(); }} />
<ToolbarTextBoxWrapper isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} buttonCount={config.get('design.homeButton') ? 6 : 5}>
- <Tippy content={`このページは${this.state.isTrusted ? '安全です。' : '安全ではありません。'}`} placement="right" arrow={true}>
+ <Tippy content={this.state.isTrusted ? (parse(this.state.barText).protocol == 'flast:' ? `保護された ${process.env.npm_package_name} ページを表示しています` : 'このサイトへの接続は保護されています') : 'このサイトへの接続は保護されていません'} theme={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private') ? 'dark' : 'light'} placement="right" arrow={true}>
<ToolbarButton src={this.state.isTrusted ? this.isDarkModeOrPrivateMode.bind(this, LightSecureIcon, DarkSecureIcon) : this.isDarkModeOrPrivateMode.bind(this, LightInSecureIcon, DarkInSecureIcon)} size={12}
isShowing={true} isRight={false} isMarginLeft={true} isEnabled={true} title="このページの情報" />
</Tippy>
<ToolbarTextBox value={this.state.barText} onChange={(e) => { this.setState({ barText: e.target.value }); }} onKeyDown={this.handleKeyDown} onClick={(e) => { e.target.select(); }} onContextMenu={this.handleContextMenu} />
- <Tippy ref="markTooltip" content={!this.state.isBookmarked ? `${this.props.windowId.startsWith('private') ? 'プライベート ' : ''}ブックマークから削除しました。` : `${this.props.windowId.startsWith('private') ? 'プライベート ' : ''}ブックマークに追加しました。`} placement="left" arrow={true} trigger="manual">
+ <Tippy ref="markTooltip" content={!this.state.isBookmarked ? `${this.props.windowId.startsWith('private') ? 'プライベート ' : ''}ブックマークから削除しました` : `${this.props.windowId.startsWith('private') ? 'プライベート ' : ''}ブックマークに追加しました`} theme={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private') ? 'dark' : 'light'} placement="left" arrow={true} trigger="manual">
<ToolbarButton isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} src={this.state.isBookmarked ? this.isDarkModeOrPrivateMode.bind(this, LightStarFilledIcon, DarkStarFilledIcon) : this.isDarkModeOrPrivateMode.bind(this, LightStarIcon, DarkStarIcon)} size={12}
isShowing={true} isRight={true} isMarginLeft={true} isEnabled={true} title={this.state.isBookmarked ? 'ブックマークから削除' : 'ブックマークに追加'} onClick={() => { this.bookMark(); }} />
</Tippy>
</ToolbarTextBoxWrapper>
+ {config.get('adBlocker') &&
+ <ToolbarButton isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} src={this.isDarkModeOrPrivateMode.bind(this, LightShieldIcon, DarkShieldIcon)} size={24}
+ isShowing={true} isRight={true} isMarginLeft={true} isEnabled={true} title={`${this.blockCount}個の広告をブロックしました`}>
+ {this.blockCount > 0 && <ToolbarButtonBadge>{this.blockCount}</ToolbarButtonBadge>}
+ </ToolbarButton>
+ }
<ToolbarButton isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} src={this.isDarkModeOrPrivateMode.bind(this, LightFeedbackIcon, DarkFeedbackIcon)} size={24}
- isShowing={true} isRight={true} isMarginLeft={true} isEnabled={true} title="フィードバックの送信" />
+ isShowing={true} isRight={true} isMarginLeft={true} isEnabled={true} title="フィードバックの送信" onClick={() => { this.props.addTab('https://join.slack.com/t/serene-develop/shared_invite/enQtNjQwOTQwOTExMTM5LTg1OTNiNGFkNzU5NDEwYTJmNTY5MDk2MzI2YTU4NmYxZWRlMjMwMGY3MmUzMDM5N2QwZjUyZjNiNTczMjkyNWE'); }} />
<ToolbarDivider isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} />
<ToolbarButton isDarkModeOrPrivateMode={config.get('design.darkTheme') || String(this.props.windowId).startsWith('private')} src={this.props.windowId.startsWith('private') ? this.isDarkModeOrPrivateMode.bind(this, LightShieldIcon, DarkShieldIcon) : this.isDarkModeOrPrivateMode.bind(this, LightAccountIcon, DarkAccountIcon)} size={24}
isShowing={true} isRight={true} isMarginLeft={true} isEnabled={true} title={this.props.windowId.startsWith('private') ? 'プライベートモード' : process.env.USERNAME} onClick={() => { this.userMenu(); }} />
width: 100%;
height: 40px;
display: flex;
+ align-content: space-around;
background: ${props => !props.isDarkModeOrPrivateMode ? '#f9f9fa' : '#353535'};
border-bottom: ${props => !props.isBookmarkBar ? `solid 1px ${!props.isDarkModeOrPrivateMode ? '#e1e1e1' : '#8b8b8b'}` : 'none'};
box-sizing: border-box;
import styled from 'styled-components';
-const ToolbarButton = styled.div`
+export const ToolbarButton = styled.div`
background-color: initial;
border: none;
border-radius: 2px;
margin: 5px;
margin-left: ${props => props.isMarginLeft ? '5px' : '0px'};
padding: 3px;
+ position: relative;
+ flex-grow: 0;
display: ${props => props.isShowing ? 'display' : 'none'};
float: ${props => props.isRight ? 'right' : 'left'};
background-image: url(${props => props.src});
background-repeat: no-repeat;
transition: 0.2s background-color;
outline: none;
- box-sizing: border-box;
&:hover {
${props => props.isEnabled && `background-color: ${!props.isDarkModeOrPrivateMode ? 'rgba(0, 0, 0, 0.06)' : 'rgba(130, 130, 130, 0.3)'};`}
}
`;
-export default ToolbarButton;
\ No newline at end of file
+export const ToolbarButtonBadge = styled.span`
+ width: 18px;
+ height: 18px;
+ padding: 5px;
+ position: absolute;
+ bottom: 6px;
+ right: 6px;
+ border-radius: 50%;
+ display: inline-block;
+ background-color: #999999;
+ font-size: 0.7em;
+ line-height: 1;
+ box-shadow: 0px 0px 3px #999;
+ text-align: center;
+ transform: translate(50%, 50%);color: white;
+ box-sizing: border-box;
+`;
\ No newline at end of file
outline: none;
color: ${props => !props.isDarkModeOrPrivateMode ? 'black' : 'white'};
font-size: 14.5px;
- width: calc((100% - 40px * ${props => props.buttonCount}) - 25px);
+ /* width: calc((100% - 40px * ${props => props.buttonCount}) - 25px); */
+ flex-grow: 4;
height: auto;
margin: 5px;
padding: 0px;
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#ffffff"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#ffffff"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#999999">
+ <path d="M0 0h24v24H0z" fill="none" />
+ <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
+</svg>
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#999999"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#999999">
+ <path d="M0 0h24v24H0z" fill="none" />
+ <path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" />
+</svg>
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#999999"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>
\ No newline at end of file
const builder = require('electron-builder');
const fs = require('fs');
-const packageJson = JSON.parse(fs.readFileSync('./app/package.json', 'utf8'));
+const pkg = JSON.parse(fs.readFileSync('./app/package.json', 'utf8'));
builder.build({
platform: 'linux',
config: {
- 'appId': `org.aoichaan0513.${packageJson.name}`,
- 'productName': packageJson.name,
- 'copyright': `Copyright 2019 ${packageJson.author.name}. All rights reserved.`,
+ 'appId': `org.aoichaan0513.${pkg.name}`,
+ 'productName': pkg.name,
+ 'copyright': `Copyright 2019 ${pkg.author.name}. All rights reserved.`,
'asar': true,
+ 'publish': {
+ 'provider': 'generic',
+ 'url': `http://aoichaan0513.xyz/flast/${process.platform}/${process.arch}/${pkg.flast_channel}`,
+ 'channel': pkg.flast_channel
+ },
'fileAssociations': [
{
'name': 'Document',
- 'description': packageJson.name,
+ 'description': pkg.name,
'role': 'Viewer',
'ext': 'html'
}
const builder = require('electron-builder');
const fs = require('fs');
-const packageJson = JSON.parse(fs.readFileSync('./app/package.json', 'utf8'));
+const pkg = JSON.parse(fs.readFileSync('./app/package.json', 'utf8'));
builder.build({
platform: 'mac',
config: {
- 'appId': `org.aoichaan0513.${packageJson.name}`,
- 'productName': packageJson.name,
- 'copyright': `Copyright 2019 ${packageJson.author.name}. All rights reserved.`,
+ 'appId': `org.aoichaan0513.${pkg.name}`,
+ 'productName': pkg.name,
+ 'copyright': `Copyright 2019 ${pkg.author.name}. All rights reserved.`,
'asar': true,
+ 'publish': {
+ 'provider': 'generic',
+ 'url': `http://aoichaan0513.xyz/flast/${process.platform}/${process.arch}/${pkg.flast_channel}`,
+ 'channel': pkg.flast_channel
+ },
'fileAssociations': [
{
'name': 'Document',
- 'description': packageJson.name,
+ 'description': pkg.name,
'role': 'Viewer',
'ext': 'html'
}
const builder = require('electron-builder');
const fs = require('fs');
-const packageJson = JSON.parse(fs.readFileSync('./app/package.json', 'utf8'));
+const pkg = JSON.parse(fs.readFileSync('./app/package.json', 'utf8'));
builder.build({
platform: 'win',
config: {
- 'appId': `org.aoichaan0513.${packageJson.name}`,
- 'productName': packageJson.name,
- 'copyright': `Copyright 2019 ${packageJson.author.name}. All rights reserved.`,
+ 'appId': `org.aoichaan0513.${pkg.name}`,
+ 'productName': pkg.name,
+ 'copyright': `Copyright 2019 ${pkg.author.name}. All rights reserved.`,
'asar': true,
'directories': {
'output': 'dist',
'buildResources': 'static'
},
+ 'publish': {
+ 'provider': 'generic',
+ 'url': `http://aoichaan0513.xyz/flast/${process.platform}/${process.arch}/${pkg.flast_channel}`,
+ 'channel': pkg.flast_channel
+ },
'fileAssociations': [
{
'name': 'Document',
- 'description': packageJson.name,
+ 'description': pkg.name,
'role': 'Viewer',
'ext': 'html'
}
{
"name": "Flast",
"description": "Cross-platform browser based on Chromium.",
- "version": "1.6.9",
+ "version": "1.7.6",
+ "flast_channel": "Stable",
"private": true,
"author": {
"name": "Aoichaan0513",
"email": "aoichaan0513@gmail.com",
"url": "http://aoichaan0513.xyz"
},
- "dependencies": {},
+ "dependencies": {
+ "electron-updater": "^4.0.6"
+ },
"scripts": {
"package:win": "node build-win",
"package:mac": "node build-mac",