Declare setting types
This commit is contained in:
parent
d01db82c0d
commit
a0882bbceb
48 changed files with 1618 additions and 903 deletions
|
@ -1,5 +1,6 @@
|
|||
import SettingUseCase from '../usecases/SettingUseCase';
|
||||
import ContentMessageClient from '../infrastructures/ContentMessageClient';
|
||||
import Settings from '../../shared/Settings';
|
||||
|
||||
export default class SettingController {
|
||||
private settingUseCase: SettingUseCase;
|
||||
|
@ -11,7 +12,7 @@ export default class SettingController {
|
|||
this.contentMessageClient = new ContentMessageClient();
|
||||
}
|
||||
|
||||
getSetting(): any {
|
||||
getSetting(): Promise<Settings> {
|
||||
return this.settingUseCase.get();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export interface GlobalMark {
|
||||
export default interface GlobalMark {
|
||||
readonly tabId: number;
|
||||
readonly url: string;
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
// eslint-disable-next-line semi
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
import DefaultSettings from '../../shared/settings/default';
|
||||
import * as settingsValues from '../../shared/settings/values';
|
||||
|
||||
type SettingValue = {
|
||||
source: string,
|
||||
json: string,
|
||||
form: any
|
||||
}
|
||||
|
||||
export default class Setting {
|
||||
private obj: SettingValue;
|
||||
|
||||
constructor({ source, json, form }: SettingValue) {
|
||||
this.obj = {
|
||||
source, json, form
|
||||
};
|
||||
}
|
||||
|
||||
get source(): string {
|
||||
return this.obj.source;
|
||||
}
|
||||
|
||||
get json(): string {
|
||||
return this.obj.json;
|
||||
}
|
||||
|
||||
get form(): any {
|
||||
return this.obj.form;
|
||||
}
|
||||
|
||||
value() {
|
||||
let value = JSON.parse(DefaultSettings.json);
|
||||
if (this.obj.source === 'json') {
|
||||
value = settingsValues.valueFromJson(this.obj.json);
|
||||
} else if (this.obj.source === 'form') {
|
||||
value = settingsValues.valueFromForm(this.obj.form);
|
||||
}
|
||||
if (!value.properties) {
|
||||
value.properties = {};
|
||||
}
|
||||
return { ...settingsValues.valueFromJson(DefaultSettings.json), ...value };
|
||||
}
|
||||
|
||||
serialize(): SettingValue {
|
||||
return this.obj;
|
||||
}
|
||||
|
||||
static deserialize(obj: SettingValue): Setting {
|
||||
return new Setting({ source: obj.source, json: obj.json, form: obj.form });
|
||||
}
|
||||
|
||||
static defaultSettings() {
|
||||
return new Setting({
|
||||
source: DefaultSettings.source,
|
||||
json: DefaultSettings.json,
|
||||
form: {},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import Setting from '../domains/Setting';
|
||||
import SettingData from '../../shared/SettingData';
|
||||
|
||||
export default class SettingRepository {
|
||||
async load(): Promise<any> {
|
||||
async load(): Promise<SettingData | null> {
|
||||
let { settings } = await browser.storage.local.get('settings');
|
||||
if (!settings) {
|
||||
return null;
|
||||
}
|
||||
return Setting.deserialize(settings);
|
||||
return SettingData.valueOf(settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import MemoryStorage from '../infrastructures/MemoryStorage';
|
||||
import Settings from '../../shared/Settings';
|
||||
import * as PropertyDefs from '../../shared/property-defs';
|
||||
|
||||
const CACHED_SETTING_KEY = 'setting';
|
||||
|
||||
|
@ -9,17 +11,41 @@ export default class SettingRepository {
|
|||
this.cache = new MemoryStorage();
|
||||
}
|
||||
|
||||
get(): Promise<any> {
|
||||
get(): Promise<Settings> {
|
||||
return Promise.resolve(this.cache.get(CACHED_SETTING_KEY));
|
||||
}
|
||||
|
||||
update(value: any): any {
|
||||
update(value: Settings): void {
|
||||
return this.cache.set(CACHED_SETTING_KEY, value);
|
||||
}
|
||||
|
||||
async setProperty(name: string, value: string): Promise<any> {
|
||||
async setProperty(
|
||||
name: string, value: string | number | boolean,
|
||||
): Promise<void> {
|
||||
let def = PropertyDefs.defs.find(d => name === d.name);
|
||||
if (!def) {
|
||||
throw new Error('unknown property: ' + name);
|
||||
}
|
||||
if (typeof value !== def.type) {
|
||||
throw new TypeError(`property type of ${name} mismatch: ${typeof value}`);
|
||||
}
|
||||
let newValue = value;
|
||||
if (typeof value === 'string' && value === '') {
|
||||
newValue = def.defaultValue;
|
||||
}
|
||||
|
||||
let current = await this.get();
|
||||
current.properties[name] = value;
|
||||
switch (name) {
|
||||
case 'hintchars':
|
||||
current.properties.hintchars = newValue as string;
|
||||
break;
|
||||
case 'smoothscroll':
|
||||
current.properties.smoothscroll = newValue as boolean;
|
||||
break;
|
||||
case 'complete':
|
||||
current.properties.complete = newValue as string;
|
||||
break;
|
||||
}
|
||||
return this.update(current);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import SettingRepository from '../repositories/SettingRepository';
|
|||
import BookmarkRepository from '../repositories/BookmarkRepository';
|
||||
import ConsoleClient from '../infrastructures/ConsoleClient';
|
||||
import ContentMessageClient from '../infrastructures/ContentMessageClient';
|
||||
import * as properties from '../../shared/settings/properties';
|
||||
|
||||
export default class CommandIndicator {
|
||||
private tabPresenter: TabPresenter;
|
||||
|
@ -115,16 +114,16 @@ export default class CommandIndicator {
|
|||
|
||||
async addbookmark(title: string): Promise<any> {
|
||||
let tab = await this.tabPresenter.getCurrent();
|
||||
let item = await this.bookmarkRepository.create(title, tab.url);
|
||||
let item = await this.bookmarkRepository.create(title, tab.url as string);
|
||||
let message = 'Saved current page: ' + item.url;
|
||||
return this.consoleClient.showInfo(tab.id, message);
|
||||
return this.consoleClient.showInfo(tab.id as number, message);
|
||||
}
|
||||
|
||||
async set(keywords: string): Promise<any> {
|
||||
if (keywords.length === 0) {
|
||||
return;
|
||||
}
|
||||
let [name, value] = parsers.parseSetOption(keywords, properties.types);
|
||||
let [name, value] = parsers.parseSetOption(keywords);
|
||||
await this.settingRepository.setProperty(name, value);
|
||||
|
||||
return this.contentMessageClient.broadcastSettingsChanged();
|
||||
|
|
|
@ -4,7 +4,7 @@ import CompletionsRepository from '../repositories/CompletionsRepository';
|
|||
import * as filters from './filters';
|
||||
import SettingRepository from '../repositories/SettingRepository';
|
||||
import TabPresenter from '../presenters/TabPresenter';
|
||||
import * as properties from '../../shared/settings/properties';
|
||||
import * as PropertyDefs from '../../shared/property-defs';
|
||||
|
||||
const COMPLETION_ITEM_LIMIT = 10;
|
||||
|
||||
|
@ -44,7 +44,7 @@ export default class CompletionsUseCase {
|
|||
let settings = await this.settingRepository.get();
|
||||
let groups: CompletionGroup[] = [];
|
||||
|
||||
let complete = settings.properties.complete || properties.defaults.complete;
|
||||
let complete = settings.properties.complete;
|
||||
for (let c of complete) {
|
||||
if (c === 's') {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
|
@ -127,25 +127,25 @@ export default class CompletionsUseCase {
|
|||
}
|
||||
|
||||
querySet(name: string, keywords: string): Promise<CompletionGroup[]> {
|
||||
let items = Object.keys(properties.docs).map((key) => {
|
||||
if (properties.types[key] === 'boolean') {
|
||||
let items = PropertyDefs.defs.map((def) => {
|
||||
if (def.type === 'boolean') {
|
||||
return [
|
||||
{
|
||||
caption: key,
|
||||
content: name + ' ' + key,
|
||||
url: 'Enable ' + properties.docs[key],
|
||||
caption: def.name,
|
||||
content: name + ' ' + def.name,
|
||||
url: 'Enable ' + def.description,
|
||||
}, {
|
||||
caption: 'no' + key,
|
||||
content: name + ' no' + key,
|
||||
url: 'Disable ' + properties.docs[key],
|
||||
caption: 'no' + def.name,
|
||||
content: name + ' no' + def.name,
|
||||
url: 'Disable ' + def.description
|
||||
}
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
caption: key,
|
||||
content: name + ' ' + key,
|
||||
url: 'Set ' + properties.docs[key],
|
||||
caption: def.name,
|
||||
content: name + ' ' + def.name,
|
||||
url: 'Set ' + def.description,
|
||||
}
|
||||
];
|
||||
});
|
||||
|
@ -195,8 +195,8 @@ export default class CompletionsUseCase {
|
|||
.map(filters.filterByTailingSlash)
|
||||
.map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
|
||||
.map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0]
|
||||
.sort((x: HistoryItem, y: HistoryItem) => {
|
||||
return Number(x.visitCount) < Number(y.visitCount);
|
||||
.sort((x: HistoryItem, y: HistoryItem): number => {
|
||||
return Number(x.visitCount) - Number(y.visitCount);
|
||||
})
|
||||
.slice(0, COMPLETION_ITEM_LIMIT);
|
||||
return histories.map(page => ({
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import Setting from '../domains/Setting';
|
||||
// eslint-disable-next-line max-len
|
||||
import PersistentSettingRepository from '../repositories/PersistentSettingRepository';
|
||||
import SettingRepository from '../repositories/SettingRepository';
|
||||
import { DefaultSettingData } from '../../shared/SettingData';
|
||||
import Settings from '../../shared/Settings';
|
||||
|
||||
export default class SettingUseCase {
|
||||
private persistentSettingRepository: PersistentSettingRepository;
|
||||
|
@ -13,20 +14,18 @@ export default class SettingUseCase {
|
|||
this.settingRepository = new SettingRepository();
|
||||
}
|
||||
|
||||
get(): Promise<any> {
|
||||
get(): Promise<Settings> {
|
||||
return this.settingRepository.get();
|
||||
}
|
||||
|
||||
async reload(): Promise<any> {
|
||||
let settings = await this.persistentSettingRepository.load();
|
||||
if (!settings) {
|
||||
settings = Setting.defaultSettings();
|
||||
async reload(): Promise<Settings> {
|
||||
let data = await this.persistentSettingRepository.load();
|
||||
if (!data) {
|
||||
data = DefaultSettingData;
|
||||
}
|
||||
|
||||
let value = settings.value();
|
||||
|
||||
let value = data.toSettings();
|
||||
this.settingRepository.update(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import * as PropertyDefs from '../../shared//property-defs';
|
||||
|
||||
const mustNumber = (v: any): number => {
|
||||
let num = Number(v);
|
||||
if (isNaN(num)) {
|
||||
|
@ -7,29 +9,28 @@ const mustNumber = (v: any): number => {
|
|||
};
|
||||
|
||||
const parseSetOption = (
|
||||
word: string,
|
||||
types: { [key: string]: string },
|
||||
args: string,
|
||||
): any[] => {
|
||||
let [key, value]: any[] = word.split('=');
|
||||
let [key, value]: any[] = args.split('=');
|
||||
if (value === undefined) {
|
||||
value = !key.startsWith('no');
|
||||
key = value ? key : key.slice(2);
|
||||
}
|
||||
let type = types[key];
|
||||
if (!type) {
|
||||
let def = PropertyDefs.defs.find(d => d.name === key);
|
||||
if (!def) {
|
||||
throw new Error('Unknown property: ' + key);
|
||||
}
|
||||
if (type === 'boolean' && typeof value !== 'boolean' ||
|
||||
type !== 'boolean' && typeof value === 'boolean') {
|
||||
throw new Error('Invalid argument: ' + word);
|
||||
if (def.type === 'boolean' && typeof value !== 'boolean' ||
|
||||
def.type !== 'boolean' && typeof value === 'boolean') {
|
||||
throw new Error('Invalid argument: ' + args);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
switch (def.type) {
|
||||
case 'string': return [key, value];
|
||||
case 'number': return [key, mustNumber(value)];
|
||||
case 'boolean': return [key, value];
|
||||
}
|
||||
throw new Error('Unknown property type: ' + type);
|
||||
throw new Error('Unknown property type: ' + def.type);
|
||||
};
|
||||
|
||||
export { parseSetOption };
|
||||
|
|
Reference in a new issue