src/content

jh-changes
Shin'ya Ueoka 6 years ago
parent a0882bbceb
commit b002d70070
  1. 16
      src/background/repositories/BrowserSettingRepository.ts
  2. 17
      src/background/usecases/MarkUseCase.ts
  3. 2
      src/background/usecases/VersionUseCase.ts
  4. 6
      src/background/usecases/filters.ts
  5. 51
      src/console/components/Console.tsx
  6. 16
      src/console/components/console/Input.tsx
  7. 2
      src/console/components/console/Message.tsx
  8. 2
      src/console/reducers/index.ts
  9. 6
      src/content/Mark.ts
  10. 18
      src/content/actions/find.ts
  11. 3
      src/content/actions/index.ts
  12. 3
      src/content/actions/input.ts
  13. 2
      src/content/components/common/index.ts
  14. 41
      src/content/components/common/mark.ts
  15. 2
      src/content/index.ts
  16. 3
      src/content/reducers/input.ts
  17. 6
      src/content/reducers/mark.ts
  18. 2
      src/content/site-style.ts
  19. 1
      test/content/reducers/setting.test.ts

@ -1,5 +1,21 @@
import * as urls from '../../shared/urls'; import * as urls from '../../shared/urls';
declare namespace browser.browserSettings.homepageOverride {
type BrowserSettings = {
value: string;
levelOfControl: LevelOfControlType;
};
type LevelOfControlType =
'not_controllable' |
'controlled_by_other_extensions' |
'controllable_by_this_extension' |
'controlled_by_this_extension';
function get(param: object): Promise<BrowserSettings>;
}
export default class BrowserSettingRepository { export default class BrowserSettingRepository {
async getHomepageUrls(): Promise<string[]> { async getHomepageUrls(): Promise<string[]> {
let { value } = await browser.browserSettings.homepageOverride.get({}); let { value } = await browser.browserSettings.homepageOverride.get({});

@ -21,7 +21,7 @@ export default class MarkUseCase {
async setGlobal(key: string, x: number, y: number): Promise<any> { async setGlobal(key: string, x: number, y: number): Promise<any> {
let tab = await this.tabPresenter.getCurrent(); let tab = await this.tabPresenter.getCurrent();
let mark = { tabId: tab.id, url: tab.url, x, y }; let mark = { tabId: tab.id as number, url: tab.url as string, x, y };
return this.markRepository.setMark(key, mark); return this.markRepository.setMark(key, mark);
} }
@ -33,15 +33,14 @@ export default class MarkUseCase {
return this.consoleClient.showError( return this.consoleClient.showError(
current.id as number, 'Mark is not set'); current.id as number, 'Mark is not set');
} }
try {
return this.contentMessageClient.scrollTo( await this.contentMessageClient.scrollTo(mark.tabId, mark.x, mark.y);
mark.tabId, mark.x, mark.y
).then(() => {
return this.tabPresenter.select(mark.tabId); return this.tabPresenter.select(mark.tabId);
}).catch(async() => { } catch (e) {
let tab = await this.tabPresenter.create(mark.url); let tab = await this.tabPresenter.create(mark.url);
let mark2 = { tabId: tab.id, url: mark.url, x: mark.x, y: mark.y }; return this.markRepository.setMark(key, {
return this.markRepository.setMark(key, mark2); tabId: tab.id as number, url: mark.url, x: mark.x, y: mark.y,
}); });
}
} }
} }

