From 7294e0fc151cf821a2c8c0055574bd505305b2f2 Mon Sep 17 00:00:00 2001 From: sheepzh Date: Sat, 15 Oct 2022 21:44:49 +0800 Subject: [PATCH 1/9] Support to build for Safari (#148) --- .gitignore | 3 ++ package.json | 1 + .../option/components/statistics.ts | 50 ++++++++++++------- src/background/timer/idle-listener.ts | 6 ++- src/manifest.ts | 6 +-- src/util/constant/environment.ts | 10 +++- webpack/webpack.dev.safari.ts | 33 ++++++++++++ webpack/webpack.dev.ts | 2 +- 8 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 webpack/webpack.dev.safari.ts diff --git a/.gitignore b/.gitignore index 5845df593..461018b64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ node_modules dist dist_dev +dist_dev_safari dist_prod dist_analyze +Timer_Safari_DEV + firefox_dev market_packages diff --git a/package.json b/package.json index 00a03c57c..a7f7c9983 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "homepage": "https://github.com/sheepzh/timer", "scripts": { "dev": "webpack --config=webpack/webpack.dev.ts --watch", + "dev:safari": "webpack --config=webpack/webpack.dev.safari.ts --watch", "build": "webpack --config=webpack/webpack.prod.ts", "analyze": "webpack --config=webpack/webpack.analyze.ts", "test": "jest --env=jsdom", diff --git a/src/app/components/option/components/statistics.ts b/src/app/components/option/components/statistics.ts index e6172fd12..f9c901dce 100644 --- a/src/app/components/option/components/statistics.ts +++ b/src/app/components/option/components/statistics.ts @@ -13,6 +13,7 @@ import { defaultStatistics } from "@util/constant/option" import { defineComponent, h, reactive, unref } from "vue" import { t } from "@app/locale" import { renderOptionItem, tagText, tooltip } from "../common" +import { IS_SAFARI } from "@util/constant/environment" function updateOptionVal(key: keyof timer.option.StatisticsOption, newVal: boolean, option: UnwrapRef) { option[key] = newVal @@ -40,6 +41,35 @@ function copy(target: timer.option.StatisticsOption, source: timer.option.Statis target.countLocalFiles = source.countLocalFiles } +function renderOptionItems(option: timer.option.StatisticsOption) { + const result = [] + if (!IS_SAFARI) { + // chrome.idle does not work in Safari, so not to display this option + result.push( + renderOptionItem({ + input: countWhenIdle(option), + idleTime: tagText(msg => msg.option.statistics.idleTime), + info: tooltip(msg => msg.option.statistics.idleTimeInfo) + }, msg => msg.statistics.countWhenIdle, t(msg => msg.option.no)), + h(ElDivider) + ) + } + result.push( + renderOptionItem({ + input: countLocalFiles(option), + localFileTime: tagText(msg => msg.option.statistics.localFileTime), + info: tooltip(msg => msg.option.statistics.localFilesInfo) + }, msg => msg.statistics.countLocalFiles, t(msg => msg.option.no)), + h(ElDivider), + renderOptionItem({ + input: collectSiteName(option), + siteName: tagText(msg => msg.option.statistics.siteName), + siteNameUsage: tooltip(msg => msg.option.statistics.siteNameUsage) + }, msg => msg.statistics.collectSiteName, t(msg => msg.option.yes)) + ) + return result +} + const _default = defineComponent({ name: "StatisticsOptionContainer", setup(_props, ctx) { @@ -51,25 +81,7 @@ const _default = defineComponent({ await optionService.setStatisticsOption(unref(option)) } }) - return () => h('div', [ - renderOptionItem({ - input: countWhenIdle(option), - idleTime: tagText(msg => msg.option.statistics.idleTime), - info: tooltip(msg => msg.option.statistics.idleTimeInfo) - }, msg => msg.statistics.countWhenIdle, t(msg => msg.option.no)), - h(ElDivider), - renderOptionItem({ - input: countLocalFiles(option), - localFileTime: tagText(msg => msg.option.statistics.localFileTime), - info: tooltip(msg => msg.option.statistics.localFilesInfo) - }, msg => msg.statistics.countLocalFiles, t(msg => msg.option.no)), - h(ElDivider), - renderOptionItem({ - input: collectSiteName(option), - siteName: tagText(msg => msg.option.statistics.siteName), - siteNameUsage: tooltip(msg => msg.option.statistics.siteNameUsage) - }, msg => msg.statistics.collectSiteName, t(msg => msg.option.yes)) - ]) + return () => h('div', renderOptionItems(option)) } }) diff --git a/src/background/timer/idle-listener.ts b/src/background/timer/idle-listener.ts index ea051644c..0941cfc43 100644 --- a/src/background/timer/idle-listener.ts +++ b/src/background/timer/idle-listener.ts @@ -5,6 +5,7 @@ * https://opensource.org/licenses/MIT */ +import { IS_SAFARI } from "@util/constant/environment" import { formatTime } from "@util/time" import TimerContext from "./context" @@ -24,6 +25,9 @@ export default class IdleListener { } listen() { - chrome.idle.onStateChanged.addListener(newState => listen(this.context, newState)) + if (!IS_SAFARI) { + // Idle does not work in macOs + chrome.idle.onStateChanged.addListener(newState => listen(this.context, newState)) + } } } \ No newline at end of file diff --git a/src/manifest.ts b/src/manifest.ts index b89e86abb..c39e6fe8a 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -34,13 +34,13 @@ const _default: chrome.runtime.ManifestV2 = { }, content_scripts: [ { - "matches": [ + matches: [ "" ], - "js": [ + js: [ "content_scripts.js" ], - "run_at": "document_start" + run_at: "document_start" } ], permissions: [ diff --git a/src/util/constant/environment.ts b/src/util/constant/environment.ts index 6c4cc09a2..34488c8ce 100644 --- a/src/util/constant/environment.ts +++ b/src/util/constant/environment.ts @@ -10,6 +10,7 @@ let isFirefox = false let isChrome = false let isEdge = false let isOpera = false +let isSafari = false if (/Firefox[\/\s](\d+\.\d+)/.test(userAgent)) { isFirefox = true @@ -19,6 +20,8 @@ if (/Firefox[\/\s](\d+\.\d+)/.test(userAgent)) { } else if (userAgent.includes("Opera") || userAgent.includes("OPR")) { // The Opera implements the chrome isOpera = true +} else if (userAgent.includes('Safari')) { + isSafari = true } else if (userAgent.includes('Chrome')) { isChrome = true } @@ -32,4 +35,9 @@ export const IS_CHROME: boolean = isChrome /** * @since 0.8.0 */ -export const IS_OPERA: boolean = isOpera \ No newline at end of file +export const IS_OPERA: boolean = isOpera + +/** + * @since 1.3.0 + */ +export const IS_SAFARI: boolean = isSafari \ No newline at end of file diff --git a/webpack/webpack.dev.safari.ts b/webpack/webpack.dev.safari.ts new file mode 100644 index 000000000..6a6d43a0c --- /dev/null +++ b/webpack/webpack.dev.safari.ts @@ -0,0 +1,33 @@ +import path from "path" +import optionGenerator from "./webpack.common" + +const outputDir = path.join(__dirname, '..', 'dist_dev_safari') + +function removeUnsupportedProperties(manifest: Partial) { + // 1. permissions. 'idle' is not supported + const originPermissions = manifest.permissions || [] + const unsupported = ['idle'] + const supported = [] + originPermissions.forEach(perm => !unsupported.includes(perm) && supported.push(perm)) + manifest.permissions = supported +} + +const options = optionGenerator( + outputDir, + baseManifest => { + baseManifest.name = 'Timer_Safari_DEV' + // Remove unsupported properties in Safari + removeUnsupportedProperties(baseManifest) + } +) + +options.mode = 'development' +options.output.path = outputDir + +// no eval with development, but generate *.map.js +options.devtool = 'cheap-module-source-map' + +// Use cache with filesystem +options.cache = { type: 'filesystem' } + +export default options \ No newline at end of file diff --git a/webpack/webpack.dev.ts b/webpack/webpack.dev.ts index 94a17e55d..d5f2cc9b5 100644 --- a/webpack/webpack.dev.ts +++ b/webpack/webpack.dev.ts @@ -56,4 +56,4 @@ options.devtool = 'cheap-module-source-map' // Use cache with filesystem options.cache = { type: 'filesystem' } -module.exports = options \ No newline at end of file +export default options \ No newline at end of file From 6730f021b95845cfe1455ffe89f52a9e4a33d624 Mon Sep 17 00:00:00 2001 From: ZHY Date: Sat, 15 Oct 2022 22:02:59 +0800 Subject: [PATCH 2/9] Update for Safari --- doc/dev-guide.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/dev-guide.md b/doc/dev-guide.md index 447dc0472..67a577965 100644 --- a/doc/dev-guide.md +++ b/doc/dev-guide.md @@ -53,11 +53,25 @@ yarn run test ``` 7. 提交代码,并 PR 主仓库的 main 分支 -## 3. 应用架构设计 +## 3. 在 Safari 里运行 + +1. 重复上述步骤 1-4 +2. 编译兼容 Safari 的代码,替换上述步骤 5 的指令即可 +```shell +npm run dev:safari +``` +3. 使用 Xcode 内置工具 safari-web-extension-converter 将 Chrome 扩展转换成 Safari 扩展 +```shell +[YOUR_PATH]/Xcode.app/Contents/Developer/usr/bin/safari-web-extension-converter dist_dev_safari +``` +项目根目录下会生成一个文件夹 Timer_Safari_DEV,同时 Xcode 会自动打开该文件夹 +4. 在 Xcode 里运行打开的项目即可 + +## 4. 应用架构设计 > todo -## 4. 目录结构 +## 5. 目录结构 ```plain project @@ -117,6 +131,7 @@ project └───webpack # webpack 打包配置 | webpack.common.ts # 基础配置 | webpack.dev.ts # 开发环境配置 + | webpack.dev.safari.ts # Safari 开发环境配置 | webpack.prod.ts # 生产配置 ``` From 289c702e51592dbbb27317a7fcc544af640c2906 Mon Sep 17 00:00:00 2001 From: sheepzh Date: Mon, 17 Oct 2022 19:44:31 +0800 Subject: [PATCH 3/9] Fix some bugs in Safari (#152) --- src/background/badge-text-manager.ts | 17 ++++++++++++++-- src/background/browser-action-menu-manager.ts | 20 ++++++++++++++----- src/util/constant/environment.ts | 3 ++- src/util/pattern.ts | 1 + 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/background/badge-text-manager.ts b/src/background/badge-text-manager.ts index 4f14db2a5..800b3be84 100644 --- a/src/background/badge-text-manager.ts +++ b/src/background/badge-text-manager.ts @@ -50,16 +50,29 @@ async function updateFocus(host?: string) { if (!host) { host = await findActiveHost() } - const milliseconds = host ? (await timerDb.get(host, new Date)).focus : undefined + const milliseconds = host ? (await timerDb.get(host, new Date())).focus : undefined setBadgeText(milliseconds) } +const ALARM_NAME = 'timer-badge-text-manager-alarm' +const ALARM_INTERVAL = 1000 +function createAlarm(beforeAction?: () => void) { + beforeAction?.() + chrome.alarms.create(ALARM_NAME, { when: Date.now() + ALARM_INTERVAL }) +} + class BadgeTextManager { timer: NodeJS.Timer isPaused: boolean async init() { - this.timer = setInterval(() => !this.isPaused && updateFocus(), 1000) + createAlarm() + chrome.alarms.onAlarm.addListener(alarm => { + if (ALARM_NAME === alarm.name) { + createAlarm(() => !this.isPaused && updateFocus()) + } + }) + // this.timer = setInterval(() => !this.isPaused && updateFocus(), 1000) const option: Partial = await optionService.getAllOption() this.pauseOrResumeAccordingToOption(!!option.displayBadgeText) diff --git a/src/background/browser-action-menu-manager.ts b/src/background/browser-action-menu-manager.ts index e62a0809d..a9e062f7e 100644 --- a/src/background/browser-action-menu-manager.ts +++ b/src/background/browser-action-menu-manager.ts @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2021 Hengyang Zhang * * This software is released under the MIT License. @@ -8,6 +8,7 @@ import { OPTION_ROUTE } from "../app/router/constants" import { getAppPageUrl, SOURCE_CODE_PAGE, TU_CAO_PAGE } from "@util/constant/url" import { t2Chrome } from "@util/i18n/chrome/t" +import { IS_SAFARI } from "@util/constant/environment" const APP_PAGE_URL = getAppPageUrl(true) @@ -19,30 +20,39 @@ const baseProps: Partial = { visible: true } +function titleOf(prefixEmoji: string, title: string) { + if (IS_SAFARI) { + // Emoji does not work in Safari's context menu + return title + } else { + return `${prefixEmoji} ${title}` + } +} + const allFunctionProps: chrome.contextMenus.CreateProperties = { id: chrome.runtime.id + '_timer_menu_item_app_link', - title: '🏷️ ' + t2Chrome(msg => msg.contextMenus.allFunctions), + title: titleOf('🏷️', t2Chrome(msg => msg.contextMenus.allFunctions)), onclick: () => chrome.tabs.create({ url: APP_PAGE_URL }), ...baseProps } const optionPageProps: chrome.contextMenus.CreateProperties = { id: chrome.runtime.id + '_timer_menu_item_option_link', - title: '🥰 ' + t2Chrome(msg => msg.contextMenus.optionPage), + title: titleOf('🥰', t2Chrome(msg => msg.contextMenus.optionPage)), onclick: () => chrome.tabs.create({ url: APP_PAGE_URL + '#' + OPTION_ROUTE }), ...baseProps } const repoPageProps: chrome.contextMenus.CreateProperties = { id: chrome.runtime.id + '_timer_menu_item_repo_link', - title: '🍻 ' + t2Chrome(msg => msg.contextMenus.repoPage), + title: titleOf('🍻', t2Chrome(msg => msg.contextMenus.repoPage)), onclick: () => chrome.tabs.create({ url: SOURCE_CODE_PAGE }), ...baseProps } const feedbackPageProps: chrome.contextMenus.CreateProperties = { id: chrome.runtime.id + '_timer_menu_item_feedback_link', - title: '😿 ' + t2Chrome(msg => msg.contextMenus.feedbackPage), + title: titleOf('😿', t2Chrome(msg => msg.contextMenus.feedbackPage)), onclick: () => chrome.tabs.create({ url: TU_CAO_PAGE }), ...baseProps } diff --git a/src/util/constant/environment.ts b/src/util/constant/environment.ts index 34488c8ce..fd553ea1e 100644 --- a/src/util/constant/environment.ts +++ b/src/util/constant/environment.ts @@ -20,7 +20,8 @@ if (/Firefox[\/\s](\d+\.\d+)/.test(userAgent)) { } else if (userAgent.includes("Opera") || userAgent.includes("OPR")) { // The Opera implements the chrome isOpera = true -} else if (userAgent.includes('Safari')) { +} else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) { + // Chrome on macOs includes 'Safari' isSafari = true } else if (userAgent.includes('Chrome')) { isChrome = true diff --git a/src/util/pattern.ts b/src/util/pattern.ts index 626e2e10d..29282b424 100644 --- a/src/util/pattern.ts +++ b/src/util/pattern.ts @@ -18,6 +18,7 @@ export function isBrowserUrl(url: string) { // Firefox addons' pages || /^moz-extension:/.test(url) || /^edge.*?:\/\/.*$/.test(url) + || /^safari.*?:\/\/.*/.test(url) } const isNotValidPort = (portStr: string) => { From 945e6ffa088fce679151938b66445d3039bdb703 Mon Sep 17 00:00:00 2001 From: ZHY Date: Tue, 18 Oct 2022 19:38:01 +0800 Subject: [PATCH 4/9] Create safari-install.md --- safari-install.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 safari-install.md diff --git a/safari-install.md b/safari-install.md new file mode 100644 index 000000000..4d6b2497b --- /dev/null +++ b/safari-install.md @@ -0,0 +1,43 @@ +# How to install in Safari + +This is a too poor developer to pay $99 per year for distribution of an opensource and free browser extension in Apple App Store. +So please intall it **manually**, GG Safari. + +## 0. Download this repository + +```shell +git clone https://github.com/sheepzh/timer.git +cd timer +``` + +## 1. Install tools + +Some tools are required to compile this project to an executable software for Safari. + +* [nodejs & npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) +* [Xcode (compitable for your version of macOS)](https://developer.apple.com/xcode/) + +## 2. Compile source code, install & run + +There are serveral steps. + +1. Compile the sourcecode programmed with TypeScript to js bundles. + +```shell +# Install dependencies +npm install +# Compile +npm run build:safari +``` +Then there will be one folder called **Timer**. + +Also, you can download the archived file from [the release page](https://github.com/sheepzh/timer/releases), and unzip it to gain this folder. + +2. Convert js bundles to Xcode project + +```shell +[YOUR_PATH]/Xcode.app/Contents/Developer/usr/bin/safari-web-extension-converter ./Timer +``` +3. Run Xcode project and one extension app will installed on your macOS +4. Enable this extension +5. Finnally, open your Safari From a1496497887ea9e5494ca282aa886cc9d8749e2d Mon Sep 17 00:00:00 2001 From: ZHY Date: Tue, 18 Oct 2022 19:45:20 +0800 Subject: [PATCH 5/9] Ignore output path of Safari (#152) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 461018b64..25488f8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,11 @@ dist dist_dev dist_dev_safari dist_prod +dist_prod_safari dist_analyze Timer_Safari_DEV +Timer firefox_dev From 153b77bb548cc6a2d3d85218cf2e4b5e8c4f5e52 Mon Sep 17 00:00:00 2001 From: sheepzh Date: Tue, 18 Oct 2022 19:48:14 +0800 Subject: [PATCH 6/9] Not display icons of website in Safari (#152) --- src/app/components/common/host-alert.ts | 36 ++++++++++------- src/app/components/report/index.ts | 3 +- src/background/icon-and-alias-collector.ts | 6 +-- src/popup/components/chart/option.ts | 6 ++- src/popup/components/footer/index.ts | 3 +- webpack/webpack.prod.safari.ts | 47 ++++++++++++++++++++++ 6 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 webpack/webpack.prod.safari.ts diff --git a/src/app/components/common/host-alert.ts b/src/app/components/common/host-alert.ts index 4c5d85465..14b43e428 100644 --- a/src/app/components/common/host-alert.ts +++ b/src/app/components/common/host-alert.ts @@ -5,6 +5,7 @@ * https://opensource.org/licenses/MIT */ +import { IS_SAFARI } from "@util/constant/environment" import { ElLink } from "element-plus" import { computed, ComputedRef, defineComponent, h } from "vue" @@ -39,20 +40,27 @@ const _default = defineComponent({ const href: ComputedRef = computed(() => props.clickable ? `http://${props.host}` : '') const target: ComputedRef = computed(() => props.clickable ? '_blank' : '') const cursor: ComputedRef = computed(() => props.clickable ? "cursor" : "default") - return () => h('div', [ - h(ElLink, - { - href: href.value, - target: target.value, - underline: props.clickable, - style: { cursor: cursor.value } - }, - () => props.host - ), h('span', - { style: HOST_ICON_STYLE }, - h('img', { src: props.iconUrl, width: 12, height: 12 }) - ) - ]) + return IS_SAFARI + ? () => h(ElLink, { + href: href.value, + target: target.value, + underline: props.clickable, + style: { cursor: cursor.value } + }, () => props.host) + : () => h('div', [ + h(ElLink, + { + href: href.value, + target: target.value, + underline: props.clickable, + style: { cursor: cursor.value } + }, + () => props.host + ), h('span', + { style: HOST_ICON_STYLE }, + h('img', { src: props.iconUrl, width: 12, height: 12 }) + ) + ]) } }) diff --git a/src/app/components/report/index.ts b/src/app/components/report/index.ts index 78d5daf6d..6ad143e7f 100644 --- a/src/app/components/report/index.ts +++ b/src/app/components/report/index.ts @@ -27,6 +27,7 @@ import { useRoute, useRouter } from "vue-router" import { groupBy, sum } from "@util/array" import { formatTime } from "@util/time" import TimerDatabase from "@db/timer-database" +import { IS_SAFARI } from "@util/constant/environment" const timerDatabase = new TimerDatabase(chrome.storage.local) @@ -38,7 +39,7 @@ async function queryData( ) { const loading = ElLoadingService({ target: `.container-card>.el-card__body`, text: "LOADING..." }) const pageInfo = { size: page.size, num: page.num } - const fillFlag = { alias: true, iconUrl: true } + const fillFlag = { alias: true, iconUrl: !IS_SAFARI } const param = { ...queryParam.value, inclusiveRemote: readRemote.value diff --git a/src/background/icon-and-alias-collector.ts b/src/background/icon-and-alias-collector.ts index adeec6e2c..499ceae66 100644 --- a/src/background/icon-and-alias-collector.ts +++ b/src/background/icon-and-alias-collector.ts @@ -8,7 +8,7 @@ import HostAliasDatabase from "@db/host-alias-database" import IconUrlDatabase from "@db/icon-url-database" import OptionDatabase from "@db/option-database" -import { IS_CHROME } from "@util/constant/environment" +import { IS_CHROME, IS_SAFARI } from "@util/constant/environment" import { iconUrlOfBrowser } from "@util/constant/url" import { extractHostname, isBrowserUrl, isHomepage } from "@util/pattern" import { defaultStatistics } from "@util/constant/option" @@ -51,7 +51,7 @@ async function processTabInfo(tab: chrome.tabs.Tab): Promise { // localhost hosts with Chrome use cache, so keep the favIcon url undefined IS_CHROME && /^localhost(:.+)?/.test(host) && (favIconUrl = undefined) const iconUrl = favIconUrl || iconUrlOfBrowser(protocol, host) - iconUrlDatabase.put(host, iconUrl) + iconUrl && iconUrlDatabase.put(host, iconUrl) collectAliasEnabled && !isBrowserUrl(url) && isHomepage(url) && collectAlias(host, tab.title) } @@ -67,7 +67,7 @@ function handleWebNavigationCompleted(detail: chrome.webNavigation.WebNavigation } function listen() { - chrome.webNavigation.onCompleted.addListener(handleWebNavigationCompleted) + !IS_SAFARI && chrome.webNavigation.onCompleted.addListener(handleWebNavigationCompleted) } /** diff --git a/src/popup/components/chart/option.ts b/src/popup/components/chart/option.ts index a6496e2e4..7bbb77a15 100644 --- a/src/popup/components/chart/option.ts +++ b/src/popup/components/chart/option.ts @@ -18,6 +18,7 @@ import { formatPeriodCommon, formatTime } from "@util/time" import { t } from "@popup/locale" import { getPrimaryTextColor, getSecondaryTextColor } from "@util/style" import { generateSiteLabel } from "@util/site" +import { IS_SAFARI } from "@util/constant/environment" type EcOption = ComposeOption< | PieSeriesOption @@ -75,7 +76,10 @@ function labelFormatter({ mergeHost }: timer.popup.QueryResult, params: any): st const format = params instanceof Array ? params[0] : params const { name } = format const data = format.data as timer.popup.Row - return mergeHost || data.isOther ? name : `{${legend2LabelStyle(name)}|} {a|${name}}` + // Un-supported to get favicon url in Safari + return mergeHost || data.isOther || IS_SAFARI + ? name + : `{${legend2LabelStyle(name)}|} {a|${name}}` } const staticOptions: EcOption = { diff --git a/src/popup/components/footer/index.ts b/src/popup/components/footer/index.ts index 7f15936c4..dc79065fd 100644 --- a/src/popup/components/footer/index.ts +++ b/src/popup/components/footer/index.ts @@ -20,12 +20,13 @@ import { t } from "@popup/locale" import { locale } from "@util/i18n" import { getDayLenth, getMonthTime, getWeekDay, getWeekTime, MILL_PER_DAY } from "@util/time" import optionService from "@service/option-service" +import { IS_SAFARI } from "@util/constant/environment" type FooterParam = TimerQueryParam & { chartTitle: string } -const FILL_FLAG_PARAM: FillFlagParam = { iconUrl: true, alias: true } +const FILL_FLAG_PARAM: FillFlagParam = { iconUrl: !IS_SAFARI, alias: true } function calculateDateRange(duration: timer.popup.Duration, weekStart: timer.option.WeekStartOption): Date | Date[] { const now = new Date() diff --git a/webpack/webpack.prod.safari.ts b/webpack/webpack.prod.safari.ts new file mode 100644 index 000000000..b3ab5cd58 --- /dev/null +++ b/webpack/webpack.prod.safari.ts @@ -0,0 +1,47 @@ +import path from "path" +import optionGenerator from "./webpack.common" +import FileManagerWebpackPlugin from "filemanager-webpack-plugin" +import webpack from "webpack" + +const { name, version } = require(path.join(__dirname, '..', 'package.json')) + +const outputDir = path.join(__dirname, '..', 'dist_prod_safari') +const normalZipFilePath = path.resolve(__dirname, '..', 'market_packages', `${name}-${version}-safari.zip`) + +function removeUnsupportedProperties(manifest: Partial) { + // 1. permissions. 'idle' is not supported + const originPermissions = manifest.permissions || [] + const unsupported = ['idle'] + const supported = [] + originPermissions.forEach(perm => !unsupported.includes(perm) && supported.push(perm)) + manifest.permissions = supported +} + +const options = optionGenerator( + outputDir, + baseManifest => { + baseManifest.name = 'Timer' + // Remove unsupported properties in Safari + removeUnsupportedProperties(baseManifest) + } +) + +const filemanagerWebpackPlugin = new FileManagerWebpackPlugin({ + events: { + // Archive at the end + onEnd: [ + { delete: [path.join(outputDir, '*.LICENSE.txt')] }, + // Define plugin to archive zip for different markets + { + delete: [normalZipFilePath], + archive: [{ source: outputDir, destination: normalZipFilePath }] + } + ] + } +}) + +options.mode = 'production' +options.plugins.push(filemanagerWebpackPlugin as webpack.WebpackPluginInstance) +options.output.path = outputDir + +export default options \ No newline at end of file From 06adecb1682a65af69860661e81d49a75cd2596a Mon Sep 17 00:00:00 2001 From: sheepzh Date: Tue, 18 Oct 2022 20:23:22 +0800 Subject: [PATCH 7/9] Move --- safari-install.md => doc/safari-install.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename safari-install.md => doc/safari-install.md (100%) diff --git a/safari-install.md b/doc/safari-install.md similarity index 100% rename from safari-install.md rename to doc/safari-install.md From b163024b328d69c3002cc6efc77d6caaca58a86f Mon Sep 17 00:00:00 2001 From: ZHY Date: Tue, 18 Oct 2022 20:26:02 +0800 Subject: [PATCH 8/9] Update safari-install.md --- doc/safari-install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/safari-install.md b/doc/safari-install.md index 4d6b2497b..03c18cf6b 100644 --- a/doc/safari-install.md +++ b/doc/safari-install.md @@ -1,4 +1,4 @@ -# How to install in Safari +# How to install for Safari This is a too poor developer to pay $99 per year for distribution of an opensource and free browser extension in Apple App Store. So please intall it **manually**, GG Safari. From 1b2d10fcbb8475d93780e3d710e7ab444203c514 Mon Sep 17 00:00:00 2001 From: ZHY Date: Tue, 18 Oct 2022 20:26:32 +0800 Subject: [PATCH 9/9] Add link for Safari --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 213cfe0c1..81c428041 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Timer can [![](https://img.shields.io/amo/rating/2690100?color=green)](https://addons.mozilla.org/en-US/firefox/addon/2690100) [![Mozilla Add-on](https://img.shields.io/amo/users/2690100?color=green)](https://addons.mozilla.org/en-US/firefox/addon/2690100) +[How to install manually for Safari](./doc/safari-install.md) + ![User Count](https://gist.githubusercontent.com/sheepzh/6aaf4c22f909db73b533491167da129b/raw/user_count.svg) ## Contribution