Skip to content

Commit e2b8271

Browse files
committed
Merge branch 'develop'
2 parents 533ff7d + 37e7a53 commit e2b8271

File tree

11 files changed

+93
-61
lines changed

11 files changed

+93
-61
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## v2.2.0
2+
- NEW: Map local sync folder to a specific server-side folder
3+
- FIX: Performance improvements for Firefox
4+
- FIX: Race condition removed that would cause issues because same account would be synced twice in parallel
5+
16
## v2.1.0
27
- NEW: Allow using an extension key to secure entered credentials
38
- FIX: Various fixes for Firefox

ISSUE_TEMPLATE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
<!--
22
Hello there! Thank you for providing feedback!
33
Please answer the below questions to allow for as smooth a process of tackling the issue as possible :)
4+
(Note: Everything surrounded by arrows, like this text for example, will not be visible.)
45
-->
56

67
### Software versions
78
<!-- Please provide the versions of the following software products in your set up -->
89

9-
* Browser:
10-
* Nextcloud:
11-
* Nextcloud Bookmarks app:
12-
* Floccus:
10+
* Browser:
11+
* Nextcloud:
12+
* Nextcloud Bookmarks app:
13+
* Floccus:
1314

1415
### Steps to reproduce
1516
<!-- What did you do? Be as specific as possible -->
1617
1. ...
17-
2.
18+
2.
1819

1920
### Expected outcome
2021
<!-- What did you think was going to happen or what do you think should have happened? -->

manifest.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",
44
"short_name": "floccus",
5-
"version": "2.1.0",
5+
"version": "2.2.0",
66
"description": "Sync your bookmarks with nextcloud",
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": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Sync your bookmarks with nextcloud",
55
"main": "index.js",
66
"scripts": {

src/entries/options.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,13 @@ function renderAccounts (accounts, secured) {
109109
})
110110
}</div>
111111
<a href="" className="btn" id="addaccount" ev-click={() => {
112-
Account.create({type: 'nextcloud', url: 'http://example.org', username: 'bob', password: 'password'})
112+
Account.create({
113+
type: 'nextcloud'
114+
, url: 'http://example.org'
115+
, username: 'bob'
116+
, password: 'password'
117+
, serverRoot: ''
118+
})
113119
.then(() => triggerRender())
114120
}}>Add account</a>
115121
<div className="security">

src/lib/Account.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ export default class Account {
1111
let storage = new AccountStorage(id)
1212
let background = await browser.runtime.getBackgroundPage()
1313
let data = await storage.getAccountData(background.controller.key)
14-
let localRoot = data.localRoot
15-
let tree = new Tree(storage, localRoot)
14+
let tree = new Tree(storage, data.localRoot, data.serverRoot)
1615
return new Account(id, storage, Adapter.factory(data), tree)
1716
}
1817

@@ -64,6 +63,10 @@ export default class Account {
6463
...ctl
6564
, update: (data) => {
6665
if (JSON.stringify(data) === JSON.stringify(originalData)) return
66+
if (originalData.serverRoot !== data.serverRoot) {
67+
this.storage.initCache()
68+
this.storage.initMappings()
69+
}
6770
ctl.update(data)
6871
}
6972
}
@@ -88,7 +91,7 @@ export default class Account {
8891
}
8992
await this.storage.initMappings()
9093
await this.storage.initCache()
91-
this.tree = new Tree(this.storage, accData.localRoot)
94+
this.tree = new Tree(this.storage, accData.localRoot, accData.serverRoot)
9295
}
9396

9497
async isInitialized () {
@@ -124,7 +127,10 @@ export default class Account {
124127
// Server handles existing URLs that we think are new, client handles new URLs that are bookmarked twice locally
125128
await this.sync_createOnServer(mappings)
126129

127-
let serverList = await this.server.pullBookmarks()
130+
let serverRoot = this.getData().serverRoot
131+
let serverList = (await this.server.pullBookmarks())
132+
.filter(bm => serverRoot ? bm.path.indexOf(serverRoot) === 0 : true)
133+
128134
// deletes everything locally that is not new but doesn't exist on the server anymore
129135
await this.sync_deleteFromTree(serverList)
130136
// Goes through server's list and updates creates things locally as needed
@@ -179,9 +185,10 @@ export default class Account {
179185
// ignore this bookmark as it's not supported by the server
180186
return
181187
}
182-
bookmark.id = serverMark.id
183-
await this.storage.addToMappings(bookmark)
184-
await this.storage.addToCache(bookmark.localId, await serverMark.hash())
188+
serverMark.localId = bookmark.localId
189+
await this.tree.updateNode(serverMark)
190+
await this.storage.addToMappings(serverMark)
191+
await this.storage.addToCache(serverMark.localId, await serverMark.hash())
185192
},
186193
BATCH_SIZE
187194
)

src/lib/Bookmark.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export default class Bookmark {
99
this.path = path
1010
}
1111

