aboutsummaryrefslogtreecommitdiff
path: root/src/content
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2019-05-11 11:37:18 +0900
committerShin'ya Ueoka <ueokande@i-beam.org>2019-05-11 11:37:18 +0900
commit1ba1660269b24446e9df7df0016de8c3e5596c8f (patch)
tree545e801e2039e1a49f86c6337a7110a8ed62cad8 /src/content
parentbacf83a32083c5a4c4a45c061288081423bbf18a (diff)
Make find as a clean architecture
Diffstat (limited to 'src/content')
-rw-r--r--src/content/actions/find.ts100
-rw-r--r--src/content/actions/index.ts11
-rw-r--r--src/content/client/ConsoleClient.ts30
-rw-r--r--src/content/client/FindClient.ts25
-rw-r--r--src/content/components/top-content/find.ts26
-rw-r--r--src/content/components/top-content/index.ts2
-rw-r--r--src/content/presenters/FindPresenter.ts59
-rw-r--r--src/content/reducers/find.ts25
-rw-r--r--src/content/reducers/index.ts4
-rw-r--r--src/content/repositories/FindRepository.ts19
-rw-r--r--src/content/repositories/SettingRepository.ts1
-rw-r--r--src/content/usecases/FindUseCase.ts81
12 files changed, 224 insertions, 159 deletions
diff --git a/src/content/actions/find.ts b/src/content/actions/find.ts
deleted file mode 100644
index 53e03ae..0000000
--- a/src/content/actions/find.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// window.find(aString, aCaseSensitive, aBackwards, aWrapAround,
-// aWholeWord, aSearchInFrames);
-//
-// NOTE: window.find is not standard API
-// https://developer.mozilla.org/en-US/docs/Web/API/Window/find
-
-import * as messages from '../../shared/messages';
-import * as actions from './index';
-import * as consoleFrames from '../console-frames';
-
-interface MyWindow extends Window {
- find(
- aString: string,
- aCaseSensitive?: boolean,
- aBackwards?: boolean,
- aWrapAround?: boolean,
- aWholeWord?: boolean,
- aSearchInFrames?: boolean,
- aShowDialog?: boolean): boolean;
-}
-
-// eslint-disable-next-line no-var, vars-on-top, init-declarations
-declare var window: MyWindow;
-
-const find = (str: string, backwards: boolean): boolean => {
- let caseSensitive = false;
- let wrapScan = true;
-
-
- // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work
- // because of same origin policy
-
- // eslint-disable-next-line no-extra-parens
- let found = window.find(str, caseSensitive, backwards, wrapScan);
- if (found) {
- return found;
- }
- let sel = window.getSelection();
- if (sel) {
- sel.removeAllRanges();
- }
-
- // eslint-disable-next-line no-extra-parens
- return window.find(str, caseSensitive, backwards, wrapScan);
-};
-
-// eslint-disable-next-line max-statements
-const findNext = async(
- currentKeyword: string, reset: boolean, backwards: boolean,
-): Promise<actions.FindAction> => {
- if (reset) {
- let sel = window.getSelection();
- if (sel) {
- sel.removeAllRanges();
- }
- }
-
- let keyword = currentKeyword;
- if (currentKeyword) {
- browser.runtime.sendMessage({
- type: messages.FIND_SET_KEYWORD,
- keyword: currentKeyword,
- });
- } else {
- keyword = await browser.runtime.sendMessage({
- type: messages.FIND_GET_KEYWORD,
- });
- }
- if (!keyword) {
- await consoleFrames.postError('No previous search keywords');
- return { type: actions.NOOP };
- }
- let found = find(keyword, backwards);
- if (found) {
- consoleFrames.postInfo('Pattern found: ' + keyword);
- } else {
- consoleFrames.postError('Pattern not found: ' + keyword);
- }
-
- return {
- type: actions.FIND_SET_KEYWORD,
- keyword,
- found,
- };
-};
-
-const next = (
- currentKeyword: string, reset: boolean,
-): Promise<actions.FindAction> => {
- return findNext(currentKeyword, reset, false);
-};
-
-const prev = (
- currentKeyword: string, reset: boolean,
-): Promise<actions.FindAction> => {
- return findNext(currentKeyword, reset, true);
-};
-
-export { next, prev };
diff --git a/src/content/actions/index.ts b/src/content/actions/index.ts
index 4e395c5..f6d19aa 100644
--- a/src/content/actions/index.ts
+++ b/src/content/actions/index.ts
@@ -1,9 +1,6 @@
import Redux from 'redux';
import * as keyUtils from '../../shared/utils/keys';
-// Find
-export const FIND_SET_KEYWORD = 'find.set.keyword';
-
// User input
export const INPUT_KEY_PRESS = 'input.key.press';
export const INPUT_CLEAR_KEYS = 'input.clear.keys';
@@ -27,12 +24,6 @@ export const MARK_SET_LOCAL = 'mark.set.local';
export const NOOP = 'noop';
-export interface FindSetKeywordAction extends Redux.Action {
- type: typeof FIND_SET_KEYWORD;
- keyword: string;
- found: boolean;
-}
-
export interface InputKeyPressAction extends Redux.Action {
type: typeof INPUT_KEY_PRESS;
key: keyUtils.Key;
@@ -84,7 +75,6 @@ export interface NoopAction extends Redux.Action {
type: typeof NOOP;
}
-export type FindAction = FindSetKeywordAction | NoopAction;
export type InputAction = InputKeyPressAction | InputClearKeysAction;
export type FollowAction =
FollowControllerEnableAction | FollowControllerDisableAction |
@@ -94,7 +84,6 @@ export type MarkAction =
MarkCancelAction | MarkSetLocalAction | NoopAction;
export type Action =
- FindAction |
InputAction |
FollowAction |
MarkAction |
diff --git a/src/content/client/ConsoleClient.ts b/src/content/client/ConsoleClient.ts
new file mode 100644
index 0000000..e7046e5
--- /dev/null
+++ b/src/content/client/ConsoleClient.ts
@@ -0,0 +1,30 @@
+import * as messages from '../../shared/messages';
+
+export default interface ConsoleClient {
+ info(text: string): Promise<void>;
+ error(text: string): Promise<void>;
+
+ // eslint-disable-next-line semi
+}
+
+export class ConsoleClientImpl implements ConsoleClient {
+ async info(text: string): Promise<void> {
+ await browser.runtime.sendMessage({
+ type: messages.CONSOLE_FRAME_MESSAGE,
+ message: {
+ type: messages.CONSOLE_SHOW_INFO,
+ text,
+ },
+ });
+ }
+
+ async error(text: string): Promise<void> {
+ await browser.runtime.sendMessage({
+ type: messages.CONSOLE_FRAME_MESSAGE,
+ message: {
+ type: messages.CONSOLE_SHOW_ERROR,
+ text,
+ },
+ });
+ }
+}
diff --git a/src/content/client/FindClient.ts b/src/content/client/FindClient.ts
new file mode 100644
index 0000000..22cd3cb
--- /dev/null
+++ b/src/content/client/FindClient.ts
@@ -0,0 +1,25 @@
+import * as messages from '../../shared/messages';
+
+export default interface FindClient {
+ getGlobalLastKeyword(): Promise<string | null>;
+
+ setGlobalLastKeyword(keyword: string): Promise<void>;
+
+ // eslint-disable-next-line semi
+}
+
+export class FindClientImpl implements FindClient {
+ async getGlobalLastKeyword(): Promise<string | null> {
+ let keyword = await browser.runtime.sendMessage({
+ type: messages.FIND_GET_KEYWORD,
+ });
+ return keyword as string;
+ }
+
+ async setGlobalLastKeyword(keyword: string): Promise<void> {
+ await browser.runtime.sendMessage({
+ type: messages.FIND_SET_KEYWORD,
+ keyword: keyword,
+ });
+ }
+}
diff --git a/src/content/components/top-content/find.ts b/src/content/components/top-content/find.ts
index 74b95bc..c25cbeb 100644
--- a/src/content/components/top-content/find.ts
+++ b/src/content/components/top-content/find.ts
@@ -1,13 +1,12 @@
-import * as findActions from '../../actions/find';
import * as messages from '../../../shared/messages';
import MessageListener from '../../MessageListener';
-export default class FindComponent {
- private store: any;
+import FindUseCase from '../../usecases/FindUseCase';
- constructor(store: any) {
- this.store = store;
+let findUseCase = new FindUseCase();
+export default class FindComponent {
+ constructor() {
new MessageListener().onWebMessage(this.onMessage.bind(this));
}
@@ -20,27 +19,18 @@ export default class FindComponent {
case messages.FIND_PREV:
return this.prev();
}
+ return Promise.resolve();
}
start(text: string) {
- let state = this.store.getState().find;
-
- if (text.length === 0) {
- return this.store.dispatch(
- findActions.next(state.keyword as string, true));
- }
- return this.store.dispatch(findActions.next(text, true));
+ return findUseCase.startFind(text.length === 0 ? null : text);
}
next() {
- let state = this.store.getState().find;
- return this.store.dispatch(
- findActions.next(state.keyword as string, false));
+ return findUseCase.findNext();
}
prev() {
- let state = this.store.getState().find;
- return this.store.dispatch(
- findActions.prev(state.keyword as string, false));
+ return findUseCase.findPrev();
}
}
diff --git a/src/content/components/top-content/index.ts b/src/content/components/top-content/index.ts
index 101edca..b9ef2dd 100644
--- a/src/content/components/top-content/index.ts
+++ b/src/content/components/top-content/index.ts
@@ -17,7 +17,7 @@ export default class TopContent {
new CommonComponent(win, store); // eslint-disable-line no-new
new FollowController(win, store); // eslint-disable-line no-new
- new FindComponent(store); // eslint-disable-line no-new
+ new FindComponent(); // eslint-disable-line no-new
// TODO make component
consoleFrames.initialize(this.win.document);
diff --git a/src/content/presenters/FindPresenter.ts b/src/content/presenters/FindPresenter.ts
new file mode 100644
index 0000000..6dd03f8
--- /dev/null
+++ b/src/content/presenters/FindPresenter.ts
@@ -0,0 +1,59 @@
+import ConsoleClient, { ConsoleClientImpl } from '../client/ConsoleClient';
+
+export default interface FindPresenter {
+ find(keyword: string, backwards: boolean): boolean;
+
+ clearSelection(): void;
+
+ // eslint-disable-next-line semi
+}
+
+// window.find(aString, aCaseSensitive, aBackwards, aWrapAround,
+// aWholeWord, aSearchInFrames);
+//
+// NOTE: window.find is not standard API
+// https://developer.mozilla.org/en-US/docs/Web/API/Window/find
+interface MyWindow extends Window {
+ find(
+ aString: string,
+ aCaseSensitive?: boolean,
+ aBackwards?: boolean,
+ aWrapAround?: boolean,
+ aWholeWord?: boolean,
+ aSearchInFrames?: boolean,
+ aShowDialog?: boolean): boolean;
+}
+
+// eslint-disable-next-line no-var, vars-on-top, init-declarations
+declare var window: MyWindow;
+
+export class FindPresenterImpl implements FindPresenter {
+ private consoleClient: ConsoleClient;
+
+ constructor({ consoleClient = new ConsoleClientImpl() } = {}) {
+ this.consoleClient = consoleClient;
+ }
+
+ find(keyword: string, backwards: boolean): boolean {
+ let caseSensitive = false;
+ let wrapScan = true;
+
+
+ // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work
+ // because of same origin policy
+ let found = window.find(keyword, caseSensitive, backwards, wrapScan);
+ if (found) {
+ return found;
+ }
+ this.clearSelection();
+
+ return window.find(keyword, caseSensitive, backwards, wrapScan);
+ }
+
+ clearSelection(): void {
+ let sel = window.getSelection();
+ if (sel) {
+ sel.removeAllRanges();
+ }
+ }
+}
diff --git a/src/content/reducers/find.ts b/src/content/reducers/find.ts
deleted file mode 100644
index 8c3e637..0000000
--- a/src/content/reducers/find.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as actions from '../actions';
-
-export interface State {
- keyword: string | null;
- found: boolean;
-}
-
-const defaultState: State = {
- keyword: null,
- found: false,
-};
-
-export default function reducer(
- state: State = defaultState,
- action: actions.FindAction,
-): State {
- switch (action.type) {
- case actions.FIND_SET_KEYWORD:
- return { ...state,
- keyword: action.keyword,
- found: action.found, };
- default:
- return state;
- }
-}
diff --git a/src/content/reducers/index.ts b/src/content/reducers/index.ts
index 21e8918..812a404 100644
--- a/src/content/reducers/index.ts
+++ b/src/content/reducers/index.ts
@@ -1,17 +1,15 @@
import { combineReducers } from 'redux';
-import find, { State as FindState } from './find';
import input, { State as InputState } from './input';
import followController, { State as FollowControllerState }
from './follow-controller';
import mark, { State as MarkState } from './mark';
export interface State {
- find: FindState;
input: InputState;
followController: FollowControllerState;
mark: MarkState;
}
export default combineReducers({
- find, input, followController, mark,
+ input, followController, mark,
});
diff --git a/src/content/repositories/FindRepository.ts b/src/content/repositories/FindRepository.ts
new file mode 100644
index 0000000..85eca40
--- /dev/null
+++ b/src/content/repositories/FindRepository.ts
@@ -0,0 +1,19 @@
+export default interface FindRepository {
+ getLastKeyword(): string | null;
+
+ setLastKeyword(keyword: string): void;
+
+ // eslint-disable-next-line semi
+}
+
+let current: string | null = null;
+
+export class FindRepositoryImpl implements FindRepository {
+ getLastKeyword(): string | null {
+ return current;
+ }
+
+ setLastKeyword(keyword: string): void {
+ current = keyword;
+ }
+}
diff --git a/src/content/repositories/SettingRepository.ts b/src/content/repositories/SettingRepository.ts
index ce13c25..711b2a2 100644
--- a/src/content/repositories/SettingRepository.ts
+++ b/src/content/repositories/SettingRepository.ts
@@ -18,5 +18,4 @@ export class SettingRepositoryImpl implements SettingRepository {
get(): Settings {
return current;
}
-
}
diff --git a/src/content/usecases/FindUseCase.ts b/src/content/usecases/FindUseCase.ts
new file mode 100644
index 0000000..4fda323
--- /dev/null
+++ b/src/content/usecases/FindUseCase.ts
@@ -0,0 +1,81 @@
+import FindPresenter, { FindPresenterImpl } from '../presenters/FindPresenter';
+import FindRepository, { FindRepositoryImpl }
+ from '../repositories/FindRepository';
+import FindClient, { FindClientImpl } from '../client/FindClient';
+import ConsoleClient, { ConsoleClientImpl } from '../client/ConsoleClient';
+
+export default class FindUseCase {
+ private presenter: FindPresenter;
+
+ private repository: FindRepository;
+
+ private client: FindClient;
+
+ private consoleClient: ConsoleClient;
+
+ constructor({
+ presenter = new FindPresenterImpl() as FindPresenter,
+ repository = new FindRepositoryImpl(),
+ client = new FindClientImpl(),
+ consoleClient = new ConsoleClientImpl(),
+ } = {}) {
+ this.presenter = presenter;
+ this.repository = repository;
+ this.client = client;
+ this.consoleClient = consoleClient;
+ }
+
+ async startFind(keyword: string | null): Promise<void> {
+ this.presenter.clearSelection();
+ if (keyword) {
+ this.saveKeyword(keyword);
+ } else {
+ let lastKeyword = await this.getKeyword();
+ if (!lastKeyword) {
+ return this.showNoLastKeywordError();
+ }
+ this.saveKeyword(lastKeyword);
+ }
+ return this.findNext();
+ }
+
+ findNext(): Promise<void> {
+ return this.findNextPrev(false);
+ }
+
+ findPrev(): Promise<void> {
+ return this.findNextPrev(true);
+ }
+
+ private async findNextPrev(
+ backwards: boolean,
+ ): Promise<void> {
+ let keyword = await this.getKeyword();
+ if (!keyword) {
+ return this.showNoLastKeywordError();
+ }
+ let found = this.presenter.find(keyword, backwards);
+ if (found) {
+ this.consoleClient.info('Pattern found: ' + keyword);
+ } else {
+ this.consoleClient.error('Pattern not found: ' + keyword);
+ }
+ }
+
+ private async getKeyword(): Promise<string | null> {
+ let keyword = this.repository.getLastKeyword();
+ if (!keyword) {
+ keyword = await this.client.getGlobalLastKeyword();
+ }
+ return keyword;
+ }
+
+ private async saveKeyword(keyword: string): Promise<void> {
+ this.repository.setLastKeyword(keyword);
+ await this.client.setGlobalLastKeyword(keyword);
+ }
+
+ private async showNoLastKeywordError(): Promise<void> {
+ await this.consoleClient.error('No previous search keywords');
+ }
+}