From bf7c125fb214b52d67527bdd292a4b5bb81b1d32 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Fri, 20 Jul 2018 23:36:03 +0900 Subject: My First Clean Architecture --- .../infrastructures/content-message-listener.js | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/background/infrastructures/content-message-listener.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js new file mode 100644 index 0000000..a0ed66c --- /dev/null +++ b/src/background/infrastructures/content-message-listener.js @@ -0,0 +1,34 @@ +import messages from '../../shared/messages'; +import CompletionsController from '../controllers/completions'; + +export default class ContentMessageListener { + constructor() { + this.completionsController = new CompletionsController(); + } + + run() { + browser.runtime.onMessage.addListener((message, sender) => { + try { + return this.onMessage(message, sender); + } catch (e) { + return browser.tabs.sendMessage(sender.tab.id, { + type: messages.CONSOLE_SHOW_ERROR, + text: e.message, + }); + } + }); + } + + onMessage(message) { + switch (message.type) { + case messages.CONSOLE_QUERY_COMPLETIONS: + return this.onConsoleQueryCompletions(message); + } + } + + async onConsoleQueryCompletions(message) { + let completions = + await this.completionsController.getCompletions(message.text); + return Promise.resolve(completions.serialize()); + } +} -- cgit v1.2.3 From 89c28d67fd7b961833b557da904bd17aa28660c5 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 22 Jul 2018 00:01:24 +0900 Subject: Settings as clean architecture --- src/background/actions/setting.js | 20 --------- src/background/components/background.js | 6 --- src/background/controllers/setting.js | 18 ++++++++ src/background/domains/setting.js | 51 ++++++++++++++++++++++ src/background/index.js | 6 +-- .../infrastructures/content-message-client.js | 12 +++++ .../infrastructures/content-message-listener.js | 21 +++++++-- src/background/infrastructures/memory-storage.js | 11 +++++ src/background/repositories/persistent-setting.js | 16 +++++++ src/background/repositories/setting.js | 17 ++++++++ src/background/usecases/setting.js | 31 +++++++++++++ 11 files changed, 176 insertions(+), 33 deletions(-) delete mode 100644 src/background/actions/setting.js create mode 100644 src/background/controllers/setting.js create mode 100644 src/background/domains/setting.js create mode 100644 src/background/infrastructures/content-message-client.js create mode 100644 src/background/infrastructures/memory-storage.js create mode 100644 src/background/repositories/persistent-setting.js create mode 100644 src/background/repositories/setting.js create mode 100644 src/background/usecases/setting.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/actions/setting.js b/src/background/actions/setting.js deleted file mode 100644 index 7eeb5de..0000000 --- a/src/background/actions/setting.js +++ /dev/null @@ -1,20 +0,0 @@ -import actions from '../actions'; -import * as settingsStorage from 'shared/settings/storage'; - -const load = async() => { - let value = await settingsStorage.loadValue(); - return { - type: actions.SETTING_SET_SETTINGS, - value, - }; -}; - -const setProperty = (name, value) => { - return { - type: actions.SETTING_SET_PROPERTY, - name, - value, - }; -}; - -export { load, setProperty }; diff --git a/src/background/components/background.js b/src/background/components/background.js index d933d7b..9ba733d 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -1,6 +1,5 @@ import messages from 'shared/messages'; import * as commandActions from 'background/actions/command'; -import * as settingActions from 'background/actions/setting'; import * as findActions from 'background/actions/find'; import * as tabActions from 'background/actions/tab'; @@ -38,11 +37,6 @@ export default class BackgroundComponent { commandActions.exec(sender.tab, message.text, settings.value), ); return this.broadcastSettingsChanged(); - case messages.SETTINGS_QUERY: - return Promise.resolve(this.store.getState().setting.value); - case messages.SETTINGS_RELOAD: - this.store.dispatch(settingActions.load()); - return this.broadcastSettingsChanged(); case messages.FIND_GET_KEYWORD: return Promise.resolve(find.keyword); case messages.FIND_SET_KEYWORD: diff --git a/src/background/controllers/setting.js b/src/background/controllers/setting.js new file mode 100644 index 0000000..9e6019e --- /dev/null +++ b/src/background/controllers/setting.js @@ -0,0 +1,18 @@ +import SettingInteractor from '../usecases/setting'; +import ContentMessageClient from '../infrastructures/content-message-client'; + +export default class SettingController { + constructor() { + this.settingInteractor = new SettingInteractor(); + this.contentMessageClient = new ContentMessageClient(); + } + + getSetting() { + return this.settingInteractor.get(); + } + + async reload() { + await this.settingInteractor.reload(); + this.contentMessageClient.broadcastSettingsChanged(); + } +} diff --git a/src/background/domains/setting.js b/src/background/domains/setting.js new file mode 100644 index 0000000..106ec0f --- /dev/null +++ b/src/background/domains/setting.js @@ -0,0 +1,51 @@ +import DefaultSettings from '../../shared/settings/default'; +import * as settingsValues from '../../shared/settings/values'; + +export default class Setting { + constructor({ source, json, form }) { + this.obj = { + source, json, form + }; + } + + get source() { + return this.obj.source; + } + + get json() { + return this.obj.json; + } + + get form() { + return this.obj.form; + } + + value() { + let value = JSON.parse(DefaultSettings.json); + if (this.obj.source === 'json') { + value = settingsValues.valueFromJson(this.obj.json); + } else if (this.obj.source === 'form') { + value = settingsValues.valueFromForm(this.obj.form); + } + if (!value.properties) { + value.properties = {}; + } + return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value }; + } + + serialize() { + return this.obj; + } + + static deserialize(obj) { + return new Setting({ source: obj.source, json: obj.json, form: obj.form }); + } + + static defaultSettings() { + return new Setting({ + source: DefaultSettings.source, + json: DefaultSettings.json, + form: {}, + }); + } +} diff --git a/src/background/index.js b/src/background/index.js index 619b076..69dbe06 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,4 +1,3 @@ -import * as settingActions from 'background/actions/setting'; import BackgroundComponent from 'background/components/background'; import OperationComponent from 'background/components/operation'; import TabComponent from 'background/components/tab'; @@ -9,6 +8,7 @@ import promise from 'redux-promise'; import * as versions from './shared/versions'; import ContentMessageListener from './infrastructures/content-message-listener'; +import SettingController from './controllers/setting'; const store = createStore( reducers, @@ -31,8 +31,8 @@ const tabComponent = new TabComponent(store); const indicatorComponent = new IndicatorComponent(store); /* eslint-enable no-unused-vars */ -store.dispatch(settingActions.load()); - checkAndNotifyUpdated(); +new SettingController().reload(); + new ContentMessageListener().run(); diff --git a/src/background/infrastructures/content-message-client.js b/src/background/infrastructures/content-message-client.js new file mode 100644 index 0000000..b3b37b4 --- /dev/null +++ b/src/background/infrastructures/content-message-client.js @@ -0,0 +1,12 @@ +import messages from '../../shared/messages'; + +export default class ContentMessageClient { + async broadcastSettingsChanged() { + let tabs = await browser.tabs.query({}); + for (let tab of tabs) { + browser.tabs.sendMessage(tab.id, { + type: messages.SETTINGS_CHANGED, + }); + } + } +} diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index a0ed66c..6236f1c 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -1,8 +1,10 @@ import messages from '../../shared/messages'; import CompletionsController from '../controllers/completions'; +import SettingController from '../controllers/setting'; export default class ContentMessageListener { constructor() { + this.settingController = new SettingController(); this.completionsController = new CompletionsController(); } @@ -22,13 +24,24 @@ export default class ContentMessageListener { onMessage(message) { switch (message.type) { case messages.CONSOLE_QUERY_COMPLETIONS: - return this.onConsoleQueryCompletions(message); + return this.onConsoleQueryCompletions(message.text); + case messages.SETTINGS_QUERY: + return this.onSettingsQuery(); + case messages.SETTINGS_RELOAD: + return this.onSettingsReload(); } } - async onConsoleQueryCompletions(message) { - let completions = - await this.completionsController.getCompletions(message.text); + async onConsoleQueryCompletions(line) { + let completions = await this.completionsController.getCompletions(line); return Promise.resolve(completions.serialize()); } + + onSettingsQuery() { + return this.settingController.getSetting(); + } + + onSettingsReload() { + return this.settingController.reload(); + } } diff --git a/src/background/infrastructures/memory-storage.js b/src/background/infrastructures/memory-storage.js new file mode 100644 index 0000000..0a05928 --- /dev/null +++ b/src/background/infrastructures/memory-storage.js @@ -0,0 +1,11 @@ +const db = {}; + +export default class MemoryStorage { + set(name, value) { + db[name] = value; + } + + get(name) { + return db[name]; + } +} diff --git a/src/background/repositories/persistent-setting.js b/src/background/repositories/persistent-setting.js new file mode 100644 index 0000000..247ea6f --- /dev/null +++ b/src/background/repositories/persistent-setting.js @@ -0,0 +1,16 @@ +import Setting from '../domains/setting'; + +export default class SettingRepository { + save(settings) { + return browser.storage.local.set({ settings: settings.serialize() }); + } + + async load() { + let { settings } = await browser.storage.local.get('settings'); + if (!settings) { + return null; + } + return Setting.deserialize(settings); + } +} + diff --git a/src/background/repositories/setting.js b/src/background/repositories/setting.js new file mode 100644 index 0000000..d9c481d --- /dev/null +++ b/src/background/repositories/setting.js @@ -0,0 +1,17 @@ +import MemoryStorage from '../infrastructures/memory-storage'; + +const CACHED_SETTING_KEY = 'setting'; + +export default class SettingRepository { + constructor() { + this.cache = new MemoryStorage(); + } + + get() { + return Promise.resolve(this.cache.get(CACHED_SETTING_KEY)); + } + + update(value) { + return this.cache.set(CACHED_SETTING_KEY, value); + } +} diff --git a/src/background/usecases/setting.js b/src/background/usecases/setting.js new file mode 100644 index 0000000..656fc3f --- /dev/null +++ b/src/background/usecases/setting.js @@ -0,0 +1,31 @@ +import Setting from '../domains/setting'; +import PersistentSettingRepository from '../repositories/persistent-setting'; +import SettingRepository from '../repositories/setting'; + +export default class SettingInteractor { + constructor() { + this.persistentSettingRepository = new PersistentSettingRepository(); + this.settingRepository = new SettingRepository(); + } + + save(settings) { + this.persistentSettingRepository.save(settings); + } + + get() { + return this.settingRepository.get(); + } + + async reload() { + let settings = await this.persistentSettingRepository.load(); + if (!settings) { + settings = Setting.defaultSettings(); + } + + let value = settings.value(); + + this.settingRepository.update(value); + + return value; + } +} -- cgit v1.2.3 From a1e5e97200bd96ba918744dfa2758f977ca823c6 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 22 Jul 2018 00:01:24 +0900 Subject: Find as clean architecture --- src/background/components/background.js | 7 ------- src/background/controllers/find.js | 15 +++++++++++++++ .../infrastructures/content-message-listener.js | 14 ++++++++++++++ src/background/repositories/find.js | 18 ++++++++++++++++++ src/background/usecases/find.js | 15 +++++++++++++++ 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 src/background/controllers/find.js create mode 100644 src/background/repositories/find.js create mode 100644 src/background/usecases/find.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/components/background.js b/src/background/components/background.js index 9ba733d..3667f14 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -1,6 +1,5 @@ import messages from 'shared/messages'; import * as commandActions from 'background/actions/command'; -import * as findActions from 'background/actions/find'; import * as tabActions from 'background/actions/tab'; export default class BackgroundComponent { @@ -21,7 +20,6 @@ export default class BackgroundComponent { onMessage(message, sender) { let settings = this.store.getState().setting; - let find = this.store.getState().find; switch (message.type) { case messages.OPEN_URL: @@ -37,11 +35,6 @@ export default class BackgroundComponent { commandActions.exec(sender.tab, message.text, settings.value), ); return this.broadcastSettingsChanged(); - case messages.FIND_GET_KEYWORD: - return Promise.resolve(find.keyword); - case messages.FIND_SET_KEYWORD: - this.store.dispatch(findActions.setKeyword(message.keyword)); - return Promise.resolve({}); } } diff --git a/src/background/controllers/find.js b/src/background/controllers/find.js new file mode 100644 index 0000000..7096014 --- /dev/null +++ b/src/background/controllers/find.js @@ -0,0 +1,15 @@ +import FindInteractor from '../usecases/find'; + +export default class FindController { + constructor() { + this.findInteractor = new FindInteractor(); + } + + getKeyword() { + return this.findInteractor.getKeyword(); + } + + setKeyword(keyword) { + return this.findInteractor.setKeyword(keyword); + } +} diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index 6236f1c..00dba51 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -1,11 +1,13 @@ import messages from '../../shared/messages'; import CompletionsController from '../controllers/completions'; import SettingController from '../controllers/setting'; +import FindController from '../controllers/find'; export default class ContentMessageListener { constructor() { this.settingController = new SettingController(); this.completionsController = new CompletionsController(); + this.findController = new FindController(); } run() { @@ -29,6 +31,10 @@ export default class ContentMessageListener { return this.onSettingsQuery(); case messages.SETTINGS_RELOAD: return this.onSettingsReload(); + case messages.FIND_GET_KEYWORD: + return this.onFindGetKeyword(); + case messages.FIND_SET_KEYWORD: + return this.onFindSetKeyword(message.keyword); } } @@ -44,4 +50,12 @@ export default class ContentMessageListener { onSettingsReload() { return this.settingController.reload(); } + + onFindGetKeyword() { + return this.findController.getKeyword(); + } + + onFindSetKeyword(keyword) { + return this.findController.setKeyword(keyword); + } } diff --git a/src/background/repositories/find.js b/src/background/repositories/find.js new file mode 100644 index 0000000..9482e78 --- /dev/null +++ b/src/background/repositories/find.js @@ -0,0 +1,18 @@ +import MemoryStorage from '../infrastructures/memory-storage'; + +const FIND_KEYWORD_KEY = 'find-keyword'; + +export default class FindRepository { + constructor() { + this.cache = new MemoryStorage(); + } + + getKeyword() { + return Promise.resolve(this.cache.get(FIND_KEYWORD_KEY)); + } + + setKeyword(keyword) { + return this.cache.set(FIND_KEYWORD_KEY, keyword); + } +} + diff --git a/src/background/usecases/find.js b/src/background/usecases/find.js new file mode 100644 index 0000000..eae480d --- /dev/null +++ b/src/background/usecases/find.js @@ -0,0 +1,15 @@ +import FindRepository from '../repositories/find'; + +export default class FindInteractor { + constructor() { + this.findRepository = new FindRepository(); + } + + getKeyword() { + return this.findRepository.getKeyword(); + } + + setKeyword(keyword) { + return this.findRepository.setKeyword(keyword); + } +} -- cgit v1.2.3 From 42d902982a1d2edbca8ca2edb5fb25e642794e2a Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 22 Jul 2018 15:39:23 +0900 Subject: Indicator as Clean Architecture --- src/background/controllers/addon-enabled.js | 11 ++++++++ src/background/index.js | 2 -- .../infrastructures/content-message-client.js | 13 ++++++++++ .../infrastructures/content-message-listener.js | 8 ++++++ src/background/presenters/indicator.js | 12 +++++++++ src/background/presenters/tab.js | 4 +++ src/background/usecases/addon-enabled.js | 29 ++++++++++++++++++++++ 7 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/background/controllers/addon-enabled.js create mode 100644 src/background/presenters/indicator.js create mode 100644 src/background/usecases/addon-enabled.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/controllers/addon-enabled.js b/src/background/controllers/addon-enabled.js new file mode 100644 index 0000000..0f5d801 --- /dev/null +++ b/src/background/controllers/addon-enabled.js @@ -0,0 +1,11 @@ +import AddonEnabledInteractor from '../usecases/addon-enabled'; + +export default class AddonEnabledController { + constructor() { + this.addonEnabledInteractor = new AddonEnabledInteractor(); + } + + indicate(enabled) { + this.addonEnabledInteractor.indicate(enabled); + } +} diff --git a/src/background/index.js b/src/background/index.js index e753c48..70d514f 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,7 +1,6 @@ import BackgroundComponent from 'background/components/background'; import OperationComponent from 'background/components/operation'; import TabComponent from 'background/components/tab'; -import IndicatorComponent from 'background/components/indicator'; import reducers from 'background/reducers'; import { createStore, applyMiddleware } from 'redux'; import promise from 'redux-promise'; @@ -19,7 +18,6 @@ const store = createStore( const backgroundComponent = new BackgroundComponent(store); const operationComponent = new OperationComponent(store); const tabComponent = new TabComponent(store); -const indicatorComponent = new IndicatorComponent(store); /* eslint-enable no-unused-vars */ new SettingController().reload(); diff --git a/src/background/infrastructures/content-message-client.js b/src/background/infrastructures/content-message-client.js index b3b37b4..d659560 100644 --- a/src/background/infrastructures/content-message-client.js +++ b/src/background/infrastructures/content-message-client.js @@ -9,4 +9,17 @@ export default class ContentMessageClient { }); } } + + async getAddonEnabled(tabId) { + let { enabled } = await browser.tabs.sendMessage(tabId, { + type: messages.ADDON_ENABLED_QUERY, + }); + return enabled; + } + + toggleAddonEnabled(tabId) { + return browser.tabs.sendMessage(tabId, { + type: messages.ADDON_TOGGLE_ENABLED, + }); + } } diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index 00dba51..8a361c1 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -2,12 +2,14 @@ import messages from '../../shared/messages'; import CompletionsController from '../controllers/completions'; import SettingController from '../controllers/setting'; import FindController from '../controllers/find'; +import AddonEnabledController from '../controllers/addon-enabled'; export default class ContentMessageListener { constructor() { this.settingController = new SettingController(); this.completionsController = new CompletionsController(); this.findController = new FindController(); + this.addonEnabledController = new AddonEnabledController(); } run() { @@ -35,6 +37,8 @@ export default class ContentMessageListener { return this.onFindGetKeyword(); case messages.FIND_SET_KEYWORD: return this.onFindSetKeyword(message.keyword); + case messages.ADDON_ENABLED_RESPONSE: + return this.onAddonEnabledResponse(message.enabled); } } @@ -58,4 +62,8 @@ export default class ContentMessageListener { onFindSetKeyword(keyword) { return this.findController.setKeyword(keyword); } + + onAddonEnabledResponse(enabled) { + return this.addonEnabledController.indicate(enabled); + } } diff --git a/src/background/presenters/indicator.js b/src/background/presenters/indicator.js new file mode 100644 index 0000000..5737519 --- /dev/null +++ b/src/background/presenters/indicator.js @@ -0,0 +1,12 @@ +export default class IndicatorPresenter { + indicate(enabled) { + let path = enabled + ? 'resources/enabled_32x32.png' + : 'resources/disabled_32x32.png'; + return browser.browserAction.setIcon({ path }); + } + + onClick(listener) { + browser.browserAction.onClicked.addListener(listener); + } +} diff --git a/src/background/presenters/tab.js b/src/background/presenters/tab.js index 8f60535..6120f6e 100644 --- a/src/background/presenters/tab.js +++ b/src/background/presenters/tab.js @@ -2,4 +2,8 @@ export default class TabPresenter { create(url) { browser.tabs.create({ url, }); } + + onSelected(listener) { + browser.tabs.onActivated.addListener(listener); + } } diff --git a/src/background/usecases/addon-enabled.js b/src/background/usecases/addon-enabled.js new file mode 100644 index 0000000..37eb1c2 --- /dev/null +++ b/src/background/usecases/addon-enabled.js @@ -0,0 +1,29 @@ +import IndicatorPresenter from '../presenters/indicator'; +import TabPresenter from '../presenters/tab'; +import ContentMessageClient from '../infrastructures/content-message-client'; + +export default class AddonEnabledInteractor { + constructor() { + this.indicatorPresentor = new IndicatorPresenter(); + + this.indicatorPresentor.onClick(tab => this.onIndicatorClick(tab.id)); + + this.tabPresenter = new TabPresenter(); + this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId)); + + this.contentMessageClient = new ContentMessageClient(); + } + + indicate(enabled) { + this.indicatorPresentor.indicate(enabled); + } + + onIndicatorClick(tabId) { + return this.contentMessageClient.toggleAddonEnabled(tabId); + } + + async onTabSelected(tabId) { + let enabled = await this.contentMessageClient.getAddonEnabled(tabId); + return this.indicatorPresentor.indicate(enabled); + } +} -- cgit v1.2.3 From 0846587baf8ff04d2183985a61f14ccdea7263d3 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 22 Jul 2018 17:09:14 +0900 Subject: Open link as Clean Architecture --- src/background/components/background.js | 9 -------- src/background/controllers/link.js | 15 ++++++++++++ .../infrastructures/content-message-listener.js | 14 ++++++++++- src/background/presenters/tab.js | 20 ++++++++++++++-- src/background/usecases/link.js | 27 ++++++++++++++++++++++ 5 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 src/background/controllers/link.js create mode 100644 src/background/usecases/link.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/components/background.js b/src/background/components/background.js index 3667f14..86e96fa 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -1,6 +1,5 @@ import messages from 'shared/messages'; import * as commandActions from 'background/actions/command'; -import * as tabActions from 'background/actions/tab'; export default class BackgroundComponent { constructor(store) { @@ -22,14 +21,6 @@ export default class BackgroundComponent { let settings = this.store.getState().setting; switch (message.type) { - case messages.OPEN_URL: - if (message.newTab) { - let action = tabActions.openNewTab( - message.url, sender.tab.id, message.background, - settings.value.properties.adjacenttab); - return this.store.dispatch(action); - } - return this.store.dispatch(tabActions.openToTab(message.url, sender.tab)); case messages.CONSOLE_ENTER_COMMAND: this.store.dispatch( commandActions.exec(sender.tab, message.text, settings.value), diff --git a/src/background/controllers/link.js b/src/background/controllers/link.js new file mode 100644 index 0000000..7ebbb34 --- /dev/null +++ b/src/background/controllers/link.js @@ -0,0 +1,15 @@ +import LinkInteractor from '../usecases/link'; + +export default class LinkController { + constructor() { + this.linkInteractor = new LinkInteractor(); + } + + openToTab(url, tabId) { + this.linkInteractor.openToTab(url, tabId); + } + + openNewTab(url, openerId, background) { + this.linkInteractor.openNewTab(url, openerId, background); + } +} diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index 8a361c1..f16804f 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -3,6 +3,7 @@ import CompletionsController from '../controllers/completions'; import SettingController from '../controllers/setting'; import FindController from '../controllers/find'; import AddonEnabledController from '../controllers/addon-enabled'; +import LinkController from '../controllers/link'; export default class ContentMessageListener { constructor() { @@ -10,6 +11,7 @@ export default class ContentMessageListener { this.completionsController = new CompletionsController(); this.findController = new FindController(); this.addonEnabledController = new AddonEnabledController(); + this.linkController = new LinkController(); } run() { @@ -25,7 +27,7 @@ export default class ContentMessageListener { }); } - onMessage(message) { + onMessage(message, sender) { switch (message.type) { case messages.CONSOLE_QUERY_COMPLETIONS: return this.onConsoleQueryCompletions(message.text); @@ -39,6 +41,9 @@ export default class ContentMessageListener { return this.onFindSetKeyword(message.keyword); case messages.ADDON_ENABLED_RESPONSE: return this.onAddonEnabledResponse(message.enabled); + case messages.OPEN_URL: + return this.onOpenUrl( + message.newTab, message.url, sender.tab.id, message.background); } } @@ -66,4 +71,11 @@ export default class ContentMessageListener { onAddonEnabledResponse(enabled) { return this.addonEnabledController.indicate(enabled); } + + onOpenUrl(newTab, url, openerId, background) { + if (newTab) { + return this.linkController.openNewTab(url, openerId, background); + } + return this.linkController.openToTab(url, openerId); + } } diff --git a/src/background/presenters/tab.js b/src/background/presenters/tab.js index 6120f6e..66a207f 100644 --- a/src/background/presenters/tab.js +++ b/src/background/presenters/tab.js @@ -1,6 +1,22 @@ export default class TabPresenter { - create(url) { - browser.tabs.create({ url, }); + open(url, tabId) { + return browser.tabs.update(tabId, { url }); + } + + create(url, { openerTabId, active }) { + return browser.tabs.create({ url, openerTabId, active }); + } + + async createAdjacent(url, { openerTabId, active }) { + let tabs = await browser.tabs.query({ + active: true, currentWindow: true + }); + return browser.tabs.create({ + url, + openerTabId, + active, + index: tabs[0].index + 1 + }); } onSelected(listener) { diff --git a/src/background/usecases/link.js b/src/background/usecases/link.js new file mode 100644 index 0000000..f9e474a --- /dev/null +++ b/src/background/usecases/link.js @@ -0,0 +1,27 @@ +import SettingRepository from '../repositories/setting'; +import TabPresenter from '../presenters/tab'; + +export default class LinkInteractor { + constructor() { + this.settingRepository = new SettingRepository(); + this.tabPresenter = new TabPresenter(); + } + + openToTab(url, tabId) { + this.tabPresenter.open(url, tabId); + + } + + async openNewTab(url, openerId, background) { + let settings = await this.settingRepository.get(); + let { adjacenttab } = settings.properties; + if (adjacenttab) { + return this.tabPresenter.create(url, { + openerTabId: openerId, active: !background + }); + } + return this.tabPresenter.create(url, { + openerTabId: openerId, active: !background + }); + } +} -- cgit v1.2.3 From c4afd7237b7720acbf642fc4c6eb529420295dcd Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Mon, 23 Jul 2018 21:26:47 +0900 Subject: [wip] implement command usecases --- src/background/controllers/command.js | 89 ++++++++++++++++ src/background/controllers/completions.js | 43 -------- .../infrastructures/content-message-listener.js | 13 ++- src/background/presenters/bookmark.js | 0 src/background/presenters/console.js | 16 +++ src/background/presenters/tab.js | 45 +++++++- src/background/presenters/window.js | 5 + src/background/repositories/bookmark.js | 13 +++ src/background/usecases/command.js | 114 +++++++++++++++++++++ src/background/usecases/completions.js | 2 +- 10 files changed, 291 insertions(+), 49 deletions(-) create mode 100644 src/background/controllers/command.js delete mode 100644 src/background/controllers/completions.js create mode 100644 src/background/presenters/bookmark.js create mode 100644 src/background/presenters/console.js create mode 100644 src/background/presenters/window.js create mode 100644 src/background/repositories/bookmark.js create mode 100644 src/background/usecases/command.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/controllers/command.js b/src/background/controllers/command.js new file mode 100644 index 0000000..41057e0 --- /dev/null +++ b/src/background/controllers/command.js @@ -0,0 +1,89 @@ +import CompletionsInteractor from '../usecases/completions'; +import CommandInteractor from '../usecases/command'; +import Completions from '../domains/completions'; + +export default class CommandController { + constructor() { + this.completionsInteractor = new CompletionsInteractor(); + this.commandIndicator = new CommandInteractor(); + } + + getCompletions(line) { + let trimmed = line.trimStart(); + let words = trimmed.split(/ +/); + let name = words[0]; + if (words.length === 1) { + return this.completionsInteractor.queryConsoleCommand(name); + } + let keywords = trimmed.slice(name.length).trimStart(); + switch (words[0]) { + case 'o': + case 'open': + case 't': + case 'tabopen': + case 'w': + case 'winopen': + return this.completionsInteractor.queryOpen(name, keywords); + case 'b': + case 'buffer': + return this.completionsInteractor.queryBuffer(name, keywords); + case 'bd': + case 'bdel': + case 'bdelete': + case 'bdeletes': + return this.completionsInteractor.queryBdelete(name, keywords); + case 'bd!': + case 'bdel!': + case 'bdelete!': + case 'bdeletes!': + return this.completionsInteractor.queryBdeleteForce(name, keywords); + case 'set': + return this.completionsInteractor.querySet(name, keywords); + } + return Promise.resolve(Completions.empty()); + } + + // eslint-disable-next-line complexity + exec(line) { + let trimmed = line.trimStart(); + let words = trimmed.split(/ +/); + let name = words[0]; + let keywords = trimmed.slice(name.length).trimStart(); + switch (words[0]) { + case 'o': + case 'open': + return this.commandIndicator.open(keywords); + case 't': + case 'tabopen': + return this.commandIndicator.tabopen(keywords); + case 'w': + case 'winopen': + return this.commandIndicator.winopen(keywords); + case 'b': + case 'buffer': + return this.commandIndicator.buffer(keywords); + case 'bd': + case 'bdel': + case 'bdelete': + return this.commandIndicator.bdelete(false, keywords); + case 'bd!': + case 'bdel!': + case 'bdelete!': + return this.commandIndicator.bdelete(true, keywords); + case 'bdeletes': + return this.commandIndicator.bdeletes(false, keywords); + case 'bdeletes!': + return this.commandIndicator.bdeletes(true, keywords); + case 'addbookmark': + return this.commandIndicator.addbookmark(keywords); + case 'q': + case 'quit': + return this.commandIndicator.quit(); + case 'qa': + case 'quitall': + return this.commandIndicator.quitAll(); + case 'set': + return this.commandIndicator.set(keywords); + } + } +} diff --git a/src/background/controllers/completions.js b/src/background/controllers/completions.js deleted file mode 100644 index f8eade9..0000000 --- a/src/background/controllers/completions.js +++ /dev/null @@ -1,43 +0,0 @@ -import CompletionsInteractor from '../usecases/completions'; -import Completions from '../domains/completions'; - -export default class ContentMessageController { - constructor() { - this.completionsInteractor = new CompletionsInteractor(); - } - - getCompletions(line) { - let trimmed = line.trimStart(); - let words = trimmed.split(/ +/); - let name = words[0]; - if (words.length === 1) { - return this.completionsInteractor.queryConsoleCommand(name); - } - let keywords = trimmed.slice(name.length).trimStart(); - switch (words[0]) { - case 'o': - case 'open': - case 't': - case 'tabopen': - case 'w': - case 'winopen': - return this.completionsInteractor.queryOpen(name, keywords); - case 'b': - case 'buffer': - return this.completionsInteractor.queryBuffer(name, keywords); - case 'bd': - case 'bdel': - case 'bdelete': - case 'bdeletes': - return this.completionsInteractor.queryBdelete(name, keywords); - case 'bd!': - case 'bdel!': - case 'bdelete!': - case 'bdeletes!': - return this.completionsInteractor.queryBdeleteForce(name, keywords); - case 'set': - return this.completionsInteractor.querySet(name, keywords); - } - return Promise.resolve(Completions.empty()); - } -} diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index f16804f..2e84fcc 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -1,5 +1,5 @@ import messages from '../../shared/messages'; -import CompletionsController from '../controllers/completions'; +import CommandController from '../controllers/command'; import SettingController from '../controllers/setting'; import FindController from '../controllers/find'; import AddonEnabledController from '../controllers/addon-enabled'; @@ -8,7 +8,7 @@ import LinkController from '../controllers/link'; export default class ContentMessageListener { constructor() { this.settingController = new SettingController(); - this.completionsController = new CompletionsController(); + this.commandController = new CommandController(); this.findController = new FindController(); this.addonEnabledController = new AddonEnabledController(); this.linkController = new LinkController(); @@ -31,6 +31,8 @@ export default class ContentMessageListener { switch (message.type) { case messages.CONSOLE_QUERY_COMPLETIONS: return this.onConsoleQueryCompletions(message.text); + case messages.CONSOLE_ENTER_COMMAND: + return this.onConsoleEnterCommand(message.text); case messages.SETTINGS_QUERY: return this.onSettingsQuery(); case messages.SETTINGS_RELOAD: @@ -48,10 +50,15 @@ export default class ContentMessageListener { } async onConsoleQueryCompletions(line) { - let completions = await this.completionsController.getCompletions(line); + let completions = await this.commandController.getCompletions(line); return Promise.resolve(completions.serialize()); } + onConsoleEnterCommand(text) { + return this.commandController.exec(text); + } + + onSettingsQuery() { return this.settingController.getSetting(); } diff --git a/src/background/presenters/bookmark.js b/src/background/presenters/bookmark.js new file mode 100644 index 0000000..e69de29 diff --git a/src/background/presenters/console.js b/src/background/presenters/console.js new file mode 100644 index 0000000..f7d3777 --- /dev/null +++ b/src/background/presenters/console.js @@ -0,0 +1,16 @@ +import messages from '../../shared/messages'; + +export default class ConsolePresenter { + showInfo(tabId, message) { + return browser.tabs.sendMessage(tabId, { + type: messages.CONSOLE_SHOW_INFO, + text: message, + }); + } + showError(tabId, message) { + return browser.tabs.sendMessage(tabId, { + type: messages.CONSOLE_SHOW_ERROR, + text: message, + }); + } +} diff --git a/src/background/presenters/tab.js b/src/background/presenters/tab.js index 66a207f..be6955a 100644 --- a/src/background/presenters/tab.js +++ b/src/background/presenters/tab.js @@ -3,8 +3,49 @@ export default class TabPresenter { return browser.tabs.update(tabId, { url }); } - create(url, { openerTabId, active }) { - return browser.tabs.create({ url, openerTabId, active }); + create(url, opts) { + return browser.tabs.create({ url, ...opts }); + } + + async getCurrent() { + let tabs = await browser.tabs.query({ + active: true, currentWindow: true + }); + return tabs[0]; + } + + getAll() { + return browser.tabs.query({ currentWindow: true }); + } + + async getByKeyword(keyword, excludePinned = false) { + let tabs = await browser.tabs.query({ currentWindow: true }); + return tabs.filter((t) => { + return t.url.toLowerCase().includes(keyword.toLowerCase()) || + t.title && t.title.toLowerCase().includes(keyword.toLowerCase()); + }).filter((t) => { + return !(excludePinned && t.pinned); + }); + } + + select(tabId) { + return browser.tabs.update(tabId, { active: true }); + } + + async selectAt(index) { + let tabs = await browser.tabs.query({ currentWindow: true }); + if (tabs.length < 2) { + return; + } + if (index < 0 || tabs.length <= index) { + throw new RangeError(`tab ${index + 1} does not exist`); + } + let id = tabs[index].id; + return browser.tabs.update(id, { active: true }); + } + + remove(ids) { + return browser.tabs.remove(ids); } async createAdjacent(url, { openerTabId, active }) { diff --git a/src/background/presenters/window.js b/src/background/presenters/window.js new file mode 100644 index 0000000..a82c4a2 --- /dev/null +++ b/src/background/presenters/window.js @@ -0,0 +1,5 @@ +export default class WindowPresenter { + create(url) { + return browser.windows.create({ url }); + } +} diff --git a/src/background/repositories/bookmark.js b/src/background/repositories/bookmark.js new file mode 100644 index 0000000..99f7ec4 --- /dev/null +++ b/src/background/repositories/bookmark.js @@ -0,0 +1,13 @@ +export default class BookmarkRepository { + async create(title, url) { + let item = await browser.bookmarks.create({ + type: 'bookmark', + title, + url, + }); + if (!item) { + throw new Error('Could not create a bookmark'); + } + return item; + } +} diff --git a/src/background/usecases/command.js b/src/background/usecases/command.js new file mode 100644 index 0000000..1d4744e --- /dev/null +++ b/src/background/usecases/command.js @@ -0,0 +1,114 @@ +import TabPresenter from '../presenters/tab'; +import WindowPresenter from '../presenters/window'; +import SettingRepository from '../repositories/setting'; +import BookmarkRepository from '../repositories/bookmark'; +import ConsolePresenter from '../presenters/console'; + +export default class CommandIndicator { + constructor() { + this.tabPresenter = new TabPresenter(); + this.windowPresenter = new WindowPresenter(); + this.settingRepository = new SettingRepository(); + this.bookmarkRepository = new BookmarkRepository(); + this.consolePresenter = new ConsolePresenter(); + } + + async open(keywords) { + let url = await this.urlOrSearch(keywords); + return this.tabPresenter.open(url); + } + + async tabopen(keywords) { + let url = await this.urlOrSearch(keywords); + return this.tabPresenter.create(url); + } + + async winopen(keywords) { + let url = await this.urlOrSearch(keywords); + return this.windowPresenter.create(url); + } + + async buffer(keywords) { + if (keywords.length === 0) { + return; + } + if (!isNaN(keywords)) { + let index = parseInt(keywords, 10) - 1; + return tabs.selectAt(index); + } + + let current = await this.tabPresenter.getCurrent(); + let tabs = await this.tabPresenter.getByKeyword(keywords); + if (tabs.length === 0) { + throw new RangeError('No matching buffer for ' + keywords); + } + for (let tab of tabs) { + if (tab.index > current.index) { + return this.tabPresenter.select(tab.id); + } + } + return this.tabPresenter.select(tabs[0].id); + } + + async bdelete(force, keywords) { + let excludePinned = !force; + let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); + if (tabs.length === 0) { + throw new Error('No matching buffer for ' + keywords); + } else if (tabs.length > 1) { + throw new Error('More than one match for ' + keywords); + } + return this.tabPresenter.remove([tabs[0].id]); + } + + async bdeletes(force, keywords) { + let excludePinned = !force; + let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); + let ids = tabs.map(tab => tab.id); + return this.tabPresenter.remove(ids); + } + + async quit() { + let tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.remove([tab.id]); + } + + async quitall() { + let tabs = await this.tabPresenter.getAll(); + let ids = tabs.map(tab => tab.id); + this.tabPresenter.tabPresenter.remove(ids); + } + + async addbookmark(title) { + let tab = await this.tabPresenter.getCurrent(); + let item = await this.bookmarkRepository.create(title, tab.url); + let message = 'Saved current page: ' + item.url; + return this.consolePresenter.showInfo(tab.id, message); + } + + set(keywords) { + // TODO implement set command + } + + async urlOrSearch(keywords) { + try { + return new URL(keywords).href; + } catch (e) { + if (keywords.includes('.') && !keywords.includes(' ')) { + return 'http://' + keywords; + } + let settings = await this.settingRepository.get(); + let engines = settings.search.engines; + + let template = engines[settings.search.default]; + let query = keywords; + + let first = keywords.trimStart().split(' ')[0]; + if (Object.keys(engines).includes(first)) { + template = engines[first]; + query = keywords.trimStart().slice(first.length).trimStart(); + } + return template.replace('{}', encodeURIComponent(query)); + } + } +} diff --git a/src/background/usecases/completions.js b/src/background/usecases/completions.js index 18174bd..ee519e1 100644 --- a/src/background/usecases/completions.js +++ b/src/background/usecases/completions.js @@ -50,7 +50,7 @@ export default class CompletionsInteractor { } queryBuffer(name, keywords) { - return this.queryTabs(name, true, keywords); + return this.queryTabs(name, false, keywords); } queryBdelete(name, keywords) { -- cgit v1.2.3 From 66c23423f931bb66c59cd29cf9279a5de5d56535 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 28 Jul 2018 10:42:32 +0900 Subject: Background operation as Clean Architecture --- src/background/components/operation.js | 3 - src/background/controllers/operation.js | 65 +++++++ .../infrastructures/content-message-listener.js | 8 + src/background/presenters/bookmark.js | 0 src/background/presenters/console.js | 20 +++ src/background/presenters/tab.js | 35 ++++ src/background/usecases/operation.js | 190 +++++++++++++++++++++ 7 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 src/background/controllers/operation.js delete mode 100644 src/background/presenters/bookmark.js create mode 100644 src/background/usecases/operation.js (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/components/operation.js b/src/background/components/operation.js index ce93270..57b2497 100644 --- a/src/background/components/operation.js +++ b/src/background/components/operation.js @@ -22,9 +22,6 @@ export default class BackgroundComponent { onMessage(message, sender) { switch (message.type) { - case messages.BACKGROUND_OPERATION: - return this.store.dispatch( - this.exec(message.operation, sender.tab)); } } diff --git a/src/background/controllers/operation.js b/src/background/controllers/operation.js new file mode 100644 index 0000000..1339006 --- /dev/null +++ b/src/background/controllers/operation.js @@ -0,0 +1,65 @@ +import operations from '../../shared/operations'; +import OperationInteractor from '../usecases/operation'; + +export default class OperationController { + constructor() { + this.operationInteractor = new OperationInteractor(); + } + + // eslint-disable-next-line complexity, max-lines-per-function + exec(operation) { + switch (operation.type) { + case operations.TAB_CLOSE: + return this.operationInteractor.close(false); + case operations.TAB_CLOSE_FORCE: + return this.operationInteractor.close(true); + case operations.TAB_REOPEN: + return this.operationInteractor.reopen(); + case operations.TAB_PREV: + return this.operationInteractor.selectPrev(1); + case operations.TAB_NEXT: + return this.operationInteractor.selectNext(1); + case operations.TAB_FIRST: + return this.operationInteractor.selectFirst(); + case operations.TAB_LAST: + return this.operationInteractor.selectLast(); + case operations.TAB_PREV_SEL: + return this.operationInteractor.selectPrevSelected(); + case operations.TAB_RELOAD: + return this.operationInteractor.reload(operation.cache); + case operations.TAB_PIN: + return this.operationInteractor.setPinned(true); + case operations.TAB_UNPIN: + return this.operationInteractor.setPinned(false); + case operations.TAB_TOGGLE_PINNED: + return this.operationInteractor.togglePinned(); + case operations.TAB_DUPLICATE: + return this.operationInteractor.duplicate(); + case operations.PAGE_SOURCE: + return this.operationInteractor.openPageSource(); + case operations.ZOOM_IN: + return this.operationInteractor.zoomIn(); + case operations.ZOOM_OUT: + return this.operationInteractor.zoomOut(); + case operations.ZOOM_NEUTRAL: + return this.operationInteractor.zoomNutoral(); + case operations.COMMAND_SHOW: + return this.operationInteractor.showCommand(); + case operations.COMMAND_SHOW_OPEN: + return this.operationInteractor.showOpenCommand(operation.alter); + case operations.COMMAND_SHOW_TABOPEN: + return this.operationInteractor.showTabopenCommand(operation.alter); + case operations.COMMAND_SHOW_WINOPEN: + return this.operationInteractor.showWinopenCommand(operation.alter); + case operations.COMMAND_SHOW_BUFFER: + return this.operationInteractor.showBufferCommand(); + case operations.COMMAND_SHOW_ADDBOOKMARK: + return this.operationInteractor.showAddbookmarkCommand(operation.alter); + case operations.FIND_START: + return this.operationInteractor.findStart(); + case operations.CANCEL: + return this.operationInteractor.hideConsole(); + } + } +} + diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index 2e84fcc..277d108 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -4,6 +4,7 @@ import SettingController from '../controllers/setting'; import FindController from '../controllers/find'; import AddonEnabledController from '../controllers/addon-enabled'; import LinkController from '../controllers/link'; +import OperationController from '../controllers/operation'; export default class ContentMessageListener { constructor() { @@ -12,6 +13,7 @@ export default class ContentMessageListener { this.findController = new FindController(); this.addonEnabledController = new AddonEnabledController(); this.linkController = new LinkController(); + this.backgroundOperationController = new OperationController(); } run() { @@ -46,6 +48,8 @@ export default class ContentMessageListener { case messages.OPEN_URL: return this.onOpenUrl( message.newTab, message.url, sender.tab.id, message.background); + case messages.BACKGROUND_OPERATION: + return this.onBackgroundOperation(message.operation); } } @@ -85,4 +89,8 @@ export default class ContentMessageListener { } return this.linkController.openToTab(url, openerId); } + + onBackgroundOperation(operation) { + return this.backgroundOperationController.exec(operation); + } } diff --git a/src/background/presenters/bookmark.js b/src/background/presenters/bookmark.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/background/presenters/console.js b/src/background/presenters/console.js index f7d3777..8259238 100644 --- a/src/background/presenters/console.js +++ b/src/background/presenters/console.js @@ -1,16 +1,36 @@ import messages from '../../shared/messages'; export default class ConsolePresenter { + showCommand(tabId, command) { + return browser.tabs.sendMessage(tabId, { + type: messages.CONSOLE_SHOW_COMMAND, + command, + }); + } + + showFind(tabId) { + return browser.tabs.sendMessage(tabId, { + type: messages.CONSOLE_SHOW_FIND + }); + } + showInfo(tabId, message) { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_INFO, text: message, }); } + showError(tabId, message) { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_ERROR, text: message, }); } + + hide(tabId) { + return browser.tabs.sendMessage(tabId, { + type: messages.CONSOLE_HIDE, + }); + } } diff --git a/src/background/presenters/tab.js b/src/background/presenters/tab.js index be6955a..2a06a5a 100644 --- a/src/background/presenters/tab.js +++ b/src/background/presenters/tab.js @@ -48,6 +48,41 @@ export default class TabPresenter { return browser.tabs.remove(ids); } + async reopen() { + let window = await browser.windows.getCurrent(); + let sessions = await browser.sessions.getRecentlyClosed(); + let session = sessions.find((s) => { + return s.tab && s.tab.windowId === window.id; + }); + if (!session) { + return; + } + if (session.tab) { + return browser.sessions.restore(session.tab.sessionId); + } + return browser.sessions.restore(session.window.sessionId); + } + + reload(tabId, cache) { + return browser.tabs.reload(tabId, { bypassCache: cache }); + } + + setPinned(tabId, pinned) { + return browser.tabs.update(tabId, { pinned }); + } + + duplicate(id) { + return browser.tabs.duplicate(id); + } + + getZoom(tabId) { + return browser.tabs.getZoom(tabId); + } + + setZoom(tabId, factor) { + return browser.tabs.setZoom(tabId, factor); + } + async createAdjacent(url, { openerTabId, active }) { let tabs = await browser.tabs.query({ active: true, currentWindow: true diff --git a/src/background/usecases/operation.js b/src/background/usecases/operation.js new file mode 100644 index 0000000..f19c632 --- /dev/null +++ b/src/background/usecases/operation.js @@ -0,0 +1,190 @@ +import MemoryStorage from '../infrastructures/memory-storage'; +import TabPresenter from '../presenters/tab'; +import ConsolePresenter from '../presenters/console'; + +const CURRENT_SELECTED_KEY = 'tabs.current.selected'; +const LAST_SELECTED_KEY = 'tabs.last.selected'; + +const ZOOM_SETTINGS = [ + 0.33, 0.50, 0.66, 0.75, 0.80, 0.90, 1.00, + 1.10, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00 +]; + +export default class OperationInteractor { + constructor() { + this.tabPresenter = new TabPresenter(); + this.tabPresenter.onSelected(info => this.onTabSelected(info.tabId)); + + this.consolePresenter = new ConsolePresenter(); + + this.cache = new MemoryStorage(); + } + + async close(force) { + let tab = await this.tabPresenter.getCurrent(); + if (!force && tab.pinned) { + return; + } + return this.tabPresenter.remove([tab.id]); + } + + reopen() { + return this.tabPresenter.reopen(); + } + + async selectPrev(count) { + let tabs = await this.tabPresenter.getAll(); + if (tabs.length < 2) { + return; + } + let tab = tabs.find(t => t.active); + if (!tab) { + return; + } + let select = (tab.index - count + tabs.length) % tabs.length; + return this.tabPresenter.select(tabs[select].id); + } + + async selectNext(count) { + let tabs = await this.tabPresenter.getAll(); + if (tabs.length < 2) { + return; + } + let tab = tabs.find(t => t.active); + if (!tab) { + return; + } + let select = (tab.index + count) % tabs.length; + return this.tabPresenter.select(tabs[select].id); + } + + async selectFirst() { + let tabs = await this.tabPresenter.getAll(); + return this.tabPresenter.select(tabs[0].id); + } + + async selectLast() { + let tabs = await this.tabPresenter.getAll(); + return this.tabPresenter.select(tabs[tabs.length - 1].id); + } + + async selectPrevSelected() { + let tabId = await this.cache.get(LAST_SELECTED_KEY); + if (tabId === null || typeof tabId === 'undefined') { + return; + } + this.tabPresenter.select(tabId); + } + + async reload(cache) { + let tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.reload(tab.id, cache); + } + + async setPinned(pinned) { + let tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.setPinned(tab.id, pinned); + } + + async togglePinned() { + let tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.setPinned(tab.id, !tab.pinned); + } + + async duplicate() { + let tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.duplicate(tab.id); + } + + async openPageSource() { + let tab = await this.tabPresenter.getCurrent(); + let url = 'view-source:' + tab.url; + return this.tabPresenter.create(url); + } + + async zoomIn(tabId) { + let tab = await this.tabPresenter.getCurrent(); + let current = await this.tabPresenter.getZoom(tab.id); + let factor = ZOOM_SETTINGS.find(f => f > current); + if (factor) { + return this.tabPresenter.setZoom(tabId, factor); + } + } + + async zoomOut(tabId) { + let tab = await this.tabPresenter.getCurrent(); + let current = await this.tabPresenter.getZoom(tab.id); + let factor = [].concat(ZOOM_SETTINGS).reverse().find(f => f < current); + if (factor) { + return this.tabPresenter.setZoom(tabId, factor); + } + } + + zoomNutoral(tabId) { + return this.tabPresenter.setZoom(tabId, 1); + } + + async showCommand() { + let tab = await this.tabPresenter.getCurrent(); + this.consolePresenter.showCommand(tab.id, ''); + } + + async showOpenCommand(alter) { + let tab = await this.tabPresenter.getCurrent(); + let command = 'open '; + if (alter) { + command += tab.url; + } + return this.consolePresenter.showCommand(tab.id, command); + } + + async showTabopenCommand(alter) { + let tab = await this.tabPresenter.getCurrent(); + let command = 'tabopen '; + if (alter) { + command += tab.url; + } + return this.consolePresenter.showCommand(tab.id, command); + } + + async showWinopenCommand(alter) { + let tab = await this.tabPresenter.getCurrent(); + let command = 'winopen '; + if (alter) { + command += tab.url; + } + return this.consolePresenter.showCommand(tab.id, command); + } + + async showBufferCommand() { + let tab = await this.tabPresenter.getCurrent(); + let command = 'buffer '; + return this.consolePresenter.showCommand(tab.id, command); + } + + async showAddbookmarkCommand(alter) { + let tab = await this.tabPresenter.getCurrent(); + let command = 'addbookmark '; + if (alter) { + command += tab.title; + } + return this.consolePresenter.showCommand(tab.id, command); + } + + async findStart() { + let tab = await this.tabPresenter.getCurrent(); + this.consolePresenter.showFind(tab.id); + } + + async hideConsole() { + let tab = await this.tabPresenter.getCurrent(); + this.consolePresenter.hide(tab.id); + } + + onTabSelected(tabId) { + let lastId = this.cache.get(CURRENT_SELECTED_KEY); + this.cache.set(LAST_SELECTED_KEY, lastId); + this.cache.set(CURRENT_SELECTED_KEY, tabId); + } +} + -- cgit v1.2.3 From ee0f7b5806c8e28e3a7dc002e9dde467d1fa2a9b Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 28 Jul 2018 17:43:52 +0900 Subject: Catch errors on background --- src/background/infrastructures/content-message-listener.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index 277d108..2fa5f54 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -18,14 +18,12 @@ export default class ContentMessageListener { run() { browser.runtime.onMessage.addListener((message, sender) => { - try { - return this.onMessage(message, sender); - } catch (e) { + return this.onMessage(message, sender).catch((e) => { return browser.tabs.sendMessage(sender.tab.id, { type: messages.CONSOLE_SHOW_ERROR, text: e.message, }); - } + }); }); } -- cgit v1.2.3 From 4bd2084ba7b23327c26a2d8b24dc4169c14bfa17 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 28 Jul 2018 19:41:07 +0900 Subject: Error on console --- src/background/controllers/command.js | 5 +++++ src/background/infrastructures/content-message-listener.js | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/background/infrastructures/content-message-listener.js') diff --git a/src/background/controllers/command.js b/src/background/controllers/command.js index 41057e0..befab42 100644 --- a/src/background/controllers/command.js +++ b/src/background/controllers/command.js @@ -48,6 +48,10 @@ export default class CommandController { let trimmed = line.trimStart(); let words = trimmed.split(/ +/); let name = words[0]; + if (words[0].length === 0) { + return Promise.resolve(); + } + let keywords = trimmed.slice(name.length).trimStart(); switch (words[0]) { case 'o': @@ -85,5 +89,6 @@ export default class CommandController { case 'set': return this.commandIndicator.set(keywords); } + throw new Error(words[0] + ' command is not defined'); } } diff --git a/src/background/infrastructures/content-message-listener.js b/src/background/infrastructures/content-message-listener.js index 2fa5f54..58716fb 100644 --- a/src/background/infrastructures/content-message-listener.js +++ b/src/background/infrastructures/content-message-listener.js @@ -18,12 +18,19 @@ export default class ContentMessageListener { run() { browser.runtime.onMessage.addListener((message, sender) => { - return this.onMessage(message, sender).catch((e) => { + try { + return this.onMessage(message, sender).catch((e) => { + return browser.tabs.sendMessage(sender.tab.id, { + type: messages.CONSOLE_SHOW_ERROR, + text: e.message, + }); + }); + } catch (e) { return browser.tabs.sendMessage(sender.tab.id, { type: messages.CONSOLE_SHOW_ERROR, text: e.message, }); - }); + } }); } -- cgit v1.2.3