12+
getLocalPath (serverRoot) {
13+
return this.path.substr(serverRoot.length)
14+
}
15+
1216
async hash () {
1317
if (!this.hashValue) {
1418
this.hashValue = Bookmark.murmur2(JSON.stringify({

src/lib/Controller.js

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class AlarmManger {
2727

2828
export default class Controller {
2929
constructor () {
30-
this.syncing = {}
3130
this.schedule = {}
3231

3332
this.alarms = new AlarmManger(this)
@@ -126,7 +125,7 @@ export default class Controller {
126125
// Filter out any accounts that are not tracking the bookmark
127126
.filter((account, i) => (trackingAccountsFilter[i]))
128127
// Filter out any accounts that are presently syncing
129-
.filter(account => !this.syncing[account.id])
128+
.filter(account => !account.getData().syncing)
130129

131130
// We should now sync all accounts that are involved in this change (2 at max)
132131
accountsToSync.forEach((account) => {
@@ -142,7 +141,7 @@ export default class Controller {
142141

143142
const containingAccount = await Account.getAccountContainingLocalId(localId, ancestors, allAccounts)
144143
if (containingAccount &&
145-
!this.syncing[containingAccount.id] &&
144+
!containingAccount.getData().syncing &&
146145
!accountsToSync.some(acc => acc.id === containingAccount.id)) {
147146
this.scheduleSyncAccount(containingAccount.id)
148147
}
@@ -155,29 +154,21 @@ export default class Controller {
155154
this.schedule[accountId] = setTimeout(() => this.syncAccount(accountId), INACTIVITY_TIMEOUT)
156155
}
157156

158-
syncAccount (accountId) {
157+
async syncAccount (accountId) {
159158
if (!this.enabled) {
160159
return
161160
}
162-
if (this.syncing[accountId]) {
163-
return this.syncing[accountId].then(() => {
164-
return this.syncAccount(accountId)
165-
})
161+
let account = await Account.get(accountId)
162+
if (account.getData().syncing) {
163+
return
166164
}
167-
this.syncing[accountId] = Account.get(accountId)
168-
.then((account) => {
169-
setTimeout(() => this.updateBadge(), 500)
170-
return account.sync()
171-
})
172-
.then(() => {
173-
this.syncing[accountId] = false
174-
this.updateBadge()
175-
}, (error) => {
176-
console.error(error)
177-
this.syncing[accountId] = false
178-
this.updateBadge()
179-
})
180-
return this.syncing[accountId]
165+
setTimeout(() => this.updateBadge(), 500)
166+
try {
167+
await account.sync()
168+
} catch (error) {
169+
console.error(error)
170+
}
171+
this.updateBadge()
181172
}
182173

183174
async updateBadge () {

src/lib/Tree.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ const treeLock = new AsyncLock()
77
const reverseStr = (str) => str.split('').reverse().join('')
88

99
export default class Tree {
10-
constructor (storage, rootId) {
10+
constructor (storage, rootId, serverRoot) {
1111
this.rootId = rootId
12+
this.serverRoot = serverRoot
1213
this.storage = storage
1314
}
1415

@@ -48,14 +49,14 @@ export default class Tree {
4849
, node.id
4950
, node.url
5051
, node.title
51-
, parentPath || '/' // only root has a trailing slash
52+
, parentPath
5253
)
5354
return
5455
}
55-
const descendantPath = (parentPath || '') + '/' + node.title.replace(/[/]/g, '\\/') // other paths don't have a trailing slash
56+
const descendantPath = parentPath + '/' + node.title.replace(/[/]/g, '\\/') // other paths don't have a trailing slash
5657
node.children.map((node) => recurse(node, descendantPath))
5758
}
58-
tree.children.forEach(node => recurse(node))
59+
tree.children.forEach(node => recurse(node, this.serverRoot))
5960
}
6061

6162
getBookmarkByLocalId (localId) {
@@ -79,7 +80,7 @@ export default class Tree {
7980
throw new Error('trying to create a node for a bookmark that already has one')
8081
}
8182

82-
const parentId = await this.mkdirpPath(bookmark.path)
83+
const parentId = await this.mkdirpPath(bookmark.getLocalPath(this.serverRoot))
8384
const node = await browser.bookmarks.create({
8485
parentId
8586
, title: bookmark.title
@@ -102,7 +103,7 @@ export default class Tree {
102103
title: bookmark.title
103104
, url: bookmark.url
104105
})
105-
const parentId = await this.mkdirpPath(bookmark.path)
106+
const parentId = await this.mkdirpPath(bookmark.getLocalPath(this.serverRoot))
106107
await browser.bookmarks.move(bookmark.localId, {parentId})
107108
}
108109

@@ -170,11 +171,11 @@ export default class Tree {
170171
ancestors = ancestors.slice(ancestors.indexOf(relativeToRoot) + 1)
171172
}
172173

173-
return '/' + (await Promise.all(
174+
return (await Promise.all(
174175
ancestors
175176
.map(async ancestor => {
176177
try {
177-
let bms = await browser.bookmarks.getSubTree(ancestor)
178+
let bms = await browser.bookmarks.get(ancestor)
178179
let bm = bms[0]
179180
return bm.title.replace(/[/]/g, '\\/')
180181
} catch (e) {
@@ -225,7 +226,7 @@ export default class Tree {
225226
return path
226227
}
227228
path.unshift(localId)
228-
let bms = await browser.bookmarks.getSubTree(localId)
229+
let bms = await browser.bookmarks.get(localId)
229230
let bm = bms[0]
230231
if (bm.parentId === localId) {
231232
return path // might be that the root is circular

src/lib/adapters/Nextcloud.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,29 @@ export default class NextcloudAdapter {
2323

2424
renderOptions (ctl, rootPath) {
2525
let data = this.getData()
26+
const saveTimeout = 1000
2627
let onchangeURL = (e) => {
2728
if (this.saveTimeout) clearTimeout(this.saveTimeout)
28-
this.saveTimeout = setTimeout(() => ctl.update({...data, url: e.target.value}), 300)
29+
this.saveTimeout = setTimeout(() => ctl.update({...data, url: e.target.value}), saveTimeout)
2930
}
3031
let onchangeUsername = (e) => {
3132
if (this.saveTimeout) clearTimeout(this.saveTimeout)
32-
this.saveTimeout = setTimeout(() => ctl.update({...data, username: e.target.value}), 300)
33+
this.saveTimeout = setTimeout(() => ctl.update({...data, username: e.target.value}), saveTimeout)
3334
}
3435
let onchangePassword = (e) => {
3536
if (this.saveTimeout) clearTimeout(this.saveTimeout)
36-
this.saveTimeout = setTimeout(() => ctl.update({...data, password: e.target.value}), 300)
37+
this.saveTimeout = setTimeout(() => ctl.update({...data, password: e.target.value}), saveTimeout)
38+
}
39+
let onchangeServerRoot = (e) => {
40+
if (this.saveTimeout) clearTimeout(this.saveTimeout)
41+
this.saveTimeout = setTimeout(() => {
42+
let val = e.target.value
43+
if (val[val.length - 1] === '/') {
44+
val = val.substr(0, val.length - 1)
45+
e.target.value = val
46+
}
47+
ctl.update({...data, serverRoot: e.target.value})
48+
}, saveTimeout)
3749
}
3850
return <div className="account">
3951
<form>
@@ -44,11 +56,16 @@ export default class NextcloudAdapter {
4456
</tr>
4557
<tr>
4658
<td><label for="username">User name:</label></td>
47-
<td><input value={new InputInitializeHook(data.username)} type="text" className="username" name="password" ev-keyup={onchangeUsername} ev-blur={onchangeUsername}/></td>
59+
<td><input value={new InputInitializeHook(data.username)} type="text" className="username" name="username" ev-keyup={onchangeUsername} ev-blur={onchangeUsername}/></td>
4860
</tr>
4961
<tr>
5062
<td><label for="password">Password:</label></td>
51-
<td><input value={new InputInitializeHook(data.password)} type="password" className="password" name="password" ev-keydown={onchangePassword} ev-blur={onchangePassword}/></td></tr>
63+
<td><input value={new InputInitializeHook(data.password)} type="password" className="password" name="password" ev-keydown={onchangePassword} ev-blur={onchangePassword}/></td>
64+
</tr>
65+
<tr>
66+
<td><label for="serverRoot">Server path:</label></td>
67+
<td><input value={new InputInitializeHook(data.serverRoot || '')} type="text" className="serverRoot" name="serverRoot" placeholder="Default: root folder Example: /my/subfolder" ev-keyup={onchangeServerRoot} ev-blur={onchangeServerRoot}/></td>
68+
</tr>
5269
<tr><td></td><td>
5370
<span className="status">{
5471
data.syncing
@@ -78,7 +95,7 @@ export default class NextcloudAdapter {
7895
<div className="options">
7996
<formgroup>
8097
<h4>Sync folder</h4>
81-
<input type="text" disabled value={rootPath} /><br/>
98+
<input type="text" disabled placeholder="*Root folder*" value={rootPath} /><br/>
8299
<a href="" title="Reset synchronized folder to create a new one" className={'btn resetRoot ' + (data.syncing ? 'disabled' : '')} ev-click={() => {
83100
!data.syncing && ctl.update({...data, localRoot: null})
84101
}}>Reset</a>
@@ -342,7 +359,7 @@ export default class NextcloudAdapter {
342359
static getPathTagFromTags (tags) {
343360
return (tags || [])
344361
.filter(tag => tag.indexOf(TAG_PREFIX) === 0)
345-
.concat([this.convertPathToTag('/')])[0]
362+
.concat([this.convertPathToTag('')])[0]
346363
}
347364

348365
static convertPathToTag (path) {

0 commit comments

Comments
 (0)