Skip to content

Commit 0164780

Browse files
committed
Merge branch 'develop'
2 parents 81f2b9f + 48249b1 commit 0164780

26 files changed

Lines changed: 510 additions & 176 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## [5.5.3] - 2025-04-27
4+
5+
### Fixed
6+
* perf(GoogleDrive): Speed up "no changes" code path
7+
* perf(Folder#clone): Make Folder#clone use prototype inheritance to save ALOT of memory
8+
* perf(BrowserAccount): Don't use browser.bookmarks.getTree() if avoidable
9+
* refactor(isUsingBrowserTabs): Expose Resource#isUsingBrowserTabs
10+
* fix(CachingTreeWrapper): Allow changes to the live tree without disturbing the cache
11+
* fix(App#openInNewTab): Use browser.tabs to open new tab instead of window.open (Didn't work in Edge for Android)
12+
* fix(Default#applyAdditionFailsafe): Only kick in if at least 20 bookmarks are being added
13+
* fix(WebDAV): Add Depth header to PROPFIND for getting filesize
14+
315
## [5.5.2] - 2025-04-16
416

517
### Fixed

_locales/fr/messages.json

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
"message": "E028: Impossible de s'authentifier sur le serveur."
8585
},
8686
"Error029": {
87-
"message": "E029: Sécurité: La synchronisation en cours aurait effacé {0}% de vos signets. La synchronisation a été annulée. Désactivez la Sécurité dans les paramètres de votre compte si vous souhaitez tout de même synchroniser."
87+
"message": "E029: Sécurité: La synchronisation en cours aurait effacé {0}% de vos signets. La synchronisation a été annulée. Désactivez la sécurité dans les paramètres de votre compte si vous souhaitez tout de même synchroniser."
8888
},
8989
"Error030": {
9090
"message": "E030: Le déchiffrement de votre fichier de signets a échoué. Le mot de passe est peut être erronée ou le fichier corrompu."
@@ -119,6 +119,15 @@
119119
"Error040": {
120120
"message": "E040: Impossible de rechercher ce nom de fichier sur votre Google Drive"
121121
},
122+
"Error041": {
123+
"message": "E041: La taille du fichier de signets distant est différente de celle obtenue depuis le serveur. C'est peut-être dû à une erreur réseau. Si cette erreur persiste, contactez l'administrateur du serveur."
124+
},
125+
"Error042": {
126+
"message": "E042: La taille du fichier de signets distant n'a pu être obtenue. Il est impossible de vérifier que le fichier de signet a été complètement téléchargé. Si cette erreur persiste, contactez l'administrateur du serveur."
127+
},
128+
"Error043": {
129+
"message": "E043: Sécurité: La synchronisation en cours aurait ajouté {0}% à la quantité de vos signets. La synchronisation a été annulée. Désactivez la sécurité dans les paramètres de votre compte si vous souhaitez tout de même synchroniser."
130+
},
122131
"LabelWebdavurl": {
123132
"message": "URL WebDAV"
124133
},
@@ -303,7 +312,7 @@
303312
"message": "Journaux de débogage"
304313
},
305314
"LabelFunddevelopment": {
306-
"message": "Soutenir le développement"
315+
"message": "💸 Soutenir le développement"
307316
},
308317
"DescriptionFunddevelopment": {
309318
"message": "Le développement de floccus est possible grâce à un modèle d'abonnement volontaire. Si vous pensez que mon travail est utile, et s'il vous est possible d'y investir un montant même minime, merci de le soutenir. De plus, prenez aussi le temps d'aller noter floccus sur le Web Store de votre choix.\nMerci ! 💙"
@@ -505,22 +514,22 @@
505514
"message": "Sécurité"
506515
},
507516
"DescriptionFailsafe": {
508-
"message": "Parfois, des erreurs de configuration ou un bogue logiciel peuvent causer la suppression involontaire de données. Ces données deviennent alors irrécupérables. Pour empêcher cela, floccus n'effacera pas d'un seul coup plus de 50% de vos signets, à moins que vous ne désactiviez la Sécurité ici."
517+
"message": "Parfois, des erreurs de configuration ou des bogues logiciel peuvent causer la suppression involontaire de données, qui deviennent alors irrécupérables, ou la duplication involontaire de données, ce qui crée des conflits difficiles à résoudre. Pour empêcher cela, floccus n'effacera pas d'un seul coup plus de 20% de vos signets, ni n'ajoutera plus de 20% de la quantité de vos signet d'un seul coup, à moins que vous ne désactiviez la sécurité ici."
509518
},
510519
"LabelFailsafeon": {
511-
"message": "Activée. N'effacera pas plus de 50% de vos signets locaux sans vous en demander d'abord l'autorisation. (Recommandé)"
520+
"message": "Activée. N'enlèvera ou n'ajoutera pas plus 20% de/à la quantité de vos signets locaux sans vous en demander d'abord l'autorisation. (Recommandé)"
512521
},
513522
"LabelFailsafeoff": {
514-
"message": "Désactivée. Permet la suppression de plus de 50% de vos signets locaux sans vous en demander l'autorisation."
523+
"message": "Désactivée. Permet la suppression ou l'ajout de plus de 20% à la quantité de vos signets locaux sans vous en demander d'abord l'autorisation."
515524
},
516525
"StatusFailsafeoff": {
517-
"message": "Sécurité désactivée. Il y a un risque de perte de données involontaire. Il est recommandé de laisser active la Protection de secours dans les paramètres de votre profil."
526+
"message": "Sécurité désactivée. Il y a un risque de perte ou de duplication de données involontaire. Il est recommandé d'activer la sécurité dans les paramètres de votre profil."
518527
},
519528
"LabelAdaptergoogledrive": {
520529
"message": "Google Drive"
521530
},
522531
"DescriptionAdaptergoogledrive": {
523-
"message": "Synchronise vos signets à l'aide d'un fichier chiffré stocké sur votre Google Drive. Les signets HTTP, FTP, data, fichier ou JavaScript peuvent être synchronisés. Cette option supporte le chiffrement point à point."
532+
"message": "Synchronise vos signets à l'aide d'un fichier (potentiellement chiffré) stocké sur votre Google Drive. Les signets HTTP, FTP, data, fichier ou JavaScript peuvent être synchronisés. Cette option supporte le chiffrement point à point."
524533
},
525534
"LabelLogingoogle": {
526535
"message": "Se connecter avec Google"
@@ -673,7 +682,7 @@
673682
"message": "Profil créé"
674683
},
675684
"DescriptionAccountcreated": {
676-
"message": "Votre profil a été créé. Vous pouvez à présent fermer cet onglet."
685+
"message": "Votre profil a été créé. Vous pouvez à présent fermer cet onglet. Avertissement: synchronisation et sauvegarde sont des opérations différentes. Assurez-vous d'effectuer régulièrement des sauvegardes de vos signets."
677686
},
678687
"DescriptionNonhttps": {
679688
"message": "Vous accédez à un serveur qui utilise un protocole non sécurisé. Il est recommandé de n'utiliser que des serveurs compatibles avec HTTPS"
@@ -709,7 +718,7 @@
709718
"message": "Profil(s) importé(s) avec succès."
710719
},
711720
"DescriptionSyncinprogress": {
712-
"message": "Synchronisation en cours"
721+
"message": "Synchronisation en cours."
713722
},
714723
"DescriptionSyncscheduled": {
715724
"message": "Ce profil sera bientôt synchronisé. Nous attendons que vos autres appareils ou vos autres profils sur cet appareil aient terminé leur synchronisation."
@@ -718,7 +727,7 @@
718727
"message": "Git via HTTPS"
719728
},
720729
"DescriptionAdaptergit": {
721-
"message": "Synchronisez vos signets en les conservant dans un fichier situé sur un dépôt Git. Il n'y a pas d'interface Web accompagnant cette option, et vous pouvez l'utiliser avec n'importe quel serveur Git, tel que Github, Gitlab, Gitea, etc. Elle permet de synchroniser des signets HTTP, FTP, données, fichier et JavaScript. Cette option ne supporte pas le chiffrement point à point."
730+
"message": "L'option Git synchronise vos signets en les conservant dans un fichier situé sur un dépôt Git. Il n'y a pas d'interface web accompagnant cette option, mais vous pouvez l'utiliser avec n'importe quel serveur Git, tel que Github, Gitlab, Gitea, etc. Elle permet de synchroniser des signets HTTP, FTP, données, fichier et JavaScript. Cette option ne supporte pas le chiffrement point à point. Cette option n'est actuellement pas disponible pour l'application mobile."
722731
},
723732
"LabelGiturl": {
724733
"message": "URL du dépôt utilisant HTTP"
@@ -787,7 +796,7 @@
787796
"message": "Ce signet existe déjà dans le profil sélectionné"
788797
},
789798
"LabelReportproblem": {
790-
"message": "Rapporter ce probleme"
799+
"message": "Signaler ce problème"
791800
},
792801
"DescriptionReportproblem": {
793802
"message": "Si vous désirez contacter directement les développeurs pour discuter d'un problème, vous pouvez le faire ici:"
@@ -818,5 +827,8 @@
818827
},
819828
"DescriptionAutosync": {
820829
"message": "Activer la synchronisation automatique déclenchera automatiquement une synchronisation, à un intervalle de temps configurable, ainsi qu'à la demande, quelques secondes après chaque changement. Si vous n'activez pas la synchronisation automatique, vous devrez la déclencher manuellement en cliquant sur le bouton de synchronisation."
830+
},
831+
"LabelOpeninnewtab": {
832+
"message": "Ouvrir cette vue dans un nouvel onglet"
821833
}
822834
}

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ android {
77
applicationId "org.handmadeideas.floccus"
88
minSdkVersion rootProject.ext.minSdkVersion
99
targetSdkVersion rootProject.ext.targetSdkVersion
10-
versionCode 5005002
11-
versionName "5.5.2"
10+
versionCode 5005003
11+
versionName "5.5.3"
1212
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1313
aaptOptions {
1414
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

manifest.chrome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 3,
33
"name": "floccus bookmarks sync",
44
"short_name": "floccus",
5-
"version": "5.5.2",
5+
"version": "5.5.3",
66
"description": "__MSG_DescriptionExtension__",
77
"icons": {
88
"48": "icons/logo.png",

manifest.firefox.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 2,
33
"name": "floccus bookmarks sync",
44
"short_name": "floccus",
5-
"version": "5.5.2",
5+
"version": "5.5.3",
66
"description": "__MSG_DescriptionExtension__",
77
"icons": {
88
"48": "icons/logo.png",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "floccus",
3-
"version": "5.5.2",
3+
"version": "5.5.3",
44
"description": "Sync your bookmarks privately across browsers and devices",
55
"scripts": {
66
"build": "NODE_OPTIONS=--max-old-space-size=5000 gulp",

src/lib/Account.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { isTest } from './isTest'
1414
import CachingAdapter from './adapters/Caching'
1515
import * as Sentry from '@sentry/vue'
1616
import AsyncLock from 'async-lock'
17+
import CachingTreeWrapper from './CachingTreeWrapper'
1718

1819
declare const DEBUG: boolean
1920

@@ -163,7 +164,7 @@ export default class Account {
163164
try {
164165
if (this.getData().syncing || this.syncing) return
165166

166-
const localResource = await this.getResource()
167+
const localResource = new CachingTreeWrapper(await this.getResource())
167168
if (!(await this.server.isAvailable()) || !(await localResource.isAvailable())) return
168169

169170
Logger.log('Starting sync process for account ' + this.getLabel())
@@ -283,9 +284,8 @@ export default class Account {
283284
await this.setData({ scheduled: false, syncing: 1 })
284285

285286
// update cache
286-
const cache = (await localResource.getBookmarksTree()).clone(false)
287+
const cache = (await localResource.getCacheTree()).clone(false)
287288
this.syncProcess.filterOutUnacceptedBookmarks(cache)
288-
this.syncProcess.filterOutUnmappedItems(cache, await mappings.getSnapshot())
289289
await this.storage.setCache(cache)
290290

291291
if (this.server.onSyncComplete) {

src/lib/CacheTree.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import CachingAdapter from './adapters/Caching'
2+
import { IResource } from './interfaces/Resource'
3+
import { Folder, ItemLocation, TItemLocation } from './Tree'
4+
5+
export default class CacheTree extends CachingAdapter implements IResource<typeof ItemLocation.LOCAL> {
6+
protected location: TItemLocation = ItemLocation.LOCAL
7+
8+
constructor() {
9+
super({})
10+
}
11+
12+
public setTree(tree: Folder<typeof ItemLocation.LOCAL>) {
13+
this.bookmarksCache = tree.clone(false)
14+
this.bookmarksCache.createIndex()
15+
}
16+
17+
async getBookmarksTree(): Promise<Folder<typeof ItemLocation.LOCAL>> {
18+
const tree = await super.getBookmarksTree()
19+
tree.createIndex()
20+
return tree as Folder<typeof ItemLocation.LOCAL>
21+
}
22+
23+
isAvailable(): Promise<boolean> {
24+
return Promise.resolve(true)
25+
}
26+
}

src/lib/CachingTreeWrapper.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { CachingResource, OrderFolderResource } from './interfaces/Resource'
2+
import { Bookmark, Folder, ItemLocation } from './Tree'
3+
import CacheTree from './CacheTree'
4+
import Ordering from './interfaces/Ordering'
5+
6+
export default class CachingTreeWrapper implements OrderFolderResource<typeof ItemLocation.LOCAL>, CachingResource<typeof ItemLocation.LOCAL> {
7+
private innerTree: OrderFolderResource<typeof ItemLocation.LOCAL>
8+
private cacheTree: CacheTree
9+
10+
constructor(innerTree: OrderFolderResource<typeof ItemLocation.LOCAL>) {
11+
this.innerTree = innerTree
12+
this.cacheTree = new CacheTree()
13+
}
14+
15+
async getBookmarksTree(): Promise<Folder<typeof ItemLocation.LOCAL>> {
16+
const tree = await this.innerTree.getBookmarksTree()
17+
this.cacheTree.setTree(tree)
18+
return tree
19+
}
20+
21+
async createBookmark(bookmark:Bookmark<typeof ItemLocation.LOCAL>): Promise<string|number> {
22+
const id = await this.innerTree.createBookmark(bookmark)
23+
const cacheId = await this.cacheTree.createBookmark(bookmark.copy(false))
24+
const cacheBookmark = this.cacheTree.bookmarksCache.findBookmark(cacheId)
25+
cacheBookmark.id = id
26+
cacheBookmark.parentId = bookmark.parentId
27+
this.cacheTree.bookmarksCache.createIndex()
28+
return id
29+
}
30+
31+
async updateBookmark(bookmark:Bookmark<typeof ItemLocation.LOCAL>):Promise<void> {
32+
await this.innerTree.updateBookmark(bookmark)
33+
await this.cacheTree.updateBookmark(bookmark.copy(false))
34+
}
35+
36+
async removeBookmark(bookmark:Bookmark<typeof ItemLocation.LOCAL>): Promise<void> {
37+
await this.innerTree.removeBookmark(bookmark)
38+
await this.cacheTree.removeBookmark(bookmark)
39+
}
40+
41+
async createFolder(folder:Folder<typeof ItemLocation.LOCAL>): Promise<string|number> {
42+
const id = await this.innerTree.createFolder(folder)
43+
const cacheId = await this.cacheTree.createFolder(folder.copy(false))
44+
const cacheFolder = this.cacheTree.bookmarksCache.findFolder(cacheId)
45+
cacheFolder.id = id
46+
cacheFolder.parentId = folder.parentId
47+
this.cacheTree.bookmarksCache.createIndex()
48+
return id
49+
}
50+
51+
async orderFolder(id:string|number, order:Ordering<typeof ItemLocation.LOCAL>): Promise<void> {
52+
await this.innerTree.orderFolder(id, order)
53+
await this.cacheTree.orderFolder(id, order)
54+
}
55+
56+
async updateFolder(folder:Folder<typeof ItemLocation.LOCAL>): Promise<void> {
57+
await this.innerTree.updateFolder(folder)
58+
await this.cacheTree.updateFolder(folder.copy(false))
59+
}
60+
61+
async removeFolder(folder:Folder<typeof ItemLocation.LOCAL>): Promise<void> {
62+
await this.innerTree.removeFolder(folder)
63+
await this.cacheTree.removeFolder(folder)
64+
}
65+
66+
isAvailable(): Promise<boolean> {
67+
return this.innerTree.isAvailable()
68+
}
69+
70+
async isUsingBrowserTabs() {
71+
return this.innerTree.isUsingBrowserTabs?.()
72+
}
73+
74+
getCacheTree(): Promise<Folder<typeof ItemLocation.LOCAL>> {
75+
return this.cacheTree.getBookmarksTree()
76+
}
77+
}

src/lib/Diff.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,16 +200,16 @@ export default class Diff<L1 extends TItemLocation, L2 extends TItemLocation, A
200200
const newId = action.payload.id
201201
newAction = {
202202
...action,
203-
payload: action.payload.cloneWithLocation(false, targetLocation),
204-
oldItem: action.oldItem.cloneWithLocation(false, action.payload.location)
203+
payload: action.payload.copyWithLocation(false, targetLocation),
204+
oldItem: action.oldItem.copyWithLocation(false, action.payload.location)
205205
}
206206
newAction.payload.id = oldId
207207
newAction.oldItem.id = newId
208208
} else {
209209
newAction = {
210210
...action,
211-
payload: action.payload.cloneWithLocation(false, targetLocation),
212-
oldItem: action.payload.clone(false)
211+
payload: action.payload.copyWithLocation(false, targetLocation),
212+
oldItem: action.payload.copy(false)
213213
}
214214
newAction.payload.id = Mappings.mapId(mappingsSnapshot, action.payload, targetLocation)
215215
}
@@ -253,8 +253,8 @@ export default class Diff<L1 extends TItemLocation, L2 extends TItemLocation, A
253253
return this.getActions().map((action: A) => {
254254
return {
255255
...action,
256-
payload: action.payload.clone(false),
257-
oldItem: action.oldItem && action.oldItem.clone(false),
256+
payload: action.payload.copy(false),
257+
oldItem: action.oldItem && action.oldItem.copy(false),
258258
}
259259
})
260260
}

0 commit comments

Comments
 (0)