@ -1,4 +1,3 @@
import manifest from '../../../manifest.json';
import TabPresenter from '../presenters/TabPresenter'; import TabPresenter from '../presenters/TabPresenter';
import NotifyPresenter from '../presenters/NotifyPresenter'; import NotifyPresenter from '../presenters/NotifyPresenter';
@ -13,6 +12,7 @@ export default class VersionUseCase {
} }
notify(): Promise<void> { notify(): Promise<void> {
let manifest = browser.runtime.getManifest();
let title = `Vim Vixen ${manifest.version} has been installed`; let title = `Vim Vixen ${manifest.version} has been installed`;
let message = 'Click here to see release notes'; let message = 'Click here to see release notes';
let url = this.releaseNoteUrl(manifest.version); let url = this.releaseNoteUrl(manifest.version);

@ -40,7 +40,8 @@ const filterByPathname = (items: Item[], min: number): Item[] => {
let pathname = url.origin + url.pathname; let pathname = url.origin + url.pathname;
if (!hash[pathname]) { if (!hash[pathname]) {
hash[pathname] = item; hash[pathname] = item;
} else if (hash[pathname].url.length > item.url.length) { } else if ((hash[pathname].url as string).length >
(item.url as string).length) {
hash[pathname] = item; hash[pathname] = item;
} }
} }
@ -57,7 +58,8 @@ const filterByOrigin = (items: Item[], min: number): Item[] => {
let origin = new URL(item.url as string).origin; let origin = new URL(item.url as string).origin;
if (!hash[origin]) { if (!hash[origin]) {
hash[origin] = item; hash[origin] = item;
} else if (hash[origin].url.length > item.url.length) { } else if ((hash[origin].url as string).length >
(item.url as string).length) {
hash[origin] = item; hash[origin] = item;
} }
} }

