Skip to content

Commit 5f542b9

Browse files
committed
Various improvements:
- sync panel UI with current layout - improve and reduce frequency of save routine - add JS Doc comments - clarify popup code - move popup CSS to external file - minor bug fixes
1 parent b2e7844 commit 5f542b9

6 files changed

Lines changed: 214 additions & 112 deletions

File tree

src/background/background.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ chrome.runtime.onInstalled.addListener(function () {
55
chrome.tabs.query({}, function (tabs) {
66
tabs.filter(tab => tab.url && tab.url.startsWith('https://workflowy.com'))
77
.forEach(tab => {
8-
chrome.tabs.executeScript(tab.id, { file: 'content/multiflow.js' }, console.log)
8+
chrome.tabs.executeScript(tab.id, { file: 'content/multiflow.js' }, function (frames) {
9+
console.log('MultiFlow content script executed in', frames)
10+
})
911
})
1012
})
1113

src/content/multiflow.js

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-unused-vars */
12
// ---------------------------------------------------------------------------------------------------------------------
23
// HELPERS
34
// ---------------------------------------------------------------------------------------------------------------------
@@ -60,13 +61,25 @@ function addStyles (document, content) {
6061
// FRAME
6162
// ---------------------------------------------------------------------------------------------------------------------
6263

64+
/**
65+
* Frame class
66+
*
67+
* @property {Manager} manager
68+
* @property {number} index
69+
* @property {Window} window
70+
* @property {HTMLElement} element
71+
*/
6372
class Frame {
64-
constructor (parent, index) {
65-
this.parent = parent
73+
/**
74+
*
75+
* @param {Manager} manager
76+
* @param {number} index
77+
*/
78+
constructor (manager, index) {
79+
this.manager = manager
6680
this.index = index
6781
this.window = null
6882
this.element = null
69-
this.visible = true
7083
}
7184

7285
create (src) {
@@ -87,7 +100,7 @@ class Frame {
87100

88101
init () {
89102
// variables
90-
const parent = this.parent
103+
const manager = this.manager
91104
const frame = this.window
92105
const element = this.element
93106
const document = getDoc(element)
@@ -104,7 +117,7 @@ class Frame {
104117
// duplicate frame handler
105118
document.querySelector('.breadcrumbs').addEventListener('click', (event) => {
106119
if (event.target.matches('a:last-of-type') && isModifier(event)) {
107-
parent.loadNextFrame(this, frame.location.href)
120+
manager.loadNextFrame(this, frame.location.href)
108121
}
109122
})
110123

@@ -116,7 +129,7 @@ class Frame {
116129
? target
117130
: target.closest(selector)
118131
if (link && isModifier(event)) {
119-
parent.loadNextFrame(this, WF_URL + link.getAttribute('href'))
132+
manager.loadNextFrame(this, WF_URL + link.getAttribute('href'))
120133
stop(event)
121134
}
122135
}, { capture: true })
@@ -127,7 +140,7 @@ class Frame {
127140
if (el.tagName === 'A') {
128141
const href = el.getAttribute('href')
129142
if (href.startsWith(WF_URL)) {
130-
parent.load(this, href, isModifier(event))
143+
manager.load(this, href, isModifier(event))
131144
stop(event)
132145
}
133146
}
@@ -142,8 +155,8 @@ class Frame {
142155
button.innerHTML = '<div class="iconButton _pn8v4l"><svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke-linecap="round" stroke="#b7bcbf" style="position: relative;"><line x1="1" y1="1" x2="19" y2="19"></line><line x1="19" y1="1" x2="1" y2="19"></line></svg></div>'
143156
button.addEventListener('click', (event) => {
144157
isModifier(event)
145-
? parent.removeFrame(this)
146-
: parent.hideFrame(this)
158+
? manager.removeFrame(this)
159+
: manager.hideFrame(this)
147160
})
148161
}
149162
}
@@ -185,6 +198,12 @@ class Frame {
185198
// MANAGER
186199
// ---------------------------------------------------------------------------------------------------------------------
187200

201+
/**
202+
* Manager class
203+
*
204+
* @property {Frame[]} frames
205+
* @property {HTMLElement} container
206+
*/
188207
class Manager {
189208
constructor () {
190209
this.frames = []
@@ -252,51 +271,86 @@ class Manager {
252271
if (this.container) {
253272
const layout = document.body.getAttribute('data-layout')
254273
this.container.style.width = layout === 'fit-content'
255-
? (WF_WIDTH * manager.numVisible) + 'px'
274+
? (WF_WIDTH * this.numVisible) + 'px'
256275
: 'auto'
257276
}
277+
278+
// trigger save
279+
document.dispatchEvent(new Event('multiflow:update'))
258280
}
259281
}
260282

261283
// ---------------------------------------------------------------------------------------------------------------------
262284
// DATA
263285
// ---------------------------------------------------------------------------------------------------------------------
264286

287+
/**
288+
* Data class
289+
*/
265290
class Data {
291+
/**
292+
* Loads frame data
293+
* @returns {{urls: string[], titles: string[], layout: string}}
294+
*/
266295
load () {
267296
return JSON.parse(localStorage.getItem('multiflow') || '{}')
268297
}
269298

270-
save (frames) {
299+
/**
300+
* Save visible frames
301+
* @param {Frame[]} frames
302+
* @param {string} layout
303+
*/
304+
save (frames, layout) {
271305
// input data
272-
const input = manager.frames
306+
const input = frames
273307
.filter(frame => frame.isVisible())
274308
.map(frame => frame.getData())
275309

276310
// output data
277311
const urls = input.map(d => d.url)
278312
const titles = input.map(d => d.title)
279-
const data = { urls, titles }
313+
const data = { urls, titles, layout }
280314

281-
// check
282-
const title = 'MultiFlow: ' + titles.join(' + ')
283-
if (document.title !== title) {
284-
document.title = title
285-
localStorage.setItem('multiflow', JSON.stringify(data))
286-
}
315+
// save
316+
document.title = 'MultiFlow: ' + titles.join(' + ')
317+
localStorage.setItem('multiflow', JSON.stringify(data))
318+
319+
// return
320+
return data
287321
}
288322
}
289323

290324
// ---------------------------------------------------------------------------------------------------------------------
291325
// APP
292326
// ---------------------------------------------------------------------------------------------------------------------
293327

328+
/**
329+
* Application class
330+
*
331+
* @property {boolean} loadState
332+
* @property {Manager} manager
333+
* @property {Data} data
334+
*/
294335
class App {
295-
constructor () {
336+
/**
337+
* Application class
338+
*
339+
* @param {Manager} manager
340+
* @param {Data} data
341+
*/
342+
constructor (manager, data) {
296343
this.loadState = null
344+
this.manager = manager
345+
this.data = data
297346
}
298347

299348
start () {
349+
// only load if top window
350+
if (window !== window.top) {
351+
return
352+
}
353+
300354
// initialize
301355
if (!this.loadState) {
302356
this.loadState = 'initializing'
@@ -322,13 +376,15 @@ class App {
322376
this.loadState = 'loaded'
323377
location.replace(WF_URL + '/#multiflow')
324378

325-
// save
326-
setInterval(this.save, 1000)
379+
// saving
380+
const save = this.save.bind(this)
381+
document.addEventListener('multiflow:update', save)
382+
setInterval(save, 5000)
327383
}
328384

329385
setup () {
330386
document.write(`
331-
<html>
387+
<html lang="en">
332388
<head>
333389
<title>MultiFlow</title>
334390
<style>
@@ -380,31 +436,39 @@ class App {
380436
}
381437
</style>
382438
</head>
383-
<body class="multiflow">
439+
<body class="multiflow" data-layout="fit-screen">
384440
<main/>
385441
</body>
386442
</html>`)
387-
manager.container = document.querySelector('main')
443+
this.manager.container = document.querySelector('main')
388444
}
389445

390446
load () {
391-
const saved = data.load()
392-
const current = location.href
393-
const urls = saved.urls || [current, current]
394-
urls.forEach(url => manager.addFrame(url))
447+
if (!this.loadState) {
448+
const saved = this.data.load()
449+
const current = location.href
450+
const urls = saved.urls || [current, current]
451+
urls.forEach(url => this.manager.addFrame(url))
452+
this.setLayout(saved.layout)
453+
}
454+
else {
455+
console.warn('MultiFLow has already loaded data')
456+
}
395457
}
396458

397459
save () {
398-
const frames = manager.frames
399-
.filter(frame => frame.isVisible())
400-
.map(frame => frame.getData())
401-
data.save(frames)
460+
return this.data.save(this.manager.frames, this.getLayout())
402461
}
403462

404463
setLayout (value) {
405-
document.body.setAttribute('data-layout', value)
406-
manager.update()
407-
return true
464+
if (value) {
465+
document.body.setAttribute('data-layout', value)
466+
this.manager.update()
467+
}
468+
}
469+
470+
getLayout () {
471+
return document.body.getAttribute('data-layout')
408472
}
409473
}
410474

@@ -419,20 +483,23 @@ const WF_WIDTH = 700
419483
// instances
420484
const manager = new Manager()
421485
const data = new Data()
422-
const app = new App()
486+
const app = new App(manager, data)
423487

424488
// debug
425489
console.log('MultiFlow is ready...')
426490

427491
// commands
428492
chrome.runtime.onMessage.addListener(function (request = {}, _sender, callback) {
429-
switch (request.type) {
493+
switch (request.command) {
430494
case 'start':
431495
return callback(app.start())
432496

433-
case 'layout':
497+
case 'setLayout':
434498
return callback(app.setLayout(request.value))
435499

500+
case 'getLayout':
501+
return callback(app.getLayout())
502+
436503
default:
437504
// eslint-disable-next-line node/no-callback-literal
438505
return callback('Unknown request')

src/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "WorkFlowy MultiFlow",
33
"description" : "Navigate, organise & maintain context between separate WorkFlowy trees",
4-
"version": "1.01",
4+
"version": "1.2.0",
55
"manifest_version": 2,
66
"icons": {
77
"16": "assets/icon-16.png",

src/popup/popup.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
body {
2+
font-size: 15px;
3+
font-family: "Varela Round", AppleSystemUIFont, SansSerif, sans-serif;
4+
}
5+
6+
.screen {
7+
width: 160px;
8+
cursor: pointer;
9+
}
10+
11+
.panel {
12+
fill: #E6E6E6;
13+
}
14+
15+
.screen.selected .panel,
16+
.screen:hover .panel {
17+
fill: #c4deef;
18+
}
19+
20+
.screen .label {
21+
display: none;
22+
}
23+
24+
.screen:hover .label {
25+
display: block;
26+
}

0 commit comments

Comments
 (0)