From f174bef0ddc2c01c0d74f1bb56a5a949f2f03466 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 10 May 2024 16:25:13 +0200 Subject: [PATCH 01/33] fix(webpack): Split initial chunks to avoid AMO review complaining Signed-off-by: Marcel Klehr --- gulpfile.js | 1 + html/background.html | 5 +++++ html/index.html | 7 +++++++ html/options.html | 7 +++++++ webpack.common.js | 2 +- 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 1c2ebfc5a1..c1197d47b8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -234,6 +234,7 @@ exports.watch = gulp.series(gulp.parallel(assets, devjs), native, watch) exports.publish = publish exports.build = build exports.native = native +exports.package = gulp.parallel(firefoxZip, chromeZip, xpi) /* * Define default task that can be called by just running `gulp` from cli */ diff --git a/html/background.html b/html/background.html index eda786e4cf..d56dac61d6 100644 --- a/html/background.html +++ b/html/background.html @@ -6,6 +6,11 @@ + + + + + diff --git a/html/index.html b/html/index.html index f7ea66accf..ac45f7cff8 100644 --- a/html/index.html +++ b/html/index.html @@ -14,6 +14,13 @@
+ + + + + + + diff --git a/html/options.html b/html/options.html index 2be15a0fb3..ddd785f7f4 100644 --- a/html/options.html +++ b/html/options.html @@ -23,6 +23,13 @@
+ + + + + + + diff --git a/webpack.common.js b/webpack.common.js index be776f964f..9b365e2ae4 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -16,7 +16,7 @@ module.exports = { native: path.join(__dirname, 'src', 'entries', 'native.js'), }, optimization: { - splitChunks: { chunks: 'async' }, + splitChunks: { chunks: 'all' }, }, output: { path: path.resolve(__dirname, 'dist', 'js'), From 54b5310bf635896658efa11aa06a09b50e73dc42 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 10 May 2024 20:25:11 +0200 Subject: [PATCH 02/33] fix(webpack): Only do splitChunks:all on prod Signed-off-by: Marcel Klehr --- gulpfile.js | 2 +- webpack.common.js | 3 --- webpack.dev.js | 3 +++ webpack.prod.js | 3 +++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index c1197d47b8..3b23361836 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -137,7 +137,7 @@ const thirdparty = gulp.parallel(mocha) const assets = gulp.parallel(html, thirdparty, icons) -const build = gulp.parallel(assets, js) +const build = gulp.series(js, assets) const main = gulp.series(build, native) diff --git a/webpack.common.js b/webpack.common.js index 9b365e2ae4..dee1b731e5 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -15,9 +15,6 @@ module.exports = { test: path.join(__dirname, 'src', 'entries', 'test.js'), native: path.join(__dirname, 'src', 'entries', 'native.js'), }, - optimization: { - splitChunks: { chunks: 'all' }, - }, output: { path: path.resolve(__dirname, 'dist', 'js'), publicPath: '/dist/js/', diff --git a/webpack.dev.js b/webpack.dev.js index aeb467d607..f82db7eac0 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -5,6 +5,9 @@ const webpack = require('webpack') module.exports = merge(common, { mode: 'development', devtool: 'cheap-module-source-map', + optimization: { + splitChunks: { chunks: 'async' }, + }, plugins: [ new webpack.DefinePlugin({ 'DEBUG': JSON.stringify(true) diff --git a/webpack.prod.js b/webpack.prod.js index dc137daaed..40007b6b7f 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -8,6 +8,9 @@ const webpack = require('webpack') module.exports = merge(common, { mode: 'production', devtool: 'source-map', + optimization: { + splitChunks: { chunks: 'all' }, + }, plugins: [ new webpack.DefinePlugin({ DEBUG: JSON.stringify(!process.env['CI']) From 89700ca534735aabb256a5e95bce58a785c77b5a Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 09:02:16 +0200 Subject: [PATCH 03/33] fix(tests): Load missing chunk in test.html Signed-off-by: Marcel Klehr --- html/test.html | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/html/test.html b/html/test.html index 0345846406..71cf771d5e 100644 --- a/html/test.html +++ b/html/test.html @@ -8,6 +8,7 @@
+ diff --git a/package.json b/package.json index a7eec70502..dc9caf2668 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "floccus", - "version": "5.1.1", + "version": "5.1.1-dev", "description": "Sync your bookmarks privately across browsers and devices", "scripts": { "build": "gulp", From a87b0aa6cefcf566a9080972ef8b4b35be38dfd8 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 09:29:58 +0200 Subject: [PATCH 04/33] fix: Wrap local tree fetch error fixes #1606 Signed-off-by: Marcel Klehr --- _locales/en/messages.json | 6 ++++++ src/errors/Error.ts | 10 +++++++++- src/lib/browser/BrowserTree.ts | 8 +++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index efb154738e..16085e74de 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -107,6 +107,12 @@ "Error036": { "message": "E036: Missing permissions to access the sync server" }, + "Error037": { + "message": "E036: Resource is locked" + }, + "Error038": { + "message": "E036: Could not find local folder" + }, "LabelWebdavurl": { "message": "WebDAV URL" }, diff --git a/src/errors/Error.ts b/src/errors/Error.ts index 0ef6565276..1283d074af 100644 --- a/src/errors/Error.ts +++ b/src/errors/Error.ts @@ -313,6 +313,14 @@ export class ResourceLockedError extends FloccusError { constructor() { super(`E037: Resource is locked`) this.code = 37 - Object.setPrototypeOf(this, MissingPermissionsError.prototype) + Object.setPrototypeOf(this, ResourceLockedError.prototype) + } +} + +export class LocalFolderNotFoundError extends FloccusError { + constructor() { + super(`E037: Could not find local folder`) + this.code = 38 + Object.setPrototypeOf(this, LocalFolderNotFoundError.prototype) } } diff --git a/src/lib/browser/BrowserTree.ts b/src/lib/browser/BrowserTree.ts index 4fdf35a284..1b876bc6d0 100644 --- a/src/lib/browser/BrowserTree.ts +++ b/src/lib/browser/BrowserTree.ts @@ -9,6 +9,7 @@ import Ordering from '../interfaces/Ordering' import random from 'random' import seedrandom from 'seedrandom' import { isVivaldi } from './BrowserDetection' +import { LocalFolderNotFoundError } from '../../errors/Error' let absoluteRoot: {id: string} @@ -30,7 +31,12 @@ export default class BrowserTree implements IResource { async getBookmarksTree():Promise { const isVivaldiBrowser = await isVivaldi() - const [tree] = await browser.bookmarks.getSubTree(this.rootId) + let tree + try { + [tree] = await browser.bookmarks.getSubTree(this.rootId) + } catch (e) { + throw new LocalFolderNotFoundError() + } await this.absoluteRootPromise const allAccounts = await (await Account.getAccountClass()).getAllAccounts() From ac0367d7663e08147a667b02a2a7460c39f14e88 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 13:22:46 +0200 Subject: [PATCH 05/33] fix: Don't error in old Chrome versions if browser.permissions.contains fails Signed-off-by: Marcel Klehr --- src/lib/adapters/Git.ts | 8 +++++++- src/lib/adapters/GoogleDrive.ts | 8 +++++++- src/lib/adapters/WebDav.ts | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/lib/adapters/Git.ts b/src/lib/adapters/Git.ts index 83f5e0093b..fd83bf7fa2 100644 --- a/src/lib/adapters/Git.ts +++ b/src/lib/adapters/Git.ts @@ -72,7 +72,13 @@ export default class GitAdapter extends CachingAdapter { if (Capacitor.getPlatform() === 'web') { const browser = (await import('../browser-api')).default - if (!(await browser.permissions.contains({ origins: [this.server.url + '/'] }))) { + let hasPermissions + try { + hasPermissions = await browser.permissions.contains({ origins: [this.server.url + '/'] }) + } catch (e) { + console.warn(e) + } + if (!hasPermissions) { throw new MissingPermissionsError() } } diff --git a/src/lib/adapters/GoogleDrive.ts b/src/lib/adapters/GoogleDrive.ts index 305a5b82eb..981afe3cc7 100644 --- a/src/lib/adapters/GoogleDrive.ts +++ b/src/lib/adapters/GoogleDrive.ts @@ -197,7 +197,13 @@ export default class GoogleDriveAdapter extends CachingAdapter { if (Capacitor.getPlatform() === 'web') { const browser = (await import('../browser-api')).default const origins = ['https://oauth2.googleapis.com/', 'https://www.googleapis.com/'] - if (!(await browser.permissions.contains({ origins }))) { + let hasPermissions + try { + hasPermissions = await browser.permissions.contains({ origins }) + } catch (e) { + console.warn(e) + } + if (!hasPermissions) { throw new MissingPermissionsError() } } diff --git a/src/lib/adapters/WebDav.ts b/src/lib/adapters/WebDav.ts index 4dc03ca5a1..8d262ea9d3 100644 --- a/src/lib/adapters/WebDav.ts +++ b/src/lib/adapters/WebDav.ts @@ -238,7 +238,13 @@ export default class WebDavAdapter extends CachingAdapter { if (Capacitor.getPlatform() === 'web') { const browser = (await import('../browser-api')).default - if (!(await browser.permissions.contains({ origins: [this.server.url + '/'] }))) { + let hasPermissions + try { + hasPermissions = await browser.permissions.contains({ origins: [this.server.url + '/'] }) + } catch (e) { + console.warn(e) + } + if (!hasPermissions) { throw new MissingPermissionsError() } } From 418e409ce7a9b59ccb3b4bf22caeddca1d12f533 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 13:25:04 +0200 Subject: [PATCH 06/33] fix(Account#progressCallback): Don't error if syncProcess is not defined yet Signed-off-by: Marcel Klehr --- src/lib/Account.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/Account.ts b/src/lib/Account.ts index d2b2bd8a86..dbaddd67d0 100644 --- a/src/lib/Account.ts +++ b/src/lib/Account.ts @@ -363,11 +363,14 @@ export default class Account { if (!this.syncing) { return } + await this.setData({ ...this.getData(), syncing: progress }) + if (!this.syncProcess) { + return + } if (actionsDone && (!(this.server instanceof CachingAdapter) || !('onSyncComplete' in this.server))) { await this.storage.setCurrentContinuation(this.syncProcess.toJSON()) await this.syncProcess.getMappingsInstance().persist() } - await this.setData({ ...this.getData(), syncing: progress }) } static async getAllAccounts():Promise { From ac08c37521366721ac00437070b16ae3ddd4f091 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 13:28:48 +0200 Subject: [PATCH 07/33] fix(BrowserController#setStatusBadge): Don't throw when setting icon Signed-off-by: Marcel Klehr --- src/lib/browser/BrowserController.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/browser/BrowserController.js b/src/lib/browser/BrowserController.js index 8dd25af00a..a13ebc8aa8 100644 --- a/src/lib/browser/BrowserController.js +++ b/src/lib/browser/BrowserController.js @@ -359,10 +359,15 @@ export default class BrowserController { } if (icon[status]) { - if (navigator.userAgent.includes('Firefox')) { + try { await browser.browserAction.setIcon(icon[status]) - } else { + } catch (e) { + console.warn(e) + } + try { await browser.action.setIcon(icon[status]) + } catch (e) { + console.warn(e) } } } From 93e49bb4d8c4662c4e21ad953f36e5d33d569c68 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 17:07:16 +0200 Subject: [PATCH 08/33] fix(webpack): Don't set DEBUG to true in production Signed-off-by: Marcel Klehr --- webpack.prod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.prod.js b/webpack.prod.js index 40007b6b7f..02f736dc5a 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -13,7 +13,7 @@ module.exports = merge(common, { }, plugins: [ new webpack.DefinePlugin({ - DEBUG: JSON.stringify(!process.env['CI']) + DEBUG: JSON.stringify(false) }), sentryWebpackPlugin({ authToken: process.env.SENTRY_AUTH_TOKEN, From 62310b9473252ee55aa82cf49173d0987adb948d Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 10 May 2024 18:02:23 +0200 Subject: [PATCH 09/33] feat(NextcloudBookmarks): Accept javascript: links fixes #1445 Signed-off-by: Marcel Klehr --- src/lib/adapters/NextcloudBookmarks.ts | 15 +++----- src/test/test.js | 52 +++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index ed7764c9b8..3cb00c5b25 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -74,6 +74,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes private lockingInterval: any private lockingPromise: Promise private ended = false + private hasFeatureJavascriptLinks: boolean = null constructor(server: NextcloudBookmarksConfig) { this.server = server @@ -111,7 +112,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes acceptsBookmark(bm: Bookmark):boolean { try { - return Boolean(~['https:', 'http:', 'ftp:'].indexOf(new URL(bm.url).protocol)) + return Boolean(~['https:', 'http:', 'ftp:'].concat(this.hasFeatureJavascriptLinks ? ['javascript:'] : []).indexOf(new URL(bm.url).protocol)) } catch (e) { return false } @@ -128,13 +129,6 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes return output + (output[output.length - 1] !== '/' ? '/' : '') } - timeout(ms) { - return new Promise((resolve, reject) => { - setTimeout(resolve, ms) - this.cancelCallback = () => reject(new InterruptedSyncError()) - }) - } - async onSyncStart(needLock = true): Promise { if (Capacitor.getPlatform() === 'web') { const browser = (await import('../browser-api')).default @@ -374,6 +368,9 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes const recurseChildren = (folderId, children) => { return children.map((item) => { if (item.type === 'bookmark') { + if ('href' in item && this.hasFeatureJavascriptLinks === null) { + this.hasFeatureJavascriptLinks = true + } return new Bookmark({ id: item.id + ';' + folderId, title: item.title, @@ -760,7 +757,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes await this.updateBookmark(updatedBookmark) } else { const body = { - url: bm.url, + ...(this.hasFeatureJavascriptLinks ? {href: bm.url} : {url: bm.url}), title: bm.title, folders: [bm.parentId], } diff --git a/src/test/test.js b/src/test/test.js index a8aea2a008..d04cdc32de 100644 --- a/src/test/test.js +++ b/src/test/test.js @@ -242,6 +242,7 @@ describe('Floccus', function() { if (ACCOUNT_DATA.type === 'nextcloud-bookmarks' && ACCOUNT_DATA.oldAPIs) { // account.server.hasFeatureHashing = false account.server.hasFeatureChildren = false + account.server.hasFeatureJavascriptLinks = false } if (ACCOUNT_DATA.noCache) { account.storage.setCache = () => { @@ -327,6 +328,53 @@ describe('Floccus', function() { false ) }) + it('should create local javascript bookmarks on the server', async function() { + if (ACCOUNT_DATA.oldAPIs) { + return this.skip() + } + expect( + (await getAllBookmarks(account)).children + ).to.have.lengthOf(0) + + const localRoot = account.getData().localRoot + const fooFolder = await browser.bookmarks.create({ + title: 'foo', + parentId: localRoot + }) + const barFolder = await browser.bookmarks.create({ + title: 'bar', + parentId: fooFolder.id + }) + const bookmark = await browser.bookmarks.create({ + title: 'url', + url: 'javascript:void(0)', + parentId: barFolder.id + }) + await account.sync() + expect(account.getData().error).to.not.be.ok + + const tree = await getAllBookmarks(account) + expectTreeEqual( + tree, + new Folder({ + title: tree.title, + children: [ + new Folder({ + title: 'foo', + children: [ + new Folder({ + title: 'bar', + children: [ + new Bookmark({ title: 'url', url: bookmark.url }) + ] + }) + ] + }) + ] + }), + false + ) + }) it('should update the server on local changes', async function() { if (ACCOUNT_DATA.noCache) { return this.skip() @@ -1829,7 +1877,7 @@ describe('Floccus', function() { expect(account.getData().error).to.be.ok // should have errored }) it('should leave alone unaccepted bookmarks entirely', async function() { - if (!~ACCOUNT_DATA.type.indexOf('nextcloud')) { + if (!~ACCOUNT_DATA.type.indexOf('nextcloud') || !ACCOUNT_DATA.oldAPIs) { this.skip() } const localRoot = account.getData().localRoot @@ -4870,6 +4918,8 @@ describe('Floccus', function() { if (ACCOUNT_DATA.type === 'nextcloud-bookmarks' && ACCOUNT_DATA.oldAPIs) { account1.server.hasFeatureHashing = false account2.server.hasFeatureHashing = false + account1.server.hasFeatureJavascriptLinks = false + account2.server.hasFeatureJavascriptLinks = false } if (ACCOUNT_DATA.noCache) { account1.storage.setCache = () => { From fb0644ce8207388ca7b32a135bcddcaac0191185 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 17:30:00 +0200 Subject: [PATCH 10/33] fix(messages): DescriptionAdapternextcloudfolders add javascript to accepted bookmarks Signed-off-by: Marcel Klehr --- _locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 16085e74de..0b946d3145 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -270,7 +270,7 @@ "message": "Nextcloud Bookmarks" }, "DescriptionAdapternextcloudfolders": { - "message": "The option 'Nextcloud Bookmarks' syncs your bookmarks with the Bookmarks app for Nextcloud. It can only sync http and ftp bookmarks. Make sure you have installed the Bookmarks app from the Nextcloud app store in your Nextcloud." + "message": "The option 'Nextcloud Bookmarks' syncs your bookmarks with the Bookmarks app for Nextcloud. It can only sync http, ftp and javascript bookmarks. Make sure you have installed the Bookmarks app from the Nextcloud app store in your Nextcloud." }, "LabelAdapternextcloud": { "message": "Nextcloud Bookmarks (legacy)" From 9393a477c62cafc6dcf0b8f8579932a9c70e7a52 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 18:21:36 +0200 Subject: [PATCH 11/33] [native] fix(intent): Register intent activity properly + reload the tree regularly in the main screen to overtake changes from the intent Signed-off-by: Marcel Klehr --- android/app/src/main/AndroidManifest.xml | 23 ++++++++++++++--------- src/ui/NativeApp.vue | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index de068c3fed..5c37adfd96 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -24,15 +24,6 @@ - - - - - - - - - @@ -43,6 +34,20 @@ + + + + + + + + + + { this.$store.dispatch(actions.LOAD_ACCOUNTS) + this.$store.dispatch(actions.LOAD_TREE) }, 5000) const controller = await Controller.getSingleton() controller.onLoad() From c4c28c72d24ba7d8a33375b6822532843a2703d9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 11 May 2024 18:58:05 +0200 Subject: [PATCH 12/33] fix(NextcloudBookmarks): Retrieve href if possible Signed-off-by: Marcel Klehr --- src/lib/adapters/NextcloudBookmarks.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index 3cb00c5b25..cfa7a4c58a 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -198,7 +198,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes data.map((bm) => { const bookmark = { id: bm.id as number | string, - url: bm.url as string, + url: bm.href || bm.url as string, title: bm.title as string, parentId: null, location: ItemLocation.SERVER, @@ -375,7 +375,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes id: item.id + ';' + folderId, title: item.title, parentId: folderId, - url: item.url, + url: item.href || item.url, location: ItemLocation.SERVER, }) } else if (item.type === 'folder') { @@ -599,7 +599,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes return new Bookmark({ id: item.id + ';' + id, title: item.title, - url: item.url, + url: item.href || item.url, parentId: id, location: ItemLocation.SERVER, }) @@ -709,7 +709,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes return bm.folders.map((parentId) => { return new Bookmark({ id: bm.id + ';' + parentId, - url: bm.url, + url: bm.href || bm.url, title: bm.title, parentId: parentId, tags: bm.tags, @@ -803,7 +803,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes const bms = await this._getBookmark(upstreamId) const body = { - url: newBm.url, + ...(this.hasFeatureJavascriptLinks ? {href: newBm.url} : {url: newBm.url}), title: newBm.title, folders: bms .map((bm) => bm.parentId) From 9d94d7912d1984df00ca7b551dd929b18aa955dc Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 12 May 2024 12:32:32 +0200 Subject: [PATCH 13/33] fix(gulpfile): Clean dis/js/ before each run Signed-off-by: Marcel Klehr --- gulpfile.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 3b23361836..b90d9a2bc5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -52,7 +52,8 @@ const paths = { js: 'src/**', builds: './builds/', icons: 'icons/*', - dist: './dist/**' + dist: './dist/**', + distJs: './dist/js', } paths.chromeZip = [...paths.zip, 'manifest.chrome.json'] @@ -131,13 +132,17 @@ const native = async function() { console.log(stdout) } +const cleanJs = async function() { + fs.rmSync(paths.distJs, {recursive: true}) +} + const mocha = gulp.parallel(mochajs, mochacss) const thirdparty = gulp.parallel(mocha) const assets = gulp.parallel(html, thirdparty, icons) -const build = gulp.series(js, assets) +const build = gulp.series(cleanJs, js, assets) const main = gulp.series(build, native) @@ -228,9 +233,8 @@ function onWatchEvent(path) { exports.html = html exports.js = js exports.mocha = mocha -exports.watch = watch exports.release = release -exports.watch = gulp.series(gulp.parallel(assets, devjs), native, watch) +exports.watch = gulp.series(cleanJs,gulp.parallel(assets, devjs), native, watch) exports.publish = publish exports.build = build exports.native = native From 399c66ff487c5dbbaca2b624cb2da7d755bdbfa5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 12 May 2024 12:31:53 +0200 Subject: [PATCH 14/33] fix(NextcloudBookmarks): Fix javascript links Signed-off-by: Marcel Klehr --- src/lib/adapters/NextcloudBookmarks.ts | 109 ++++++++++++++++++------- src/test/test.js | 31 +++++++ 2 files changed, 109 insertions(+), 31 deletions(-) diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index cfa7a4c58a..bf21b256c5 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -75,6 +75,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes private lockingPromise: Promise private ended = false private hasFeatureJavascriptLinks: boolean = null + private rootHash: string = null constructor(server: NextcloudBookmarksConfig) { this.server = server @@ -198,7 +199,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes data.map((bm) => { const bookmark = { id: bm.id as number | string, - url: bm.href || bm.url as string, + url: (bm.target || bm.url) as string, title: bm.title as string, parentId: null, location: ItemLocation.SERVER, @@ -221,30 +222,16 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes async getBookmarksTree(loadAll = false):Promise { this.list = null // clear cache before starting a new sync - // feature detection: Check if the server offers hashes - const hashResponse = await this.sendRequest( - 'GET', - 'index.php/apps/bookmarks/public/rest/v2/folder/-1/hash', - null, - null, - true - ) - let json - try { - json = await hashResponse.json() - } catch (e) { - json = hashResponse.data - // noop + await this.checkFeatureJavascriptLinks() + if (this.hasFeatureJavascriptLinks) { + this.hasFeatureHashing = true + } else { + await this.checkFeatureHashing() } - if ( - !loadAll && - ((hashResponse.status === 200 && json && json.status === 'success') || - hashResponse.status === 504) - ) { + if (!loadAll && this.hasFeatureHashing) { return this.getSparseBookmarksTree() } else { - this.hasFeatureHashing = false return this.getCompleteBookmarksTree() } } @@ -329,7 +316,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes this.list = null tree.loaded = false - tree.hashValue = { true: await this._getFolderHash(tree.id) } + tree.hashValue = { true: this.rootHash || await this._getFolderHash(tree.id) } this.tree = tree.clone(true) // we clone (withHash), so we can mess with our own version return tree } @@ -368,14 +355,14 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes const recurseChildren = (folderId, children) => { return children.map((item) => { if (item.type === 'bookmark') { - if ('href' in item && this.hasFeatureJavascriptLinks === null) { + if ('target' in item && this.hasFeatureJavascriptLinks === null) { this.hasFeatureJavascriptLinks = true } return new Bookmark({ id: item.id + ';' + folderId, title: item.title, parentId: folderId, - url: item.href || item.url, + url: item.target || item.url, location: ItemLocation.SERVER, }) } else if (item.type === 'folder') { @@ -599,7 +586,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes return new Bookmark({ id: item.id + ';' + id, title: item.title, - url: item.href || item.url, + url: item.target || item.url, parentId: id, location: ItemLocation.SERVER, }) @@ -709,7 +696,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes return bm.folders.map((parentId) => { return new Bookmark({ id: bm.id + ';' + parentId, - url: bm.href || bm.url, + url: bm.target || bm.url, title: bm.title, parentId: parentId, tags: bm.tags, @@ -731,7 +718,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes )}` ) if (json.data.length) { - return {...json.data[0], parentId: json.data[0].folders[0]} + return {...json.data[0], parentId: json.data[0].folders[0], url} } else { return false } @@ -757,7 +744,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes await this.updateBookmark(updatedBookmark) } else { const body = { - ...(this.hasFeatureJavascriptLinks ? {href: bm.url} : {url: bm.url}), + url: bm.url, title: bm.title, folders: [bm.parentId], } @@ -785,8 +772,10 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes const upstreamMark = bm.clone() upstreamMark.id = bm.id.split(';')[0] this.list && this.list.push(upstreamMark) - this.tree.findFolder(bm.parentId).children.push(upstreamMark) - this.tree.createIndex() + if (this.tree) { + this.tree.findFolder(bm.parentId).children.push(upstreamMark) + this.tree.createIndex() + } return bm.id }) @@ -803,7 +792,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes const bms = await this._getBookmark(upstreamId) const body = { - ...(this.hasFeatureJavascriptLinks ? {href: newBm.url} : {url: newBm.url}), + url: newBm.url, title: newBm.title, folders: bms .map((bm) => bm.parentId) @@ -859,6 +848,64 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes }) } + async checkFeatureHashing(): Promise { + if (this.hasFeatureHashing !== null) { + return + } + // feature detection: Check if the server offers hashes + const hashResponse = await this.sendRequest( + 'GET', + 'index.php/apps/bookmarks/public/rest/v2/folder/-1/hash', + null, + null, + true + ) + let json + try { + json = await hashResponse.json() + } catch (e) { + json = hashResponse.data + // noop + } + + if ( + ((hashResponse.status === 200 && json && json.status === 'success') || + hashResponse.status === 504) + ) { + this.hasFeatureHashing = true + this.rootHash = json.data + } else { + this.hasFeatureHashing = false + } + } + + async checkFeatureJavascriptLinks(): Promise { + if (this.hasFeatureJavascriptLinks !== null) { + return + } + try { + const json = await this.sendRequest( + 'GET', + `index.php/apps/bookmarks/public/rest/v2/bookmark?page=1&limit=1` + ) + console.log(json) + if (!json.data.length) { + this.hasFeatureJavascriptLinks = true + try { + const id = await this.createBookmark(new Bookmark({id: null, parentId: '-1', title: 'floccus', url: 'javascript:void(0)', location: ItemLocation.SERVER})) + await this.removeBookmark(new Bookmark({id, parentId: '-1', title: 'floccus', url: 'javascript:void(0)', location: ItemLocation.SERVER})) + } catch (e) { + console.log(e) + this.hasFeatureJavascriptLinks = false + } + return + } + this.hasFeatureJavascriptLinks = 'target' in json.data[0] + } catch (e) { + this.hasFeatureJavascriptLinks = false + } + } + async sendRequest(verb:string, relUrl:string, type:string = null, body:any = null, returnRawResponse = false):Promise { const url = this.normalizeServerURL(this.server.url) + relUrl let res diff --git a/src/test/test.js b/src/test/test.js index d04cdc32de..48e8255da8 100644 --- a/src/test/test.js +++ b/src/test/test.js @@ -374,6 +374,37 @@ describe('Floccus', function() { }), false ) + + const bookmark2 = await browser.bookmarks.create({ + title: 'url2', + url: 'javascript:void(1)', + parentId: barFolder.id + }) + await account.sync() + expect(account.getData().error).to.not.be.ok + + const tree2 = await getAllBookmarks(account) + expectTreeEqual( + tree2, + new Folder({ + title: tree.title, + children: [ + new Folder({ + title: 'foo', + children: [ + new Folder({ + title: 'bar', + children: [ + new Bookmark({ title: 'url', url: bookmark.url }), + new Bookmark({ title: 'url2', url: bookmark2.url }), + ] + }) + ] + }) + ] + }), + false + ) }) it('should update the server on local changes', async function() { if (ACCOUNT_DATA.noCache) { From 3d9edea0245c188591005856229eb540f499f77e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 12 May 2024 14:43:10 +0200 Subject: [PATCH 15/33] fix(NextcloudBookmarks): Remove feature detection of 5yo features Signed-off-by: Marcel Klehr --- .github/workflows/tests.yml | 8 - src/lib/adapters/NextcloudBookmarks.ts | 241 +++++-------------------- src/test/test.js | 31 +--- 3 files changed, 45 insertions(+), 235 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a7d5463c51..e1cc972f3a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -92,7 +92,6 @@ jobs: floccus-adapter: - fake - nextcloud-bookmarks - - nextcloud-bookmarks-old - webdav-xbel - webdav-html - webdav-html-encrypted @@ -122,13 +121,6 @@ jobs: browsers: firefox node-version: 14.x npm-version: 7.x - - app-version: master - server-version: 28 - floccus-adapter: nextcloud-bookmarks-old - test-name: benchmark root - browsers: firefox - node-version: 14.x - npm-version: 7.x - app-version: stable server-version: 28 floccus-adapter: fake-noCache diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index bf21b256c5..04d6f2741b 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -15,9 +15,7 @@ import Ordering from '../interfaces/Ordering' import { AuthenticationError, CreateBookmarkError, HttpError, - InconsistentBookmarksExistenceError, - InconsistentServerStateError, - InterruptedSyncError, MissingPermissionsError, + MissingPermissionsError, NetworkError, ParseResponseError, RedirectError, @@ -62,9 +60,6 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes private server: NextcloudBookmarksConfig private fetchQueue: PQueue<{ concurrency: 12 }> private bookmarkLock: AsyncLock - public hasFeatureHashing:boolean = null - public hasFeatureExistenceCheck:boolean = null - public hasFeatureChildren:boolean = null public hasFeatureBulkImport:boolean = null private list: Bookmark[] private tree: Folder @@ -81,8 +76,6 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes this.server = server this.fetchQueue = new PQueue({ concurrency: 12 }) this.bookmarkLock = new AsyncLock() - this.hasFeatureHashing = false - this.hasFeatureExistenceCheck = false } static getDefaultValues(): NextcloudBookmarksConfig { @@ -223,13 +216,8 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes this.list = null // clear cache before starting a new sync await this.checkFeatureJavascriptLinks() - if (this.hasFeatureJavascriptLinks) { - this.hasFeatureHashing = true - } else { - await this.checkFeatureHashing() - } - if (!loadAll && this.hasFeatureHashing) { + if (!loadAll) { return this.getSparseBookmarksTree() } else { return this.getCompleteBookmarksTree() @@ -305,9 +293,6 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes } async getSparseBookmarksTree() :Promise { - this.hasFeatureHashing = true - this.hasFeatureExistenceCheck = true - let tree = new Folder({ id: -1, location: ItemLocation.SERVER }) if (this.server.serverRoot) { @@ -316,7 +301,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes this.list = null tree.loaded = false - tree.hashValue = { true: this.rootHash || await this._getFolderHash(tree.id) } + tree.hashValue = { true: await this._getFolderHash(tree.id) } this.tree = tree.clone(true) // we clone (withHash), so we can mess with our own version return tree } @@ -335,145 +320,41 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes } async _getChildren(folderId:string|number, layers:number):Promise { - let childrenJson - if ( - this.hasFeatureChildren === null || - this.hasFeatureChildren - ) { - try { - childrenJson = await this.sendRequest( - 'GET', - `index.php/apps/bookmarks/public/rest/v2/folder/${folderId}/children?layers=${layers}` - ) - this.hasFeatureChildren = true - } catch (e) { - this.hasFeatureChildren = false - } - } - if (this.hasFeatureChildren) { - const children = childrenJson.data - const recurseChildren = (folderId, children) => { - return children.map((item) => { - if (item.type === 'bookmark') { - if ('target' in item && this.hasFeatureJavascriptLinks === null) { - this.hasFeatureJavascriptLinks = true - } - return new Bookmark({ - id: item.id + ';' + folderId, - title: item.title, - parentId: folderId, - url: item.target || item.url, - location: ItemLocation.SERVER, - }) - } else if (item.type === 'folder') { - const childFolder = new Folder({ - id: item.id, - parentId: folderId, - title: item.title, - location: ItemLocation.SERVER, - }) - childFolder.loaded = Boolean(item.children) // not children.length but whether the whole children field exists - childFolder.children = recurseChildren(item.id, item.children || []) - return childFolder + const childrenJson = await this.sendRequest( + 'GET', + `index.php/apps/bookmarks/public/rest/v2/folder/${folderId}/children?layers=${layers}` + ) + const children = childrenJson.data + const recurseChildren = (folderId, children) => { + return children.map((item) => { + if (item.type === 'bookmark') { + if ('target' in item && this.hasFeatureJavascriptLinks === null) { + this.hasFeatureJavascriptLinks = true } - }) - } - return recurseChildren(folderId, children).filter(item => String(item.id) !== String(this.lockId)) - } else { - // We don't have the children endpoint available, so we have to query all bookmarks that exist :( - await this.getBookmarksList() - - const tree = new Folder({id: folderId, location: ItemLocation.SERVER}) - const [childrenOrder, childFolders, childBookmarks] = await Promise.all([ - this._getChildOrder(folderId, layers), - this._getChildFolders(folderId, layers), - Promise.resolve().then( - () => - this.list || - this.sendRequest( - 'GET', - `index.php/apps/bookmarks/public/rest/v2/bookmark?folder=${folderId}&page=-1` - ).then((json) => json.data) - ), - ]) - const recurseChildFolders = async(tree:Folder, childFolders:IChildFolder[], childrenOrder:IChildOrderItem[], childBookmarks:any[], layers:number) => { - const folders = await Parallel.map( - childrenOrder, - async(child) => { - if (child.type === 'folder') { - // get the folder from the tree we've fetched above - const folder = childFolders.find((folder) => String(folder.id) === String(child.id)) - if (!folder) throw new InconsistentServerStateError() - const newFolder = new Folder({ - id: child.id, - title: folder.title, - parentId: tree.id, - loaded: false, - location: ItemLocation.SERVER, - }) - tree.children.push(newFolder) - return { newFolder, child, folder} - } else { - // get the bookmark from the list we've fetched above - // which might either be Bookmark[] or a raw bookmark list response with no parentId but a folders array - let childBookmark = childBookmarks.find( - (bookmark) => - String(bookmark.id) === String(child.id) && - (!bookmark.parentId || String(bookmark.parentId) === String(tree.id)) - ) - if (!childBookmark) { - throw new InconsistentBookmarksExistenceError( - `#${tree.id}[${tree.title}]`, - String(child.id) - ) - } - if (!(childBookmark instanceof Bookmark)) { - childBookmark = new Bookmark(childBookmark) - } - childBookmark = childBookmark.clone() - childBookmark.id = childBookmark.id + ';' + tree.id - childBookmark.parentId = tree.id - tree.children.push(childBookmark) - } - }, - 1 - ) - tree.loaded = true - if (layers === 0) { - return + return new Bookmark({ + id: item.id + ';' + folderId, + title: item.title, + parentId: folderId, + url: item.target || item.url, + location: ItemLocation.SERVER, + }) + } else if (item.type === 'folder') { + const childFolder = new Folder({ + id: item.id, + parentId: folderId, + title: item.title, + location: ItemLocation.SERVER, + }) + childFolder.loaded = Boolean(item.children) // not children.length but whether the whole children field exists + childFolder.children = recurseChildren(item.id, item.children || []) + return childFolder } - - const nextLayer = layers < 0 ? -1 : layers - 1 - await Parallel.each( - folders.filter(Boolean), - async({ newFolder, child, folder}) => { - if (typeof child.children === 'undefined') { - child.children = await this._getChildOrder(child.id, nextLayer) - } - if (typeof folder.children === 'undefined') { - folder.children = await this._getChildFolders(folder.id, nextLayer) - } - const childBookmarks = this.list || - await this.sendRequest( - 'GET', - `index.php/apps/bookmarks/public/rest/v2/bookmark?folder=${newFolder.id}&page=-1` - ).then((json) => json.data) - - // ... and recurse - return recurseChildFolders(newFolder, folder.children, child.children, childBookmarks, nextLayer) - }, - 3 - ) - } - await recurseChildFolders(tree, childFolders, childrenOrder, childBookmarks, layers) - return tree.children.filter(item => String(item.id) !== String(this.lockId)) + }) } + return recurseChildren(folderId, children).filter(item => String(item.id) !== String(this.lockId)) } async loadFolderChildren(folderId:string|number, all?: boolean): Promise { - if (!this.hasFeatureHashing) { - return - } const folder = this.tree.findFolder(folderId) if (!folder) { throw new Error('Could not find folder for loadFolderChildren') @@ -496,7 +377,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes child.hashValue = { true: folderHash } } await recurse(child.children) - }) + }, 5) } await recurse(children) } @@ -710,23 +591,16 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes * querying urls directly */ async getExistingBookmark(url:string):Promise { - if (this.hasFeatureExistenceCheck) { - const json = await this.sendRequest( - 'GET', - `index.php/apps/bookmarks/public/rest/v2/bookmark?url=${encodeURIComponent( - url - )}` - ) - if (json.data.length) { - return {...json.data[0], parentId: json.data[0].folders[0], url} - } else { - return false - } + const json = await this.sendRequest( + 'GET', + `index.php/apps/bookmarks/public/rest/v2/bookmark?url=${encodeURIComponent( + url + )}` + ) + if (json.data.length) { + return {...json.data[0], parentId: json.data[0].folders[0], url} } else { - await this.getBookmarksList() - const existing = this.list.find((bookmark) => bookmark.url === url) - if (!existing) return false - return existing + return false } } @@ -848,37 +722,6 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes }) } - async checkFeatureHashing(): Promise { - if (this.hasFeatureHashing !== null) { - return - } - // feature detection: Check if the server offers hashes - const hashResponse = await this.sendRequest( - 'GET', - 'index.php/apps/bookmarks/public/rest/v2/folder/-1/hash', - null, - null, - true - ) - let json - try { - json = await hashResponse.json() - } catch (e) { - json = hashResponse.data - // noop - } - - if ( - ((hashResponse.status === 200 && json && json.status === 'success') || - hashResponse.status === 504) - ) { - this.hasFeatureHashing = true - this.rootHash = json.data - } else { - this.hasFeatureHashing = false - } - } - async checkFeatureJavascriptLinks(): Promise { if (this.hasFeatureJavascriptLinks !== null) { return diff --git a/src/test/test.js b/src/test/test.js index 48e8255da8..1d671bad8b 100644 --- a/src/test/test.js +++ b/src/test/test.js @@ -86,12 +86,6 @@ describe('Floccus', function() { url: SERVER, ...CREDENTIALS }, - { - type: 'nextcloud-bookmarks', - url: SERVER, - oldAPIs: true, - ...CREDENTIALS - }, { type: 'nextcloud-bookmarks', url: SERVER, @@ -239,11 +233,6 @@ describe('Floccus', function() { }) } await account.init() - if (ACCOUNT_DATA.type === 'nextcloud-bookmarks' && ACCOUNT_DATA.oldAPIs) { - // account.server.hasFeatureHashing = false - account.server.hasFeatureChildren = false - account.server.hasFeatureJavascriptLinks = false - } if (ACCOUNT_DATA.noCache) { account.storage.setCache = () => { // noop @@ -329,9 +318,6 @@ describe('Floccus', function() { ) }) it('should create local javascript bookmarks on the server', async function() { - if (ACCOUNT_DATA.oldAPIs) { - return this.skip() - } expect( (await getAllBookmarks(account)).children ).to.have.lengthOf(0) @@ -1908,7 +1894,7 @@ describe('Floccus', function() { expect(account.getData().error).to.be.ok // should have errored }) it('should leave alone unaccepted bookmarks entirely', async function() { - if (!~ACCOUNT_DATA.type.indexOf('nextcloud') || !ACCOUNT_DATA.oldAPIs) { + if (!~ACCOUNT_DATA.type.indexOf('nextcloud')) { this.skip() } const localRoot = account.getData().localRoot @@ -1932,7 +1918,7 @@ describe('Floccus', function() { }) await browser.bookmarks.create({ title: 'url2', - url: 'javascript:void(0)', + url: 'chrome://extensions/', parentId: fooFolder.id }) await account.sync() // propagate to server @@ -1978,7 +1964,7 @@ describe('Floccus', function() { children: [ new Bookmark({ title: 'url2', - url: 'javascript:void(0)' + url: 'chrome://extensions/' }) ] }) @@ -4605,10 +4591,6 @@ describe('Floccus', function() { } await account.init() await account.setData({...account.getData(), localRoot: 'tabs'}) - if (ACCOUNT_DATA.type === 'nextcloud-bookmarks' && ACCOUNT_DATA.oldAPIs) { - // account.server.hasFeatureHashing = false - account.server.hasFeatureChildren = false - } if (ACCOUNT_DATA.noCache) { account.storage.setCache = () => { // noop @@ -4946,12 +4928,6 @@ describe('Floccus', function() { }) account2.server.__defineGetter__('highestId', () => account1.server.highestId) } - if (ACCOUNT_DATA.type === 'nextcloud-bookmarks' && ACCOUNT_DATA.oldAPIs) { - account1.server.hasFeatureHashing = false - account2.server.hasFeatureHashing = false - account1.server.hasFeatureJavascriptLinks = false - account2.server.hasFeatureJavascriptLinks = false - } if (ACCOUNT_DATA.noCache) { account1.storage.setCache = () => { // noop @@ -6788,7 +6764,6 @@ async function syncAccountWithInterrupts(account) { function stringifyAccountData(ACCOUNT_DATA) { return `${ACCOUNT_DATA.type}${ - (ACCOUNT_DATA.type === 'nextcloud-bookmarks' && ACCOUNT_DATA.oldAPIs ? '-old' : '') + (ACCOUNT_DATA.noCache ? '-noCache' : '') + (typeof ACCOUNT_DATA.bookmark_file_type !== 'undefined' ? '-' + ACCOUNT_DATA.bookmark_file_type : '') + ((ACCOUNT_DATA.type === 'google-drive' && ACCOUNT_DATA.password) || (ACCOUNT_DATA.type === 'webdav' && ACCOUNT_DATA.passphrase) ? '-encrypted' : '') From cf1665c3a59d8ec43ed117df434729f9bc40ebb7 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 12 May 2024 15:21:20 +0200 Subject: [PATCH 16/33] fix: fix build Signed-off-by: Marcel Klehr --- gulpfile.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index b90d9a2bc5..35c7c05c94 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -133,7 +133,11 @@ const native = async function() { } const cleanJs = async function() { - fs.rmSync(paths.distJs, {recursive: true}) + try { + fs.rmSync(paths.distJs, { recursive: true }) + } catch (e) { + // noop + } } const mocha = gulp.parallel(mochajs, mochacss) From 0c6a74d0f9c65a562493626773cb99be4df06c62 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 12 May 2024 18:19:25 +0200 Subject: [PATCH 17/33] [native] fix: Reload tree on app resume fixes #1542 Signed-off-by: Marcel Klehr --- src/lib/native/NativeTree.ts | 6 +++++- src/ui/NativeApp.vue | 1 - src/ui/store/native/actions.js | 5 +++++ src/ui/views/native/Tree.vue | 7 ++++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/lib/native/NativeTree.ts b/src/lib/native/NativeTree.ts index d08907a8d5..c291cfc8f1 100644 --- a/src/lib/native/NativeTree.ts +++ b/src/lib/native/NativeTree.ts @@ -17,14 +17,18 @@ export default class NativeTree extends CachingAdapter implements BulkImportReso this.accountId = this.storage.accountId } - async load():Promise { + async load():Promise { const {value: tree} = await Storage.get({key: `bookmarks[${this.accountId}].tree`}) const {value: highestId} = await Storage.get({key: `bookmarks[${this.accountId}].highestId`}) if (tree) { + const oldHash = this.bookmarksCache && await this.bookmarksCache.hash(true) this.bookmarksCache = Folder.hydrate(JSON.parse(tree)) + const newHash = await this.bookmarksCache.hash(true) this.highestId = parseInt(highestId) + return oldHash && oldHash !== newHash } else { await this.save() + return false } } diff --git a/src/ui/NativeApp.vue b/src/ui/NativeApp.vue index c743895dc7..dc8aa62311 100644 --- a/src/ui/NativeApp.vue +++ b/src/ui/NativeApp.vue @@ -30,7 +30,6 @@ export default { async created() { setInterval(() => { this.$store.dispatch(actions.LOAD_ACCOUNTS) - this.$store.dispatch(actions.LOAD_TREE) }, 5000) const controller = await Controller.getSingleton() controller.onLoad() diff --git a/src/ui/store/native/actions.js b/src/ui/store/native/actions.js index ee2313025b..6d4cea852f 100644 --- a/src/ui/store/native/actions.js +++ b/src/ui/store/native/actions.js @@ -29,8 +29,13 @@ export const actionsDefinition = { async [actions.LOAD_TREE]({ commit, dispatch, state }, id) { const account = await Account.get(id) const tree = await account.getResource() + const treeChanged = await tree.load() const rootFolder = await tree.getBookmarksTree(true) await commit(mutations.LOAD_TREE, rootFolder) + if (treeChanged) { + dispatch(actions.TRIGGER_SYNC, id) + dispatch(actions.LOAD_ACCOUNTS, id) + } }, async [actions.CREATE_BOOKMARK]({commit}, {accountId, bookmark}) { const account = await Account.get(accountId) diff --git a/src/ui/views/native/Tree.vue b/src/ui/views/native/Tree.vue index 46179202b5..e47bde9e1e 100644 --- a/src/ui/views/native/Tree.vue +++ b/src/ui/views/native/Tree.vue @@ -407,7 +407,7 @@ export default { }, breadcrumbs() { const folders = [this.currentFolder] - while (String(folders[folders.length - 1 ].id) !== String(this.tree.id)) { + while (this.tree && folders[folders.length - 1 ] && String(folders[folders.length - 1 ].id) !== String(this.tree.id)) { folders.push(this.findItem(folders[folders.length - 1 ].parentId, this.tree)) } return folders.reverse() @@ -429,6 +429,11 @@ export default { } }, }, + created() { + App.addListener('resume', () => { + this.$store.dispatch(actions.LOAD_TREE, this.$route.params.accountId) + }) + }, mounted() { this.$store.dispatch(actions.LOAD_TREE, this.$route.params.accountId) }, From 60a37647fa25e6a96b76e87f4ff083398ac3f1b1 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 05:03:08 +0000 Subject: [PATCH 18/33] Translate _locales/en/messages.json in ja 100% translated source file: '_locales/en/messages.json' on 'ja'. --- _locales/ja/messages.json | 57 ++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index 25872e3135..6b578afe5f 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -75,7 +75,7 @@ "message": "E025: ブックマークファイルの設定は、スラッシュ: '/' で始まってはいけません" }, "Error026": { - "message": "E026: HTTP ステータス {0}。ブックマークのフェッチに失敗しました" + "message": "E026: 同期プロセスがキャンセルされました" }, "Error027": { "message": "E027: 同期のプロセスが中断されました" @@ -107,6 +107,12 @@ "Error036": { "message": "E036: サーバーと同期する権限がありません" }, + "Error037": { + "message": "E036: リソースがロックされています" + }, + "Error038": { + "message": "E036: ローカルフォルダーを見つけられません" + }, "LabelWebdavurl": { "message": "WebDAV URL" }, @@ -126,11 +132,14 @@ "message": "ブックマークファイルのパス" }, "DescriptionBookmarksfile": { - "message": "WebDAV URL からのブックマークファイルの相対パス (パスのすべてのフォルダーがすでに存在していなければなりません) 例: personal_stuff/bookmarks.xbel" + "message": "WebDAV URL からのブックマークファイルの相対パス (パスのすべてのフォルダーが先に存在していなければなりません) 例: personal_stuff/bookmarks.xbel" }, "DescriptionBookmarksfilegoogle": { "message": "Googleドライブに保存するブックマークファイルの名前 (ドライブ内でファイル名が重複していないことを確認してください)" }, + "DescriptionBookmarksfilegit": { + "message": "Git リポジトリーのルートからのブックマークファイルの相対パス (パスのすべてのフォルダーが先に存在していなければなりません) 例: personal_stuff/bookmarks.xbel" + }, "LabelServerfolder": { "message": "サーバーの対象" }, @@ -216,7 +225,7 @@ "message": "キャッシュをリセット" }, "DescriptionResetcache": { - "message": "選択すると、キャッシュがリセットされ、次の同期実行でデータが削除されず、サーバーとローカルのブックマークがマージされるだけになります。" + "message": "選択するとキャッシュがリセットされ、次の同期実行でデータが削除されず、サーバーとローカルのブックマークがマージされるだけになります。" }, "LabelParallelsync": { "message": "同期の高速化" @@ -261,7 +270,7 @@ "message": "Nextcloud Bookmarks" }, "DescriptionAdapternextcloudfolders": { - "message": "「Nextcloud Bookmarks」のオプションは Nextclound のブックマークアプリとブックマークを同期します。http と ftp のブックマークのみ同期できます。お使いの Nextcloud でブックマークアプリを Nextcloud アプリストアからインストールしていることを確認してください。" + "message": "「Nextcloud Bookmarks」のオプションは Nextclound のブックマークアプリとブックマークを同期します。http、ftp、JavaScript のブックマークのみ同期できます。お使いの Nextcloud でブックマークアプリを Nextcloud アプリストアからインストールしていることを確認してください。" }, "LabelAdapternextcloud": { "message": "Nextcloud Bookmarks (レガシー)" @@ -351,6 +360,18 @@ "DescriptionGithubsponsors": { "message": "GitHub sponsors で定期的に寄付をしてプロジェクトを支援する" }, + "LabelPatreon": { + "message": "Patreon" + }, + "DescriptionPatreon": { + "message": "Patreon で定期的に寄付してプロジェクトを支援する" + }, + "LabelKofi": { + "message": "Kofi" + }, + "DescriptionKofi": { + "message": "Ko-fi で 1 回限りまたは定期的な寄付をしてプロジェクトを支援する" + }, "LegacyAdapterDeprecation": { "message": "このレガシープロファイルタイプは非推奨になり、間もなく削除されます。新しい Nextcloud の同期方法に変更してください。改善されたパフォーマンスと精度でななたをお待ちしています。" }, @@ -508,7 +529,7 @@ "message": "クライアント認証情報を送信する" }, "DescriptionClientcert": { - "message": "サーバーが認証にクライアント証明書または Cookie を要求する場合は、このオプションを有効にしてください。floccus は通常のブラウザーのプロファイルと Cookie を共有するため、予期せぬ副作用を引き起こす場合があります。" + "message": "サーバーが認証にクライアント証明書または Cookie を要求する場合は、このオプションを有効にしてください。floccus は通常のブラウザーセッションと Cookie を共有するため、予期せぬ副作用を引き起こす場合があります。" }, "LabelAllowredirects": { "message": "サーバー URL のリダイレクトを許可する" @@ -673,9 +694,33 @@ "message": "正常にプロファイルをインポートしました" }, "DescriptionSyncinprogress": { - "message": "進行中の同期。" + "message": "同期が進行中。" }, "DescriptionSyncscheduled": { "message": "このプロファイルはまもなく同期されます。他のデバイスまたはこのデバイス上の他のプロファイルの同期が完了するのを待っています。" + }, + "LabelAdaptergit": { + "message": "Git over HTTPS" + }, + "DescriptionAdaptergit": { + "message": "Git オプションは、提供された Git リポジトリー内のファイルにブックマークを保存して同期します。ウェブ UI はありませんが、Github、Gitlab、Gitea などのすべての Git ホスティングサーバーに対応しています。http、ftp、データ、ファイル、JavaScript のブックマークを同期できます。 " + }, + "LabelGiturl": { + "message": "リポジトリーの URL (HTTP)" + }, + "LabelGitbranch": { + "message": "Git ブランチ" + }, + "LabelTelemetry": { + "message": "自動エラーレポート" + }, + "DescriptionTelemetry": { + "message": "floccus は開発者に自動でエラーデータを送信できます。エラーデータは floccus のバグの迅速な発見と解消に役立ち、長期的には floccus のユーザー体験を向上させます。エラーレポートを有効にしても、floccus の開発者はあなたのブックマークを閲覧できません。" + }, + "LabelTelemetryenable": { + "message": "floccus の開発者にエラーデータを自動送信する" + }, + "LabelTelemetrydisable": { + "message": "floccus の開発者にエラーデータを送信しない" } } From 633594b727e30f9eada163b109448d2d20f9e9be Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 13 May 2024 16:17:49 +0200 Subject: [PATCH 19/33] Update ios files --- ios/App/App.xcodeproj/project.pbxproj | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 42a34e135d..cf6616b2e9 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -363,13 +363,13 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 5.0.8; + MARKETING_VERSION = 5.1.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.handmadeideas.floccus.new-bookmark"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "March 23 New Bookmark v2"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "floccus new Bookmark May 24"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -397,12 +397,12 @@ INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 5.0.8; + MARKETING_VERSION = 5.1.1; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.handmadeideas.floccus.new-bookmark"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "March 23 New Bookmark v2"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "floccus new Bookmark May 24"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -459,7 +459,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 5.0.8; + MARKETING_VERSION = 5.1.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -511,7 +511,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 5.0.8; + MARKETING_VERSION = 5.1.1; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -529,6 +529,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = BW96RCL7W9; INFOPLIST_FILE = App/Info.plist; @@ -536,12 +537,12 @@ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 5.0.8; + MARKETING_VERSION = 5.1.1; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = org.handmadeideas.floccus; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "March 23 v3"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "floccus May 24"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -558,6 +559,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = BW96RCL7W9; INFOPLIST_FILE = App/Info.plist; @@ -565,11 +567,11 @@ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 5.0.8; + MARKETING_VERSION = 5.1.1; PRODUCT_BUNDLE_IDENTIFIER = org.handmadeideas.floccus; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "March 23 v3"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "floccus May 24"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; From fb5a41e882d8e413ff02a39bc5644330e647bf34 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sun, 12 May 2024 18:31:34 +0200 Subject: [PATCH 20/33] fix(GoogleDrive): Catch 500 errors Signed-off-by: Marcel Klehr --- src/lib/adapters/GoogleDrive.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/adapters/GoogleDrive.ts b/src/lib/adapters/GoogleDrive.ts index 981afe3cc7..e4059d47c7 100644 --- a/src/lib/adapters/GoogleDrive.ts +++ b/src/lib/adapters/GoogleDrive.ts @@ -6,7 +6,7 @@ import Credentials from '../../../google-api.credentials.json' import { AuthenticationError, DecryptionError, FileUnreadableError, - GoogleDriveAuthenticationError, InterruptedSyncError, MissingPermissionsError, + GoogleDriveAuthenticationError, HttpError, InterruptedSyncError, MissingPermissionsError, NetworkError, OAuthTokenError, ResourceLockedError } from '../../errors/Error' @@ -379,6 +379,10 @@ export default class GoogleDriveAdapter extends CachingAdapter { throw new AuthenticationError() } + if (res.status >= 500) { + throw new HttpError(res.status, method) + } + return { status: res.status, json: () => Promise.resolve(res.data), From 4f6121522b2c1fc7015386093a68ab4de8f75b47 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 14 May 2024 14:07:05 +0200 Subject: [PATCH 21/33] v5.1.2 Signed-off-by: Marcel Klehr --- CHANGELOG.md | 14 ++++++++++++++ android/app/build.gradle | 4 ++-- html/index.html | 2 +- html/options.html | 2 +- manifest.chrome.json | 2 +- manifest.firefox.json | 2 +- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 9 files changed, 24 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3549e9f53a..8f3494fa8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,18 @@ # Changelog +## [5.1.2] - 2024-05-14 + +### Fixed +* fix(GoogleDrive): Catch 500 errors +* [native] fix: Reload tree on app resume +* fix(NextcloudBookmarks): Remove feature detection of 5yo features +* [native] fix(intent): Register intent activity properly +* feat(NextcloudBookmarks): Accept javascript: links +* fix(webpack): Don't set DEBUG to true in production +* fix(BrowserController#setStatusBadge): Don't throw when setting icon +* fix(Account#progressCallback): Don't error if syncProcess is not defined yet +* fix: Don't error in old Chrome versions if browser.permissions.contains fails +* fix: Wrap local tree fetch error +* fix(webpack): Split initial chunks to avoid AMO review complaining ## [5.1.1] - 2024-05-10 diff --git a/android/app/build.gradle b/android/app/build.gradle index d4e08a9ea5..e44f4e5c56 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "org.handmadeideas.floccus" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 5001001 - versionName "5.1.1" + versionCode 5001002 + versionName "5.1.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/html/index.html b/html/index.html index ac45f7cff8..8bf5521974 100644 --- a/html/index.html +++ b/html/index.html @@ -20,7 +20,7 @@ - + diff --git a/html/options.html b/html/options.html index ddd785f7f4..39e8efc7c7 100644 --- a/html/options.html +++ b/html/options.html @@ -29,7 +29,7 @@ - + diff --git a/manifest.chrome.json b/manifest.chrome.json index 32b64daf41..66ec957fa3 100644 --- a/manifest.chrome.json +++ b/manifest.chrome.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.1", + "version": "5.1.2", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", diff --git a/manifest.firefox.json b/manifest.firefox.json index 2be45a61d0..0224649a03 100644 --- a/manifest.firefox.json +++ b/manifest.firefox.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.1", + "version": "5.1.2", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", diff --git a/manifest.json b/manifest.json index 2be45a61d0..0224649a03 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.1", + "version": "5.1.2", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", diff --git a/package-lock.json b/package-lock.json index 0c3dcb4ee5..5665a3adaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "floccus", - "version": "5.1.1", + "version": "5.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "floccus", - "version": "5.1.1", + "version": "5.1.2", "license": "MPL-2.0", "dependencies": { "@byteowls/capacitor-oauth2": "5.x", diff --git a/package.json b/package.json index dc9caf2668..3129423cb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "floccus", - "version": "5.1.1-dev", + "version": "5.1.2", "description": "Sync your bookmarks privately across browsers and devices", "scripts": { "build": "gulp", From e73126d34c25c161852a903a4e009739f1c3804c Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 15 May 2024 08:30:15 +0200 Subject: [PATCH 22/33] Fix chrome build Signed-off-by: Marcel Klehr --- gulpfile.js | 18 ++++++++++++++++-- manifest.firefox.json | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 35c7c05c94..3c691ccf03 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -104,12 +104,25 @@ const js = function() { /* stats options */ }) ) - resolve() }) ) } +const fixupBgScript = async function () { + const bgScript = fs.readFileSync(paths.distJs + '/background-script.js', 'utf8') + const addition = ` +if ("undefined"!=typeof self && 'importScripts' in self) { + self.importScripts('./79.js') + self.importScripts('./88.js') + self.importScripts('./206.js') + self.importScripts('./895.js') + self.importScripts('./80.js') +} +` + fs.writeFileSync(paths.distJs + '/background-script.js', addition + bgScript) +} + const html = function() { return Promise.all([ gulp.src(paths.nativeHTML).pipe(gulp.dest('./dist/')), @@ -146,7 +159,7 @@ const thirdparty = gulp.parallel(mocha) const assets = gulp.parallel(html, thirdparty, icons) -const build = gulp.series(cleanJs, js, assets) +const build = gulp.series(cleanJs, js, fixupBgScript, assets) const main = gulp.series(build, native) @@ -243,6 +256,7 @@ exports.publish = publish exports.build = build exports.native = native exports.package = gulp.parallel(firefoxZip, chromeZip, xpi) +exports.fixupBgScript = fixupBgScript /* * Define default task that can be called by just running `gulp` from cli */ diff --git a/manifest.firefox.json b/manifest.firefox.json index 0224649a03..1d9e8a8f4e 100644 --- a/manifest.firefox.json +++ b/manifest.firefox.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.2", + "version": "5.1.2.1", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", From d9750051c66c1faf531fa48cf2334ec6960e5cc6 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 15 May 2024 10:07:04 +0200 Subject: [PATCH 23/33] chore: Update gulpfile Signed-off-by: Marcel Klehr --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 3c691ccf03..a871f4e1aa 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -255,7 +255,7 @@ exports.watch = gulp.series(cleanJs,gulp.parallel(assets, devjs), native, watch) exports.publish = publish exports.build = build exports.native = native -exports.package = gulp.parallel(firefoxZip, chromeZip, xpi) +exports.package = gulp.series(gulp.parallel(firefoxZip, chromeZip, xpi), crx) exports.fixupBgScript = fixupBgScript /* * Define default task that can be called by just running `gulp` from cli From 23bae6de731523c57d1b5c4ae10cfac705c12211 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 15 May 2024 10:07:37 +0200 Subject: [PATCH 24/33] fix: Unhandled error "Receiving end does not exist" Signed-off-by: Marcel Klehr --- src/lib/browser/BrowserController.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/browser/BrowserController.js b/src/lib/browser/BrowserController.js index a13ebc8aa8..a134e515d4 100644 --- a/src/lib/browser/BrowserController.js +++ b/src/lib/browser/BrowserController.js @@ -119,7 +119,11 @@ export default class BrowserController { const clientList = await self.clients.matchAll() clientList.forEach(client => client.postMessage({ type: 'status:update', params: [] })) } else { - browser.runtime.sendMessage({type: 'status:update', params: []}) + try { + browser.runtime.sendMessage({ type: 'status:update', params: [] }) + } catch (e) { + console.warning(e) + } } }) } From e991ae191b924b5bbfe4c6a9eeddf481d0ed9f19 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 16 May 2024 17:58:12 +0200 Subject: [PATCH 25/33] fix: Improve bulkImport performance by chunking Signed-off-by: Marcel Klehr --- src/lib/adapters/NextcloudBookmarks.ts | 2 +- src/lib/strategies/Default.ts | 139 +++++++++++++++++++------ 2 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index 04d6f2741b..2f614bd598 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -422,7 +422,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes throw new Error('Current server does not support bulk import') } if (folder.count() > 75) { - throw new Error('Refusing to bulk import more than 300 bookmarks') + throw new Error('Refusing to bulk import more than 75 bookmarks') } Logger.log('(nextcloud-folders)BULKIMPORT', { parentId, folder }) const parentFolder = this.tree.findFolder(parentId) diff --git a/src/lib/strategies/Default.ts b/src/lib/strategies/Default.ts index 540fde7c52..b3b7d2acc0 100644 --- a/src/lib/strategies/Default.ts +++ b/src/lib/strategies/Default.ts @@ -10,6 +10,7 @@ import { TAdapter } from '../interfaces/Adapter' import { CancelledSyncError, FailsafeError } from '../../errors/Error' import NextcloudBookmarksAdapter from '../adapters/NextcloudBookmarks' +import CachingAdapter from '../adapters/Caching' const ACTION_CONCURRENCY = 12 @@ -111,8 +112,12 @@ export default class SyncProcess { } countPlannedActions() { - this.actionsPlanned = this.serverPlan.getActions().length + this.localPlan.getActions().length - this.actionsPlanned += this.localPlan.getActions(ActionType.CREATE).map(action => action.payload.count()).reduce((a, i) => a + i, 0) + let actionsPlanned = this.serverPlan.getActions().length + this.localPlan.getActions().length + actionsPlanned += this.localPlan.getActions(ActionType.CREATE).map(action => action.payload.count()).reduce((a, i) => a + i, 0) + if (this.actionsPlanned < actionsPlanned) { + // only update if there is not more plans already + this.actionsPlanned = actionsPlanned + } } updateProgress():void { @@ -736,47 +741,115 @@ export default class SyncProcess { if (item instanceof Folder && ((action.payload instanceof Folder && action.payload.children.length) || (action.oldItem instanceof Folder && action.oldItem.children.length))) { if ('bulkImportFolder' in resource) { - try { - // Try bulk import - const imported = await resource.bulkImportFolder(item.id, (action.oldItem || action.payload) as Folder) - const newMappings = [] - const subScanner = new Scanner( - action.oldItem || action.payload, - imported, - (oldItem, newItem) => { - if (oldItem.type === newItem.type && oldItem.canMergeWith(newItem)) { - // if two items can be merged, we'll add mappings here directly - newMappings.push([oldItem, newItem.id]) - return true + if (action.payload.count() < 75 || this.server instanceof CachingAdapter) { + Logger.log('Attempting full bulk import') + try { + // Try bulk import with sub folders + const imported = await resource.bulkImportFolder(item.id, (action.oldItem || action.payload) as Folder) + const newMappings = [] + const subScanner = new Scanner( + action.oldItem || action.payload, + imported, + (oldItem, newItem) => { + if (oldItem.type === newItem.type && oldItem.canMergeWith(newItem)) { + // if two items can be merged, we'll add mappings here directly + newMappings.push([oldItem, newItem.id]) + return true + } + return false + }, + this.preserveOrder, + false, + ) + await subScanner.run() + await Parallel.each(newMappings, async ([oldItem, newId]) => { + await this.addMapping(resource, oldItem, newId) + }) + + done() + return + } catch (e) { + Logger.log('Bulk import failed, continuing with normal creation', e) + } + } else { + try { + // Try bulk import without sub folders + const tempItem = (action.oldItem ? action.oldItem.clone(false) : action.payload.clone(false)) as Folder + const bookmarks = tempItem.children.filter(child => child instanceof Bookmark) + while (bookmarks.length > 0) { + Logger.log('Attempting chunked bulk import') + tempItem.children = bookmarks.splice(0, 70) + const imported = await resource.bulkImportFolder(item.id, tempItem) + const newMappings = [] + const subScanner = new Scanner( + tempItem, + imported, + (oldItem, newItem) => { + if (oldItem.type === newItem.type && oldItem.canMergeWith(newItem)) { + // if two items can be merged, we'll add mappings here directly + newMappings.push([oldItem, newItem.id]) + return true + } + return false + }, + this.preserveOrder, + false, + ) + await subScanner.run() + await Parallel.each(newMappings, async([oldItem, newId]) => { + await this.addMapping(resource, oldItem, newId) + }) + } + + // create sub plan for the folders + + if (action.oldItem && action.oldItem instanceof Folder) { + const subPlan = new Diff + action.oldItem.children + .filter(child => child instanceof Folder) + .forEach((child) => { + const newAction : Action = { type: ActionType.CREATE, payload: child } + subPlan.commit(newAction) + plan.commit(newAction) + }) + const mappingsSnapshot = this.mappings.getSnapshot() + const mappedSubPlan = subPlan.map(mappingsSnapshot, targetLocation) + Logger.log('executing sub plan') + this.actionsPlanned += mappedSubPlan.getActions().length + await this.execute(resource, mappedSubPlan, targetLocation, null, true) + + if ('orderFolder' in resource && item.children.length > 1) { + // Order created items after the fact, as they've been created concurrently + plan.commit({ + type: ActionType.REORDER, + oldItem: action.payload, + payload: action.oldItem, + order: action.oldItem.children.map(i => ({ type: i.type, id: i.id })) + }) } - return false - }, - this.preserveOrder, - false, - ) - await subScanner.run() - await Parallel.each(newMappings, async([oldItem, newId]) => { - await this.addMapping(resource, oldItem, newId) - }) + } - done() - return - } catch (e) { - Logger.log('Bulk import failed, continuing with normal creation', e) + done() + return + } catch (e) { + Logger.log('Bulk import failed, continuing with normal creation', e) + } } } - // Create a sub plan + // Create a sub plan and create each child individually (worst performance) if (action.oldItem && action.oldItem instanceof Folder) { const subPlan = new Diff - action.oldItem.children.forEach((child) => { - const newAction : Action = { type: ActionType.CREATE, payload: child } - subPlan.commit(newAction) - plan.commit(newAction) - }) + action.oldItem.children + .forEach((child) => { + const newAction : Action = { type: ActionType.CREATE, payload: child } + subPlan.commit(newAction) + plan.commit(newAction) + }) const mappingsSnapshot = this.mappings.getSnapshot() const mappedSubPlan = subPlan.map(mappingsSnapshot, targetLocation) Logger.log('executing sub plan') + this.actionsPlanned += mappedSubPlan.getActions().length await this.execute(resource, mappedSubPlan, targetLocation, null, true) if ('orderFolder' in resource && item.children.length > 1) { From 5db36fbd39fb68d2964bb766681b156cdb779221 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 16 May 2024 17:59:00 +0200 Subject: [PATCH 26/33] fix: Limit concurrency for reorderings Signed-off-by: Marcel Klehr --- src/lib/strategies/Default.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/strategies/Default.ts b/src/lib/strategies/Default.ts index b3b7d2acc0..b869b87ebd 100644 --- a/src/lib/strategies/Default.ts +++ b/src/lib/strategies/Default.ts @@ -990,7 +990,7 @@ export default class SyncProcess { } reorderings.retract(action) this.updateProgress() - }) + }, ACTION_CONCURRENCY) } async addMapping(resource:TResource, item:TItem, newId:string|number):Promise { From f63e61d9465b069098effe7f95b9f461f3e2d886 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 16 May 2024 17:59:56 +0200 Subject: [PATCH 27/33] fix: Make Diff#inspect() output more readable Signed-off-by: Marcel Klehr --- src/lib/Diff.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/Diff.ts b/src/lib/Diff.ts index d3149d1d31..389275916f 100644 --- a/src/lib/Diff.ts +++ b/src/lib/Diff.ts @@ -189,10 +189,6 @@ export default class Diff { return batches } - inspect(): Action[] { - return this.getActions() - } - /** * on ServerToLocal: don't map removals * on LocalToServer: @@ -281,6 +277,12 @@ export default class Diff { }) } + inspect(depth = 0):string { + return 'Diff\n' + this.getActions().map((action: Action) => { + return `\nAction: ${action.type}\nPayload: ${action.payload.inspect()}${'index' in action ? `Index: ${action.index}\n` : ''}${'order' in action ? `Order: ${JSON.stringify(action.order, null, '\t')}` : ''}` + }).join('\n') + } + static fromJSON(json) { const diff = new Diff json.forEach((action: Action): void => { From 6c4ef489dcf0305e0d9c32659e238ebaf500b5b5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Fri, 17 May 2024 07:28:42 +0200 Subject: [PATCH 28/33] fix(NextcloudBookmarks#getExistingBookmarks): Don't use search-by-url for javascript links Signed-off-by: Marcel Klehr --- src/lib/adapters/NextcloudBookmarks.ts | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index 2f614bd598..9c837db3be 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -586,11 +586,28 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes }) } - /* - * This is pretty expensive! We need to wait until NcBookmarks has support for - * querying urls directly - */ async getExistingBookmark(url:string):Promise { + if (url.toLowerCase().startsWith('javascript:')) { + if (!this.hasFeatureJavascriptLinks) { + return false + } + const json = await this.sendRequest( + 'GET', + `index.php/apps/bookmarks/public/rest/v2/bookmark?page=-1&search[]=${encodeURIComponent( + 'javascript:' + )}` + ) + if (json.data.length) { + const bookmark = json.data.find(bookmark => bookmark.target === url) + if (bookmark) { + return {...bookmark, parentId: bookmark.folders[0], url} + } else { + return false + } + } else { + return false + } + } const json = await this.sendRequest( 'GET', `index.php/apps/bookmarks/public/rest/v2/bookmark?url=${encodeURIComponent( From df20961792ae633dfe264e30cd95c5a4bf635f90 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 08:32:03 +0000 Subject: [PATCH 29/33] Translate _locales/en/messages.json in gl 100% translated source file: '_locales/en/messages.json' on 'gl'. --- _locales/gl/messages.json | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/_locales/gl/messages.json b/_locales/gl/messages.json index 442ea1c204..79acba2332 100644 --- a/_locales/gl/messages.json +++ b/_locales/gl/messages.json @@ -107,6 +107,12 @@ "Error036": { "message": "E036: Faltan permisos para acceder ao servidor de sincronización" }, + "Error037": { + "message": "E036: O recurso está bloqueado" + }, + "Error038": { + "message": "E036: Non foi posíbel atopar o cartafol local" + }, "LabelWebdavurl": { "message": "URL de WebDAV" }, @@ -126,11 +132,14 @@ "message": "Ruta do ficheiro de marcadores" }, "DescriptionBookmarksfile": { - "message": "unha ruta ao ficheiro de marcadores relativo ao URL de WebDAV (xa deben existir todos os cartafoles da ruta). p. ex. cousas_persoais/bookmarks.xbel" + "message": "unha ruta ao ficheiro de marcadores relativa ao URL de WebDAV (xa deben existir todos os cartafoles da ruta). p. ex. cousas_persoais/bookmarks.xbel" }, "DescriptionBookmarksfilegoogle": { "message": "o nome do ficheiro de marcadores que residirá no seu Google Drive (asegúrese de que este nome é único no seu Drive)" }, + "DescriptionBookmarksfilegit": { + "message": "unha ruta ao ficheiro de marcadores relativa á raíz do repositorio de Git (xa deben existir todos os cartafoles da ruta). p. ex. cousas_persoais/bookmarks.xbel" + }, "LabelServerfolder": { "message": "Servidor de destino" }, @@ -261,7 +270,7 @@ "message": "Marcadores de Nextcloud" }, "DescriptionAdapternextcloudfolders": { - "message": "A opción «Marcadores de Nextcloud» sincroniza os marcadores coa aplicación Marcadores de Nextcloud. Só pode sincronizar os marcadores http e ftp. Asegúrese de ter instalado a aplicación Marcadores da tenda de aplicacións de Nextcloud na súa instancia de Nextcloud." + "message": "A opción «Marcadores de Nextcloud» sincroniza os marcadores coa aplicación Marcadores de Nextcloud. Só pode sincronizar os marcadores http, ftp e javascript. Asegúrese de ter instalada a aplicación Marcadores da tenda de aplicacións de Nextcloud na súa instancia de Nextcloud." }, "LabelAdapternextcloud": { "message": "Marcadores de Nextcloud (herdados)" @@ -689,5 +698,29 @@ }, "DescriptionSyncscheduled": { "message": "Este perfil sincronizarase en breve. Estamos a agardar que outros dispositivos seus ou outros perfís deste dispositivo rematen de sincronizar." + }, + "LabelAdaptergit": { + "message": "Git sobre HTTPS" + }, + "DescriptionAdaptergit": { + "message": "A opción Git sincroniza os seus marcadores almacenándoos nun ficheiro no repositorio de Git fornecido. Non hai unha interface de usuario web para esta opción, mais pode usala con calquera servidor de aloxamento de Git, como Github, Gitlab, Gitea, etc. Pode sincronizar os marcadores http, ftp, datos, ficheiros e javascript." + }, + "LabelGiturl": { + "message": "URL do repositorio usando HTTP" + }, + "LabelGitbranch": { + "message": "Póla de Git" + }, + "LabelTelemetry": { + "message": "Informe automatizado de erros" + }, + "DescriptionTelemetry": { + "message": "Floccus pode enviarme automaticamente os datos de erros a min, o desenvolvedor. Esta é unha gran axuda para descubrir e resolver os erros en floccus máis rápido e axudará a mellorar a súa experiencia con floccus a longo prazo. Aínda que estea activado o informe de erros, os desenvolvedores de floccus nunca poderán ver os seus marcadores." + }, + "LabelTelemetryenable": { + "message": "Envíiar automaticamente os datos de erros aos desenvolvedores de floccus" + }, + "LabelTelemetrydisable": { + "message": "Non enviar os datos de erros aos desenvolvedores de floccus" } } From 755c83cc3199de4f5d23827e2ea1c1d37c46eb36 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 18 May 2024 14:07:20 +0200 Subject: [PATCH 30/33] fix: Improve locking mechanism Signed-off-by: Marcel Klehr --- src/lib/adapters/Git.ts | 6 ++++-- src/lib/adapters/GoogleDrive.ts | 7 ++++++- src/lib/adapters/NextcloudBookmarks.ts | 9 +++++++-- src/lib/adapters/WebDav.ts | 19 +++++++++++++++---- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/lib/adapters/Git.ts b/src/lib/adapters/Git.ts index fd83bf7fa2..40bbfc4252 100644 --- a/src/lib/adapters/Git.ts +++ b/src/lib/adapters/Git.ts @@ -149,14 +149,16 @@ export default class GitAdapter extends CachingAdapter { throw new SlashError() } + if (this.lockingInterval) { + clearInterval(this.lockingInterval) + } if (needLock) { await this.obtainLock() + this.lockingInterval = setInterval(() => this.setLock(), LOCK_INTERVAL) // Set lock every minute } const status = await this.pullFromServer() - this.lockingInterval = setInterval(() => this.setLock(), LOCK_INTERVAL) // Set lock every minute - this.initialTreeHash = await this.bookmarksCache.hash(true) Logger.log('onSyncStart: completed') diff --git a/src/lib/adapters/GoogleDrive.ts b/src/lib/adapters/GoogleDrive.ts index e4059d47c7..2aa4fee5a1 100644 --- a/src/lib/adapters/GoogleDrive.ts +++ b/src/lib/adapters/GoogleDrive.ts @@ -263,7 +263,12 @@ export default class GoogleDriveAdapter extends CachingAdapter { }) this.bookmarksCache = XbelSerializer.deserialize(xmlDocText) - this.lockingInterval = setInterval(() => this.setLock(this.fileId), LOCK_INTERVAL) // Set lock every minute + if (this.lockingInterval) { + clearInterval(this.lockingInterval) + } + if (needLock) { + this.lockingInterval = setInterval(() => this.setLock(this.fileId), LOCK_INTERVAL) // Set lock every minute + } } else { this.resetCache() this.alwaysUpload = true diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index 9c837db3be..395903d1d4 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -39,6 +39,7 @@ export interface NextcloudBookmarksConfig { includeCredentials?: boolean allowRedirects?: boolean allowNetwork?: boolean + label?: string } interface IChildFolder { @@ -101,7 +102,7 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes getLabel():string { const data = this.getData() - return data.username.includes('@') ? data.username + ' on ' + new URL(data.url).hostname : data.username + '@' + new URL(data.url).hostname + return data.label || data.username.includes('@') ? data.username + ' on ' + new URL(data.url).hostname : data.username + '@' + new URL(data.url).hostname } acceptsBookmark(bm: Bookmark):boolean { @@ -137,15 +138,19 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes } } + if (this.lockingInterval) { + clearInterval(this.lockingInterval) + } if (needLock) { if (!(await this.acquireLock())) { throw new ResourceLockedError() } + this.lockingInterval = setInterval(() => !this.ended && this.acquireLock(), LOCK_INTERVAL) } this.canceled = false this.ended = false - this.lockingInterval = setInterval(() => !this.ended && this.acquireLock(), LOCK_INTERVAL) + } async onSyncComplete(): Promise { diff --git a/src/lib/adapters/WebDav.ts b/src/lib/adapters/WebDav.ts index 8d262ea9d3..cdb08178f0 100644 --- a/src/lib/adapters/WebDav.ts +++ b/src/lib/adapters/WebDav.ts @@ -92,7 +92,7 @@ export default class WebDavAdapter extends CachingAdapter { if (res.headers['Last-Modified']) { const date = new Date(res.headers['Last-Modified']) const dateLocked = date.valueOf() - if (dateLocked > Date.now() - LOCK_TIMEOUT) { + if (dateLocked + LOCK_TIMEOUT > Date.now()) { throw new ResourceLockedError() } } else { @@ -120,7 +120,15 @@ export default class WebDavAdapter extends CachingAdapter { 'text/html', 'I am a lock file' ) - await this.lockingPromise + try { + await this.lockingPromise + } catch (e) { + if (e instanceof HttpError && e.status === 423) { + this.locked = false + throw new ResourceLockedError() + } + throw e + } this.locked = true } @@ -253,8 +261,13 @@ export default class WebDavAdapter extends CachingAdapter { throw new SlashError() } + if (this.lockingInterval) { + clearInterval(this.lockingInterval) + } if (needLock) { await this.obtainLock() + this.lockingInterval = setInterval(() => this.setLock(), LOCK_INTERVAL) // Set lock every minute + } const resp = await this.pullFromServer() @@ -265,8 +278,6 @@ export default class WebDavAdapter extends CachingAdapter { } } - this.lockingInterval = setInterval(() => this.setLock(), LOCK_INTERVAL) // Set lock every minute - this.initialTreeHash = await this.bookmarksCache.hash(true) Logger.log('onSyncStart: completed') From 7ddc07f68ad60ac5580b1f0e72e1532630fe5fdd Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 18 May 2024 17:15:37 +0200 Subject: [PATCH 31/33] fix: Improve locking logic Signed-off-by: Marcel Klehr --- src/lib/adapters/NextcloudBookmarks.ts | 23 +++++------------------ src/lib/adapters/WebDav.ts | 2 +- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/lib/adapters/NextcloudBookmarks.ts b/src/lib/adapters/NextcloudBookmarks.ts index 395903d1d4..092a6c0394 100644 --- a/src/lib/adapters/NextcloudBookmarks.ts +++ b/src/lib/adapters/NextcloudBookmarks.ts @@ -141,16 +141,15 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes if (this.lockingInterval) { clearInterval(this.lockingInterval) } - if (needLock) { - if (!(await this.acquireLock())) { - throw new ResourceLockedError() - } - this.lockingInterval = setInterval(() => !this.ended && this.acquireLock(), LOCK_INTERVAL) + + // if needLock -- we always need it + if (!(await this.acquireLock())) { + throw new ResourceLockedError() } + this.lockingInterval = setInterval(() => !this.ended && this.acquireLock(), LOCK_INTERVAL) this.canceled = false this.ended = false - } async onSyncComplete(): Promise { @@ -229,18 +228,6 @@ export default class NextcloudBookmarksAdapter implements Adapter, BulkImportRes } } - async _getChildOrder(folderId:string|number, layers:number):Promise { - const childrenOrderJson = await this.sendRequest( - 'GET', - `index.php/apps/bookmarks/public/rest/v2/folder/${folderId}/childorder` + - (layers ? `?layers=${layers}` : '') - ) - if (!Array.isArray(childrenOrderJson.data)) { - throw new UnexpectedServerResponseError() - } - return childrenOrderJson.data - } - async _getChildFolders(folderId:string|number, layers = 0):Promise { const folderJson = await this.sendRequest( 'GET', diff --git a/src/lib/adapters/WebDav.ts b/src/lib/adapters/WebDav.ts index cdb08178f0..7c891fdff3 100644 --- a/src/lib/adapters/WebDav.ts +++ b/src/lib/adapters/WebDav.ts @@ -102,6 +102,7 @@ export default class WebDavAdapter extends CachingAdapter { if (res.status === 200) { // continue anyway + this.locked = true } else if (res.status === 404) { await this.setLock() } else { @@ -267,7 +268,6 @@ export default class WebDavAdapter extends CachingAdapter { if (needLock) { await this.obtainLock() this.lockingInterval = setInterval(() => this.setLock(), LOCK_INTERVAL) // Set lock every minute - } const resp = await this.pullFromServer() From 6fa6bafd07c2341c8e67260ecc5462e4d0b59d21 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 18 May 2024 17:18:04 +0200 Subject: [PATCH 32/33] [native] fix: set largeHeap to true on android + fix git settings Signed-off-by: Marcel Klehr --- android/app/src/main/AndroidManifest.xml | 3 ++- src/ui/views/native/Options.vue | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5c37adfd96..daeaf76cde 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" - android:networkSecurityConfig="@xml/network_security_config"> + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + @@ -54,11 +59,11 @@ import OptionsNextcloudBookmarks from '../../components/OptionsNextcloudBookmark import { actions } from '../../store/native' import { routes } from '../../NativeRouter' import OptionsGoogleDrive from '../../components/OptionsGoogleDrive' -// iport PathHelper from '../../../lib/PathHelper' +import OptionsGit from '../../components/OptionsGit.vue' export default { name: 'Options', - components: { OptionsGoogleDrive, OptionsNextcloudBookmarks, OptionsWebdav, OptionsFake }, + components: { OptionsGit, OptionsGoogleDrive, OptionsNextcloudBookmarks, OptionsWebdav, OptionsFake }, data() { return { drawer: false, From f3302c6c86d09acbf582a263525f2f2e4e9ee444 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Sat, 18 May 2024 20:28:57 +0200 Subject: [PATCH 33/33] v5.1.3 Signed-off-by: Marcel Klehr --- CHANGELOG.md | 13 +++++++++++++ android/app/build.gradle | 4 ++-- manifest.chrome.json | 2 +- manifest.firefox.json | 2 +- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f3494fa8d..38e6646094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,17 @@ # Changelog + +## [5.1.3] - 2024-05-18 + +### Fixed + +[native] fix: set largeHeap to true on android + fix git settings +fix: Improve locking logic +fix(NextcloudBookmarks#getExistingBookmarks): Don't use search-by-url for javascript links +fix: Make Diff#inspect() output more readable +fix: Limit concurrency for reorderings +fix: Improve bulkImport performance by chunking +fix: Unhandled error "Receiving end does not exist" + ## [5.1.2] - 2024-05-14 ### Fixed diff --git a/android/app/build.gradle b/android/app/build.gradle index e44f4e5c56..3072f0e8f2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "org.handmadeideas.floccus" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 5001002 - versionName "5.1.2" + versionCode 5001003 + versionName "5.1.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/manifest.chrome.json b/manifest.chrome.json index 66ec957fa3..53c349f024 100644 --- a/manifest.chrome.json +++ b/manifest.chrome.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.2", + "version": "5.1.3.1", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", diff --git a/manifest.firefox.json b/manifest.firefox.json index 1d9e8a8f4e..0ad1c2d95e 100644 --- a/manifest.firefox.json +++ b/manifest.firefox.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.2.1", + "version": "5.1.3.1", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", diff --git a/manifest.json b/manifest.json index 0224649a03..0ad1c2d95e 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "floccus bookmarks sync", "short_name": "floccus", - "version": "5.1.2", + "version": "5.1.3.1", "description": "__MSG_DescriptionExtension__", "icons": { "48": "icons/logo.png", diff --git a/package-lock.json b/package-lock.json index 5665a3adaf..8d4a004095 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "floccus", - "version": "5.1.2", + "version": "5.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "floccus", - "version": "5.1.2", + "version": "5.1.3", "license": "MPL-2.0", "dependencies": { "@byteowls/capacitor-oauth2": "5.x", diff --git a/package.json b/package.json index 3129423cb1..d9e8417c22 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "floccus", - "version": "5.1.2", + "version": "5.1.3", "description": "Sync your bookmarks privately across browsers and devices", "scripts": { "build": "gulp",