diff options
| author | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-11 11:37:18 +0900 | 
|---|---|---|
| committer | Shin'ya Ueoka <ueokande@i-beam.org> | 2019-05-11 11:37:18 +0900 | 
| commit | 1ba1660269b24446e9df7df0016de8c3e5596c8f (patch) | |
| tree | 545e801e2039e1a49f86c6337a7110a8ed62cad8 /src/content | |
| parent | bacf83a32083c5a4c4a45c061288081423bbf18a (diff) | |
Make find as a clean architecture
Diffstat (limited to 'src/content')
| -rw-r--r-- | src/content/actions/find.ts | 100 | ||||
| -rw-r--r-- | src/content/actions/index.ts | 11 | ||||
| -rw-r--r-- | src/content/client/ConsoleClient.ts | 30 | ||||
| -rw-r--r-- | src/content/client/FindClient.ts | 25 | ||||
| -rw-r--r-- | src/content/components/top-content/find.ts | 26 | ||||
| -rw-r--r-- | src/content/components/top-content/index.ts | 2 | ||||
| -rw-r--r-- | src/content/presenters/FindPresenter.ts | 59 | ||||
| -rw-r--r-- | src/content/reducers/find.ts | 25 | ||||
| -rw-r--r-- | src/content/reducers/index.ts | 4 | ||||
| -rw-r--r-- | src/content/repositories/FindRepository.ts | 19 | ||||
| -rw-r--r-- | src/content/repositories/SettingRepository.ts | 1 | ||||
| -rw-r--r-- | src/content/usecases/FindUseCase.ts | 81 | 
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'); +  } +}  | 
