From bf7c125fb214b52d67527bdd292a4b5bb81b1d32 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Fri, 20 Jul 2018 23:36:03 +0900 Subject: [PATCH] My First Clean Architecture --- src/background/components/background.js | 3 -- src/background/controllers/completions.js | 42 +++++++++++++++++++ src/background/domains/completion-group.js | 14 +++++++ src/background/domains/completion-item.js | 24 +++++++++++ src/background/domains/completions.js | 27 ++++++++++++ src/background/index.js | 4 ++ .../content-message-listener.js | 34 +++++++++++++++ src/background/usecases/completions.js | 40 ++++++++++++++++++ 8 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 src/background/controllers/completions.js create mode 100644 src/background/domains/completion-group.js create mode 100644 src/background/domains/completion-item.js create mode 100644 src/background/domains/completions.js create mode 100644 src/background/infrastructures/content-message-listener.js create mode 100644 src/background/usecases/completions.js diff --git a/src/background/components/background.js b/src/background/components/background.js index f7b0511..d933d7b 100644 --- a/src/background/components/background.js +++ b/src/background/components/background.js @@ -3,7 +3,6 @@ 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'; -import * as completions from '../shared/completions'; export default class BackgroundComponent { constructor(store) { @@ -41,8 +40,6 @@ export default class BackgroundComponent { return this.broadcastSettingsChanged(); case messages.SETTINGS_QUERY: return Promise.resolve(this.store.getState().setting.value); - case messages.CONSOLE_QUERY_COMPLETIONS: - return completions.complete(message.text, settings.value); case messages.SETTINGS_RELOAD: this.store.dispatch(settingActions.load()); return this.broadcastSettingsChanged(); diff --git a/src/background/controllers/completions.js b/src/background/controllers/completions.js new file mode 100644 index 0000000..613940f --- /dev/null +++ b/src/background/controllers/completions.js @@ -0,0 +1,42 @@ +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); + } + switch (words[0]) { + case 'o': + case 'open': + case 't': + case 'tabopen': + case 'w': + case 'winopen': + break; + case 'b': + case 'buffer': + break; + case 'bd!': + case 'bdel!': + case 'bdelete!': + case 'bdeletes!': + break; + case 'bd': + case 'bdel': + case 'bdelete': + case 'bdeletes': + break; + case 'set': + break; + } + return Promise.resolve(Completions.empty()); + } +} diff --git a/src/background/domains/completion-group.js b/src/background/domains/completion-group.js new file mode 100644 index 0000000..1749d72 --- /dev/null +++ b/src/background/domains/completion-group.js @@ -0,0 +1,14 @@ +export default class CompletionGroup { + constructor(name, items) { + this.name0 = name; + this.items0 = items; + } + + get name() { + return this.name0; + } + + get items() { + return this.items0; + } +} diff --git a/src/background/domains/completion-item.js b/src/background/domains/completion-item.js new file mode 100644 index 0000000..c7ad8a1 --- /dev/null +++ b/src/background/domains/completion-item.js @@ -0,0 +1,24 @@ +export default class CompletionItem { + constructor({ caption, content, url, icon }) { + this.caption0 = caption; + this.content0 = content; + this.url0 = url; + this.icon0 = icon; + } + + get caption() { + return this.caption0; + } + + get content() { + return this.content0; + } + + get url() { + return this.url0; + } + + get icon() { + return this.icon0; + } +} diff --git a/src/background/domains/completions.js b/src/background/domains/completions.js new file mode 100644 index 0000000..4e4219f --- /dev/null +++ b/src/background/domains/completions.js @@ -0,0 +1,27 @@ +export default class Completions { + constructor(groups) { + this.g = groups; + } + + get groups() { + return this.g; + } + + serialize() { + return this.groups.map(group => ({ + name: group.name, + items: group.items.map(item => ({ + caption: item.caption, + content: item.content, + url: item.url, + icon: item.icon, + })), + })); + } + + static EMPTY_COMPLETIONS = new Completions([]); + + static empty() { + return Completions.EMPTY_COMPLETIONS; + } +} diff --git a/src/background/index.js b/src/background/index.js index 1e4c078..619b076 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -8,6 +8,8 @@ import { createStore, applyMiddleware } from 'redux'; import promise from 'redux-promise'; import * as versions from './shared/versions'; +import ContentMessageListener from './infrastructures/content-message-listener'; + const store = createStore( reducers, applyMiddleware(promise), @@ -32,3 +34,5 @@ const indicatorComponent = new IndicatorComponent(store); store.dispatch(settingActions.load()); checkAndNotifyUpdated(); + +new ContentMessageListener().run(); 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()); + } +} diff --git a/src/background/usecases/completions.js b/src/background/usecases/completions.js new file mode 100644 index 0000000..fc1ff52 --- /dev/null +++ b/src/background/usecases/completions.js @@ -0,0 +1,40 @@ +import CompletionItem from '../domains/completion-item'; +import CompletionGroup from '../domains/completion-group'; +import Completions from '../domains/completions'; +import CompletionRepository from '../repositories/completions'; +import CommandDocs from 'background/shared/commands/docs'; + +export default class CompletionsInteractor { + constructor() { + this.completionRepository = new CompletionRepository(); + } + + queryConsoleCommand(prefix) { + let keys = Object.keys(CommandDocs); + let items = keys + .filter(name => name.startsWith(prefix)) + .map(name => ({ + caption: name, + content: name, + url: CommandDocs[name], + })); + + if (items.length === 0) { + return Promise.resolve(Completions.empty()); + } + return Promise.resolve(new Completions( + [new CompletionGroup('Console Command', items)] + )); + } + + async queryBdeleteCommand(name, force, args) { + let tabs = await this.completionRepository.queryTabs(args); + let items = tabs.map(tab => new CompletionItem({ + caption: tab.title, + content: name + ' ' + tab.title, + url: tab.url, + icon: tab.favIconUrl + })); + return [new CompletionGroup('Buffers', items)]; + } +}