Mark set/jump as a clean architecture

This commit is contained in:
Shin'ya Ueoka 2019-05-11 16:38:08 +09:00
parent ebfb172520
commit c6288f19d9
16 changed files with 316 additions and 137 deletions

View file

@ -20,7 +20,6 @@ export const FOLLOW_CONTROLLER_BACKSPACE = 'follow.controller.backspace';
export const MARK_START_SET = 'mark.start.set';
export const MARK_START_JUMP = 'mark.start.jump';
export const MARK_CANCEL = 'mark.cancel';
export const MARK_SET_LOCAL = 'mark.set.local';
export const NOOP = 'noop';
@ -64,13 +63,6 @@ export interface MarkCancelAction extends Redux.Action {
type: typeof MARK_CANCEL;
}
export interface MarkSetLocalAction extends Redux.Action {
type: typeof MARK_SET_LOCAL;
key: string;
x: number;
y: number;
}
export interface NoopAction extends Redux.Action {
type: typeof NOOP;
}
@ -80,8 +72,7 @@ export type FollowAction =
FollowControllerEnableAction | FollowControllerDisableAction |
FollowControllerKeyPressAction | FollowControllerBackspaceAction;
export type MarkAction =
MarkStartSetAction | MarkStartJumpAction |
MarkCancelAction | MarkSetLocalAction | NoopAction;
MarkStartSetAction | MarkStartJumpAction | MarkCancelAction | NoopAction;
export type Action =
InputAction |

View file