@ -5,23 +5,23 @@ import Input from './console/Input';
import Completion from './console/Completion'; import Completion from './console/Completion';
import Message from './console/Message'; import Message from './console/Message';
import * as consoleActions from '../../console/actions/console'; import * as consoleActions from '../../console/actions/console';
import { State as AppState } from '../reducers';
const COMPLETION_MAX_ITEMS = 33; const COMPLETION_MAX_ITEMS = 33;
interface Props { type StateProps = ReturnType<typeof mapStateToProps>;
mode?: string; interface DispatchProps {
consoleText?: string; dispatch: (action: any) => void,
messageText?: string;
children?: string;
} }
type Props = StateProps & DispatchProps
class Console extends React.Component<Props> { class Console extends React.Component<Props> {
private input: HTMLInputElement | null; private input: React.RefObject<Input>;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.input = null; this.input = React.createRef();
} }
onBlur() { onBlur() {
@ -34,7 +34,7 @@ class Console extends React.Component<Props> {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
let value = e.target.value; let value = (e.target as HTMLInputElement).value;
if (this.props.mode === 'command') { if (this.props.mode === 'command') {
return this.props.dispatch(consoleActions.enterCommand(value)); return this.props.dispatch(consoleActions.enterCommand(value));
} else if (this.props.mode === 'find') { } else if (this.props.mode === 'find') {
@ -55,15 +55,12 @@ class Console extends React.Component<Props> {
} }
onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) { onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) { switch (e.key) {
this.props.dispatch(consoleActions.hideCommand()); case 'Escape':
}
switch (e.keyCode) {
case KeyboardEvent.DOM_VK_ESCAPE:
return this.props.dispatch(consoleActions.hideCommand()); return this.props.dispatch(consoleActions.hideCommand());
case KeyboardEvent.DOM_VK_RETURN: case 'Enter':
return this.doEnter(e); return this.doEnter(e);
case KeyboardEvent.DOM_VK_TAB: case 'Tab':
if (e.shiftKey) { if (e.shiftKey) {
this.props.dispatch(consoleActions.completionPrev()); this.props.dispatch(consoleActions.completionPrev());
} else { } else {
@ -72,22 +69,22 @@ class Console extends React.Component<Props> {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
break; break;
case KeyboardEvent.DOM_VK_OPEN_BRACKET: case '[':
if (e.ctrlKey) { if (e.ctrlKey) {
return this.props.dispatch(consoleActions.hideCommand()); return this.props.dispatch(consoleActions.hideCommand());
} }
break; break;
case KeyboardEvent.DOM_VK_M: case 'm':
if (e.ctrlKey) { if (e.ctrlKey) {
return this.doEnter(e); return this.doEnter(e);
} }
break; break;
case KeyboardEvent.DOM_VK_N: case 'n':
if (e.ctrlKey) { if (e.ctrlKey) {
this.selectNext(e); this.selectNext(e);
} }
break; break;
case KeyboardEvent.DOM_VK_P: case 'p':
if (e.ctrlKey) { if (e.ctrlKey) {
this.selectPrev(e); this.selectPrev(e);
} }
@ -105,9 +102,6 @@ class Console extends React.Component<Props> {
componentDidUpdate(prevProps: Props) { componentDidUpdate(prevProps: Props) {
if (!this.input) {
return;
}
if (prevProps.mode !== 'command' && this.props.mode === 'command') { if (prevProps.mode !== 'command' && this.props.mode === 'command') {
this.props.dispatch( this.props.dispatch(
consoleActions.getCompletions(this.props.consoleText)); consoleActions.getCompletions(this.props.consoleText));
@ -128,7 +122,7 @@ class Console extends React.Component<Props> {
select={this.props.select} select={this.props.select}
/> />
<Input <Input
ref={(c) => { this.input = c; }} ref={this.input}
mode={this.props.mode} mode={this.props.mode}
onBlur={this.onBlur.bind(this)} onBlur={this.onBlur.bind(this)}
onKeyDown={this.onKeyDown.bind(this)} onKeyDown={this.onKeyDown.bind(this)}
@ -148,11 +142,14 @@ class Console extends React.Component<Props> {
focus() { focus() {
window.focus(); window.focus();
if (this.input) { if (this.input.current) {
this.input.focus(); this.input.current.focus();
} }
} }
} }
const mapStateToProps = (state: any) => state; const mapStateToProps = (state: AppState) => ({ ...state });
export default connect(mapStateToProps)(Console);
export default connect(
mapStateToProps,
)(Console);

@ -3,23 +3,23 @@ import React from 'react';
interface Props { interface Props {
mode: string; mode: string;
value: string; value: string;
onBlur: (e: React.FocusEvent<Element>) => void; onBlur: (e: React.FocusEvent<HTMLInputElement>) => void;
onKeyDown: (e: React.KeyboardEvent<Element>) => void; onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;
onChange: (e: React.ChangeEvent<Element>) => void; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
} }
class Input extends React.Component<Props> { class Input extends React.Component<Props> {
private input: HTMLInputElement | null; private input: React.RefObject<HTMLInputElement>;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.input = null; this.input = React.createRef();
} }
focus() { focus() {
if (this.input) { if (this.input.current) {
this.input.focus(); this.input.current.focus();
} }
} }
@ -38,7 +38,7 @@ class Input extends React.Component<Props> {
</i> </i>
<input <input
className='vimvixen-console-command-input' className='vimvixen-console-command-input'
ref={(c) => { this.input = c; }} ref={this.input}
onBlur={this.props.onBlur} onBlur={this.props.onBlur}
onKeyDown={this.props.onKeyDown} onKeyDown={this.props.onKeyDown}
onChange={this.props.onChange} onChange={this.props.onChange}

@ -2,7 +2,7 @@ import React from 'react';
interface Props { interface Props {
mode: string; mode: string;
children: string[]; children: string;
} }
const Message = (props: Props) => { const Message = (props: Props) => {

@ -1,6 +1,6 @@
import * as actions from '../actions'; import * as actions from '../actions';
interface State { export interface State {
mode: string; mode: string;
messageText: string; messageText: string;
consoleText: string; consoleText: string;

@ -0,0 +1,6 @@
export default interface Mark {
x: number;
y: number;
// eslint-disable-next-line semi
}

@ -9,6 +9,20 @@ import * as messages from '../../shared/messages';
import * as actions from './index'; import * as actions from './index';
import * as consoleFrames from '../console-frames'; 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 => { const find = (str: string, backwards: boolean): boolean => {
let caseSensitive = false; let caseSensitive = false;
let wrapScan = true; let wrapScan = true;
@ -18,7 +32,7 @@ const find = (str: string, backwards: boolean): boolean => {
// because of same origin policy // because of same origin policy
// eslint-disable-next-line no-extra-parens // eslint-disable-next-line no-extra-parens
let found = (<any>window).find(str, caseSensitive, backwards, wrapScan); let found = window.find(str, caseSensitive, backwards, wrapScan);
if (found) { if (found) {
return found; return found;
} }
@ -28,7 +42,7 @@ const find = (str: string, backwards: boolean): boolean => {
} }
// eslint-disable-next-line no-extra-parens // eslint-disable-next-line no-extra-parens
return (<any>window).find(str, caseSensitive, backwards, wrapScan); return window.find(str, caseSensitive, backwards, wrapScan);
}; };
// eslint-disable-next-line max-statements // eslint-disable-next-line max-statements

@ -1,5 +1,6 @@
import Redux from 'redux'; import Redux from 'redux';
import Settings from '../../shared/Settings'; import Settings from '../../shared/Settings';
import * as keyUtils from '../../shared/utils/keys';
// Enable/disable // Enable/disable
export const ADDON_SET_ENABLED = 'addon.set.enabled'; export const ADDON_SET_ENABLED = 'addon.set.enabled';
@ -51,7 +52,7 @@ export interface SettingSetAction extends Redux.Action {
export interface InputKeyPressAction extends Redux.Action { export interface InputKeyPressAction extends Redux.Action {
type: typeof INPUT_KEY_PRESS; type: typeof INPUT_KEY_PRESS;
key: string; key: keyUtils.Key;
} }
export interface InputClearKeysAction extends Redux.Action { export interface InputClearKeysAction extends Redux.Action {

@ -1,6 +1,7 @@
import * as actions from './index'; import * as actions from './index';
import * as keyUtils from '../../shared/utils/keys';
const keyPress = (key: string): actions.InputAction => { const keyPress = (key: keyUtils.Key): actions.InputAction => {
return { return {
type: actions.INPUT_KEY_PRESS, type: actions.INPUT_KEY_PRESS,
key, key,

@ -18,7 +18,7 @@ export default class Common {
constructor(win: Window, store: any) { constructor(win: Window, store: any) {
const input = new InputComponent(win.document.body); const input = new InputComponent(win.document.body);
const follow = new FollowComponent(win); const follow = new FollowComponent(win);
const mark = new MarkComponent(win.document.body, store); const mark = new MarkComponent(store);
const keymapper = new KeymapperComponent(store); const keymapper = new KeymapperComponent(store);
input.onKey((key: keys.Key) => follow.key(key)); input.onKey((key: keys.Key) => follow.key(key));

@ -1,27 +1,30 @@
import * as markActions from '../../actions/mark'; import * as markActions from '../../actions/mark';
import * as scrolls from '../..//scrolls'; import * as scrolls from '../..//scrolls';
import * as consoleFrames from '../..//console-frames'; import * as consoleFrames from '../..//console-frames';
import * as keyUtils from '../../../shared/utils/keys';
import Mark from '../../Mark';
const cancelKey = (key): boolean => { const cancelKey = (key: keyUtils.Key): boolean => {
return key.key === 'Esc' || key.key === '[' && key.ctrlKey; return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey);
}; };
const globalKey = (key) => { const globalKey = (key: string): boolean => {
return (/^[A-Z0-9]$/).test(key); return (/^[A-Z0-9]$/).test(key);
}; };
export default class MarkComponent { export default class MarkComponent {
constructor(body, store) { private store: any;
this.body = body;
constructor(store: any) {
this.store = store; this.store = store;
} }
// eslint-disable-next-line max-statements // eslint-disable-next-line max-statements
key(key) { key(key: keyUtils.Key) {
let { mark: markStage, setting } = this.store.getState(); let { mark: markState, setting } = this.store.getState();
let smoothscroll = setting.properties.smoothscroll; let smoothscroll = setting.properties.smoothscroll;
if (!markStage.setMode && !markStage.jumpMode) { if (!markState.setMode && !markState.jumpMode) {
return false; return false;
} }
@ -32,26 +35,30 @@ export default class MarkComponent {
if (key.ctrlKey || key.metaKey || key.altKey) { if (key.ctrlKey || key.metaKey || key.altKey) {
consoleFrames.postError('Unknown mark'); consoleFrames.postError('Unknown mark');
} else if (globalKey(key.key) && markStage.setMode) { } else if (globalKey(key.key) && markState.setMode) {
this.doSetGlobal(key); this.doSetGlobal(key);
} else if (globalKey(key.key) && markStage.jumpMode) { } else if (globalKey(key.key) && markState.jumpMode) {
this.doJumpGlobal(key); this.doJumpGlobal(key);
} else if (markStage.setMode) { } else if (markState.setMode) {
this.doSet(key); this.doSet(key);
} else if (markStage.jumpMode) { } else if (markState.jumpMode) {
this.doJump(markStage.marks, key, smoothscroll); this.doJump(markState.marks, key, smoothscroll);
} }
this.store.dispatch(markActions.cancel()); this.store.dispatch(markActions.cancel());
return true; return true;
} }
doSet(key) { doSet(key: keyUtils.Key) {
let { x, y } = scrolls.getScroll(); let { x, y } = scrolls.getScroll();
this.store.dispatch(markActions.setLocal(key.key, x, y)); this.store.dispatch(markActions.setLocal(key.key, x, y));
} }
doJump(marks, key, smoothscroll) { doJump(
marks: { [key: string]: Mark },
key: keyUtils.Key,
smoothscroll: boolean,
) {
if (!marks[key.key]) { if (!marks[key.key]) {
consoleFrames.postError('Mark is not set'); consoleFrames.postError('Mark is not set');
return; return;
@ -61,12 +68,12 @@ export default class MarkComponent {
scrolls.scrollTo(x, y, smoothscroll); scrolls.scrollTo(x, y, smoothscroll);
} }
doSetGlobal(key) { doSetGlobal(key: keyUtils.Key) {
let { x, y } = scrolls.getScroll(); let { x, y } = scrolls.getScroll();
this.store.dispatch(markActions.setGlobal(key.key, x, y)); this.store.dispatch(markActions.setGlobal(key.key, x, y));
} }
doJumpGlobal(key) { doJumpGlobal(key: keyUtils.Key) {
this.store.dispatch(markActions.jumpGlobal(key.key)); this.store.dispatch(markActions.jumpGlobal(key.key));
} }
} }

@ -12,5 +12,5 @@ if (window.self === window.top) {
} }
let style = window.document.createElement('style'); let style = window.document.createElement('style');
style.textContent = consoleFrameStyle.default; style.textContent = consoleFrameStyle;
window.document.head.appendChild(style); window.document.head.appendChild(style);

@ -1,7 +1,8 @@
import * as actions from '../actions'; import * as actions from '../actions';
import * as keyUtils from '../../shared/utils/keys';
export interface State { export interface State {
keys: string[]; keys: keyUtils.Key[],
} }
const defaultState: State = { const defaultState: State = {

@ -1,10 +1,6 @@
import Mark from '../Mark';
import * as actions from '../actions'; import * as actions from '../actions';
interface Mark {
x: number;
y: number;
}
export interface State { export interface State {
setMode: boolean; setMode: boolean;
jumpMode: boolean; jumpMode: boolean;

@ -1,4 +1,4 @@
exports.default = ` export default `
.vimvixen-console-frame { .vimvixen-console-frame {
margin: 0; margin: 0;
padding: 0; padding: 0;

@ -20,7 +20,6 @@ describe("content setting reducer", () => {
} }
} }
let state = settingReducer(undefined, action); let state = settingReducer(undefined, action);
console.log(JSON.stringify(state.keymaps));
expect(state.keymaps).to.have.deep.all.members([ expect(state.keymaps).to.have.deep.all.members([
{ key: [{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }, { key: [{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
{ key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }], { key: 'z', shiftKey: false, ctrlKey: false, altKey: false, metaKey: false }],