From 6829e24c62c0291336502b3390905b57b81abd21 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Thu, 26 Mar 2020 07:17:55 +0900 Subject: Use new completion use-case on current use-case (aliased) --- src/background/completion/BookmarkRepository.ts | 32 +++++++++ src/background/completion/CompletionUseCase.ts | 39 +++++++++++ src/background/completion/HistoryRepository.ts | 32 +++++++++ src/background/completion/filters.ts | 76 ++++++++++++++++++++++ .../repositories/CompletionsRepository.ts | 24 ------- src/background/usecases/CompletionsUseCase.ts | 55 ++++++---------- src/background/usecases/filters.ts | 76 ---------------------- 7 files changed, 199 insertions(+), 135 deletions(-) create mode 100644 src/background/completion/BookmarkRepository.ts create mode 100644 src/background/completion/CompletionUseCase.ts create mode 100644 src/background/completion/HistoryRepository.ts create mode 100644 src/background/completion/filters.ts delete mode 100644 src/background/usecases/filters.ts (limited to 'src/background') diff --git a/src/background/completion/BookmarkRepository.ts b/src/background/completion/BookmarkRepository.ts new file mode 100644 index 0000000..12f5455 --- /dev/null +++ b/src/background/completion/BookmarkRepository.ts @@ -0,0 +1,32 @@ +import { injectable } from "tsyringe"; + +export type BookmarkItem = { + title: string + url: string +} + +const COMPLETION_ITEM_LIMIT = 10; + +@injectable() +export default class BookmarkRepository { + async queryBookmarks(query: string): Promise { + const items = await browser.bookmarks.search({ query }); + return items + .filter(item => item.title && item.title.length > 0) + .filter(item => item.type === 'bookmark' && item.url) + .filter((item) => { + let url = undefined; + try { + url = new URL(item.url!!); + } catch (e) { + return false; + } + return url.protocol !== 'place:'; + }) + .slice(0, COMPLETION_ITEM_LIMIT) + .map(item => ({ + title: item.title!!, + url: item.url!!, + })); + } +} diff --git a/src/background/completion/CompletionUseCase.ts b/src/background/completion/CompletionUseCase.ts new file mode 100644 index 0000000..898efd4 --- /dev/null +++ b/src/background/completion/CompletionUseCase.ts @@ -0,0 +1,39 @@ +import { inject, injectable } from "tsyringe"; +import HistoryRepository from "./HistoryRepository"; +import BookmarkRepository from "./BookmarkRepository"; +import CachedSettingRepository from "../repositories/CachedSettingRepository"; + +export type BookmarkItem = { + title: string + url: string +} + +export type HistoryItem = { + title: string + url: string +} + +@injectable() +export default class CompletionUseCase { + constructor( + private bookmarkRepository: BookmarkRepository, + private historyRepository: HistoryRepository, + @inject("CachedSettingRepository") + private cachedSettingRepository: CachedSettingRepository, + ) { + } + + async requestSearchEngines(query: string): Promise { + const settings = await this.cachedSettingRepository.get(); + return Object.keys(settings.search.engines) + .filter(key => key.startsWith(query)) + } + + requestBookmarks(query: any): Promise { + return this.bookmarkRepository.queryBookmarks(query); + } + + async requestHistory(query: string): Promise { + return this.historyRepository.queryHistories(query); + } +} \ No newline at end of file diff --git a/src/background/completion/HistoryRepository.ts b/src/background/completion/HistoryRepository.ts new file mode 100644 index 0000000..1cfaf1b --- /dev/null +++ b/src/background/completion/HistoryRepository.ts @@ -0,0 +1,32 @@ +import * as filters from "./filters"; +import { injectable } from "tsyringe"; + +export type HistoryItem = { + title: string + url: string +} + +const COMPLETION_ITEM_LIMIT = 10; + +@injectable() +export default class HistoryRepository { + async queryHistories(keywords: string): Promise { + const items = await browser.history.search({ + text: keywords, + startTime: 0, + }); + + return [items] + .map(filters.filterBlankTitle) + .map(filters.filterHttp) + .map(filters.filterByTailingSlash) + .map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT)) + .map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0] + .sort((x, y) => Number(y.visitCount) - Number(x.visitCount)) + .slice(0, COMPLETION_ITEM_LIMIT) + .map(item => ({ + title: item.title!!, + url: item.url!!, + })) + } +} diff --git a/src/background/completion/filters.ts b/src/background/completion/filters.ts new file mode 100644 index 0000000..98957a7 --- /dev/null +++ b/src/background/completion/filters.ts @@ -0,0 +1,76 @@ +type Item = browser.history.HistoryItem; + +const filterHttp = (items: Item[]): Item[] => { + const httpsHosts = items.map(x => new URL(x.url as string)) + .filter(x => x.protocol === 'https:') + .map(x => x.host); + const hostsSet = new Set(httpsHosts); + + return items.filter((item: Item) => { + const url = new URL(item.url as string); + return url.protocol === 'https:' || !hostsSet.has(url.host); + }); +}; + +const filterBlankTitle = (items: Item[]): Item[] => { + return items.filter(item => item.title && item.title !== ''); +}; + +const filterByTailingSlash = (items: Item[]): Item[] => { + const urls = items.map(item => new URL(item.url as string)); + const simplePaths = urls + .filter(url => url.hash === '' && url.search === '') + .map(url => url.origin + url.pathname); + const pathsSet = new Set(simplePaths); + + return items.filter((item) => { + const url = new URL(item.url as string); + if (url.hash !== '' || url.search !== '' || + url.pathname.slice(-1) !== '/') { + return true; + } + return !pathsSet.has(url.origin + url.pathname.slice(0, -1)); + }); +}; + +const filterByPathname = (items: Item[], min: number): Item[] => { + const hash: {[key: string]: Item} = {}; + for (const item of items) { + const url = new URL(item.url as string); + const pathname = url.origin + url.pathname; + if (!hash[pathname]) { + hash[pathname] = item; + } else if ((hash[pathname].url as string).length > + (item.url as string).length) { + hash[pathname] = item; + } + } + const filtered = Object.values(hash); + if (filtered.length < min) { + return items; + } + return filtered; +}; + +const filterByOrigin = (items: Item[], min: number): Item[] => { + const hash: {[key: string]: Item} = {}; + for (const item of items) { + const origin = new URL(item.url as string).origin; + if (!hash[origin]) { + hash[origin] = item; + } else if ((hash[origin].url as string).length > + (item.url as string).length) { + hash[origin] = item; + } + } + const filtered = Object.values(hash); + if (filtered.length < min) { + return items; + } + return filtered; +}; + +export { + filterHttp, filterBlankTitle, filterByTailingSlash, + filterByPathname, filterByOrigin +}; diff --git a/src/background/repositories/CompletionsRepository.ts b/src/background/repositories/CompletionsRepository.ts index dfecff0..7d0434c 100644 --- a/src/background/repositories/CompletionsRepository.ts +++ b/src/background/repositories/CompletionsRepository.ts @@ -1,33 +1,9 @@ import { injectable } from 'tsyringe'; type Tab = browser.tabs.Tab; -type BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; @injectable() export default class CompletionsRepository { - async queryBookmarks(keywords: string): Promise { - const items = await browser.bookmarks.search({ query: keywords }); - return items.filter((item) => { - if (!item.url) { - return false; - } - let url = undefined; - try { - url = new URL(item.url); - } catch (e) { - return false; - } - return item.type === 'bookmark' && url.protocol !== 'place:'; - }); - } - - queryHistories(keywords: string): Promise { - return browser.history.search({ - text: keywords, - startTime: 0, - }); - } - async queryTabs(keywords: string, excludePinned: boolean): Promise { const tabs = await browser.tabs.query({ currentWindow: true }); return tabs.filter((t) => { diff --git a/src/background/usecases/CompletionsUseCase.ts b/src/background/usecases/CompletionsUseCase.ts index 9874644..b75b635 100644 --- a/src/background/usecases/CompletionsUseCase.ts +++ b/src/background/usecases/CompletionsUseCase.ts @@ -2,15 +2,12 @@ import { injectable, inject } from 'tsyringe'; import CompletionGroup from '../domains/CompletionGroup'; import CommandDocs from '../domains/CommandDocs'; import CompletionsRepository from '../repositories/CompletionsRepository'; -import * as filters from './filters'; import CachedSettingRepository from '../repositories/CachedSettingRepository'; import TabPresenter from '../presenters/TabPresenter'; import Properties from '../../shared/settings/Properties'; - -const COMPLETION_ITEM_LIMIT = 10; +import CompletionUseCase from "../completion/CompletionUseCase"; type Tab = browser.tabs.Tab; -type HistoryItem = browser.history.HistoryItem; @injectable() export default class CompletionsUseCase { @@ -18,6 +15,7 @@ export default class CompletionsUseCase { private tabPresenter: TabPresenter, private completionsRepository: CompletionsRepository, @inject("CachedSettingRepository") private cachedSettingRepository: CachedSettingRepository, + private completionUseCase: CompletionUseCase ) { } @@ -179,42 +177,29 @@ export default class CompletionsUseCase { return [{ name: 'Buffers', items }]; } - async querySearchEngineItems(name: string, keywords: string) { - const settings = await this.cachedSettingRepository.get(); - const engines = Object.keys(settings.search.engines) - .filter(key => key.startsWith(keywords)); - return engines.map(key => ({ - caption: key, - content: name + ' ' + key, + async querySearchEngineItems(name: string, query: string) { + const engines = await this.completionUseCase.requestSearchEngines(query); + return engines.map(item => ({ + caption: item, + content: name + ' ' + item, })); } - async queryHistoryItems(name: string, keywords: string) { - let histories = await this.completionsRepository.queryHistories(keywords); - histories = [histories] - .map(filters.filterBlankTitle) - .map(filters.filterHttp) - .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): number => { - return Number(y.visitCount) - Number(x.visitCount); - }) - .slice(0, COMPLETION_ITEM_LIMIT); - return histories.map(page => ({ - caption: page.title, - content: name + ' ' + page.url, - url: page.url + async queryHistoryItems(name: string, query: string) { + const items = await this.completionUseCase.requestHistory(query); + return items.map(item => ({ + caption: item.title, + content: name + ' ' + item.url, + url: item.url })); } - async queryBookmarkItems(name: string, keywords: string) { - const bookmarks = await this.completionsRepository.queryBookmarks(keywords); - return bookmarks.slice(0, COMPLETION_ITEM_LIMIT) - .map(page => ({ - caption: page.title, - content: name + ' ' + page.url, - url: page.url - })); + async queryBookmarkItems(name: string, query: string) { + const items = await this.completionUseCase.requestHistory(query); + return items.map(item => ({ + caption: item.title, + content: name + ' ' + item.url, + url: item.url + })); } } diff --git a/src/background/usecases/filters.ts b/src/background/usecases/filters.ts deleted file mode 100644 index 98957a7..0000000 --- a/src/background/usecases/filters.ts +++ /dev/null @@ -1,76 +0,0 @@ -type Item = browser.history.HistoryItem; - -const filterHttp = (items: Item[]): Item[] => { - const httpsHosts = items.map(x => new URL(x.url as string)) - .filter(x => x.protocol === 'https:') - .map(x => x.host); - const hostsSet = new Set(httpsHosts); - - return items.filter((item: Item) => { - const url = new URL(item.url as string); - return url.protocol === 'https:' || !hostsSet.has(url.host); - }); -}; - -const filterBlankTitle = (items: Item[]): Item[] => { - return items.filter(item => item.title && item.title !== ''); -}; - -const filterByTailingSlash = (items: Item[]): Item[] => { - const urls = items.map(item => new URL(item.url as string)); - const simplePaths = urls - .filter(url => url.hash === '' && url.search === '') - .map(url => url.origin + url.pathname); - const pathsSet = new Set(simplePaths); - - return items.filter((item) => { - const url = new URL(item.url as string); - if (url.hash !== '' || url.search !== '' || - url.pathname.slice(-1) !== '/') { - return true; - } - return !pathsSet.has(url.origin + url.pathname.slice(0, -1)); - }); -}; - -const filterByPathname = (items: Item[], min: number): Item[] => { - const hash: {[key: string]: Item} = {}; - for (const item of items) { - const url = new URL(item.url as string); - const pathname = url.origin + url.pathname; - if (!hash[pathname]) { - hash[pathname] = item; - } else if ((hash[pathname].url as string).length > - (item.url as string).length) { - hash[pathname] = item; - } - } - const filtered = Object.values(hash); - if (filtered.length < min) { - return items; - } - return filtered; -}; - -const filterByOrigin = (items: Item[], min: number): Item[] => { - const hash: {[key: string]: Item} = {}; - for (const item of items) { - const origin = new URL(item.url as string).origin; - if (!hash[origin]) { - hash[origin] = item; - } else if ((hash[origin].url as string).length > - (item.url as string).length) { - hash[origin] = item; - } - } - const filtered = Object.values(hash); - if (filtered.length < min) { - return items; - } - return filtered; -}; - -export { - filterHttp, filterBlankTitle, filterByTailingSlash, - filterByPathname, filterByOrigin -}; -- cgit v1.2.3