@ -1,5 +1,4 @@
import * as actions from './index';
import * as messages from '../../shared/messages';
const startSet = (): actions.MarkAction => {
return { type: actions.MARK_START_SET };
@ -13,34 +12,6 @@ const cancel = (): actions.MarkAction => {
return { type: actions.MARK_CANCEL };
};
const setLocal = (key: string, x: number, y: number): actions.MarkAction => {
return {
type: actions.MARK_SET_LOCAL,
key,
x,
y,
};
};
const setGlobal = (key: string, x: number, y: number): actions.MarkAction => {
browser.runtime.sendMessage({
type: messages.MARK_SET_GLOBAL,
key,
x,
y,
});
return { type: actions.NOOP };
};
const jumpGlobal = (key: string): actions.MarkAction => {
browser.runtime.sendMessage({
type: messages.MARK_JUMP_GLOBAL,
key,
});
return { type: actions.NOOP };
};
export {
startSet, startJump, cancel, setLocal,
setGlobal, jumpGlobal,
startSet, startJump, cancel,
};

View file

@ -0,0 +1,28 @@
import Mark from '../domains/Mark';
import * as messages from '../../shared/messages';
export default interface MarkClient {
setGloablMark(key: string, mark: Mark): Promise<void>;
jumpGlobalMark(key: string): Promise<void>;
// eslint-disable-next-line semi
}
export class MarkClientImpl implements MarkClient {
async setGloablMark(key: string, mark: Mark): Promise<void> {
await browser.runtime.sendMessage({
type: messages.MARK_SET_GLOBAL,
key,
x: mark.x,
y: mark.y,
});
}
async jumpGlobalMark(key: string): Promise<void> {
await browser.runtime.sendMessage({
type: messages.MARK_JUMP_GLOBAL,
key,
});
}
}

View file

@ -1,22 +1,15 @@
import * as markActions from '../../actions/mark';
import * as consoleFrames from '../..//console-frames';
import * as keyUtils from '../../../shared/utils/keys';
import Mark from '../../Mark';
import { SettingRepositoryImpl } from '../../repositories/SettingRepository';
import { ScrollPresenterImpl } from '../../presenters/ScrollPresenter';
import MarkUseCase from '../../usecases/MarkUseCase';
let settingRepository = new SettingRepositoryImpl();
let scrollPresenter = new ScrollPresenterImpl();
let markUseCase = new MarkUseCase();
const cancelKey = (key: keyUtils.Key): boolean => {
return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey);
};
const globalKey = (key: string): boolean => {
return (/^[A-Z0-9]$/).test(key);
};
export default class MarkComponent {
private store: any;
@ -26,7 +19,6 @@ export default class MarkComponent {
// eslint-disable-next-line max-statements
key(key: keyUtils.Key) {
let smoothscroll = settingRepository.get().properties.smoothscroll;
let { mark: markState } = this.store.getState();
if (!markState.setMode && !markState.jumpMode) {
@ -40,45 +32,13 @@ export default class MarkComponent {
if (key.ctrlKey || key.metaKey || key.altKey) {
consoleFrames.postError('Unknown mark');
} else if (globalKey(key.key) && markState.setMode) {
this.doSetGlobal(key);
} else if (globalKey(key.key) && markState.jumpMode) {
this.doJumpGlobal(key);
} else if (markState.setMode) {
this.doSet(key);
markUseCase.set(key.key);
} else if (markState.jumpMode) {
this.doJump(markState.marks, key, smoothscroll);
markUseCase.jump(key.key);
}
this.store.dispatch(markActions.cancel());
return true;
}
doSet(key: keyUtils.Key) {
let { x, y } = scrollPresenter.getScroll();
this.store.dispatch(markActions.setLocal(key.key, x, y));
}
doJump(
marks: { [key: string]: Mark },
key: keyUtils.Key,
smoothscroll: boolean,
) {
if (!marks[key.key]) {
consoleFrames.postError('Mark is not set');
return;
}
let { x, y } = marks[key.key];
scrollPresenter.scrollTo(x, y, smoothscroll);
}
doSetGlobal(key: keyUtils.Key) {
let { x, y } = scrollPresenter.getScroll();
this.store.dispatch(markActions.setGlobal(key.key, x, y));
}
doJumpGlobal(key: keyUtils.Key) {
this.store.dispatch(markActions.jumpGlobal(key.key));
}
}

View file

@ -94,7 +94,7 @@ class Scroller {
}
}
type Point = { x: number, y: number };
export type Point = { x: number, y: number };
export default interface ScrollPresenter {
getScroll(): Point;

View file

@ -1,16 +1,13 @@
import Mark from '../Mark';
import * as actions from '../actions';
export interface State {
setMode: boolean;
jumpMode: boolean;
marks: { [key: string]: Mark };
}
const defaultState: State = {
setMode: false,
jumpMode: false,
marks: {},
};
export default function reducer(
@ -24,11 +21,6 @@ export default function reducer(
return { ...state, jumpMode: true };
case actions.MARK_CANCEL:
return { ...state, setMode: false, jumpMode: false };
case actions.MARK_SET_LOCAL: {
let marks = { ...state.marks };
marks[action.key] = { x: action.x, y: action.y };
return { ...state, setMode: false, marks };
}
default:
return state;
}

View file

@ -0,0 +1,25 @@
import Mark from '../domains/Mark';
export default interface MarkRepository {
set(key: string, mark: Mark): void;
get(key: string): Mark | null;
// eslint-disable-next-line semi
}
const saved: {[key: string]: Mark} = {};
export class MarkRepositoryImpl implements MarkRepository {
set(key: string, mark: Mark): void {
saved[key] = mark;
}
get(key: string): Mark | null {
let v = saved[key];
if (!v) {
return null;
}
return { ...v };
}
}

View file

@ -0,0 +1,62 @@
import ScrollPresenter, { ScrollPresenterImpl }
from '../presenters/ScrollPresenter';
import MarkClient, { MarkClientImpl } from '../client/MarkClient';
import MarkRepository, { MarkRepositoryImpl }
from '../repositories/MarkRepository';
import SettingRepository, { SettingRepositoryImpl }
from '../repositories/SettingRepository';
import ConsoleClient, { ConsoleClientImpl } from '../client/ConsoleClient';
export default class MarkUseCase {
private scrollPresenter: ScrollPresenter;
private client: MarkClient;
private repository: MarkRepository;
private settingRepository: SettingRepository;
private consoleClient: ConsoleClient;
constructor({
scrollPresenter = new ScrollPresenterImpl(),
client = new MarkClientImpl(),
repository = new MarkRepositoryImpl(),
settingRepository = new SettingRepositoryImpl(),
consoleClient = new ConsoleClientImpl(),
} = {}) {
this.scrollPresenter = scrollPresenter;
this.client = client;
this.repository = repository;
this.settingRepository = settingRepository;
this.consoleClient = consoleClient;
}
async set(key: string): Promise<void> {
let pos = this.scrollPresenter.getScroll();
if (this.globalKey(key)) {
this.client.setGloablMark(key, pos);
await this.consoleClient.info(`Set global mark to '${key}'`);
} else {
this.repository.set(key, pos);
await this.consoleClient.info(`Set local mark to '${key}'`);
}
}
async jump(key: string): Promise<void> {
if (this.globalKey(key)) {
await this.client.jumpGlobalMark(key);
} else {
let pos = this.repository.get(key);
if (!pos) {
throw new Error('Mark is not set');
}
let smooth = this.settingRepository.get().properties.smoothscroll;
this.scrollPresenter.scrollTo(pos.x, pos.y, smooth);
}
}
private globalKey(key: string) {
return (/^[A-Z0-9]$/).test(key);
}
}