git ssb

16+

Dominic / patchbay



Commit f282bf4a9ac0e60ba0de219dad5704df86000ec5

Merge branch 'master' into async_router (and fix undefined / null

problem)
mix irving committed on 2/18/2018, 9:33:17 PM
Parent: 9c48d54a35357e3a8ff864b699903b843a0b13d3
Parent: 930e266beb35562806c1081330831a203ab75c66

Files changed

README.mdchanged
app/html/app.jschanged
app/html/tabs.jschanged
app/page/notifications.jschanged
app/page/thread.jschanged
app/sync/initialise/electronState.jsadded
app/sync/initialise/errorCatcher.jsadded
app/sync/initialise/styles.jsadded
app/sync/initialise/userActionListeners.jsadded
index.jschanged
message/html/render/blog.jsadded
message/html/render/blog.mcssadded
package-lock.jsonchanged
package.jsonchanged
README.mdView
@@ -7,9 +7,8 @@
77 Patchbay is built using [patchcore](https://github.com/ssbc/patchcore) + [depject](https://github.com/dominictarr/depject). The goal is to make it easier to develop new features, and enable or disable features. This has so far been quite successful!
88
99 This makes in very easy to create say, a renderer for a new message type, or switch to a different method for choosing user names.
1010
11-
1211 ## Setup
1312
1413 Libsodium has some build dependencies. On ubuntu systems the following might help:
1514
@@ -99,8 +98,16 @@
9998 # from the patchbay repo folder
10099 npm run dev
101100 ```
102101
102 +## Keyboard shortcuts
103 +`CmdOrCtrl` is the `command` key on Apple keyboards or the `ctrl` key on PC keyboards.
104 +
105 +### Tabs and Window
106 +- `CmdOrCtrl+Shift+]` and `CmdOrCtrl+Shift+[` will cycle the tabs left and right
107 +- `CmdOrCtrl+w` will close the current tab
108 +- `CmdOrCtrl+Shift+w` will close the current window
109 +
103110 ## How to add a feature
104111
105112 To add a new message type, add add a js to `./modules/` that exports a function named `message_content` (it should return an HTML element). To add a new tab, export a function named `screen_view` (returns an html element).
106113
app/html/app.jsView
@@ -1,24 +1,17 @@
11 const nest = require('depnest')
22 const { h } = require('mutant')
3-const insertCss = require('insert-css')
4-const electron = require('electron')
53
64 exports.gives = nest('app.html.app')
75
86 exports.needs = nest({
9- 'app.async.catchLinkClick': 'first',
107 'app.html.tabs': 'first',
118 'app.page.errors': 'first',
12- 'app.sync.catchKeyboardShortcut': 'first',
139 'app.sync.goTo': 'first',
1410 'app.sync.initialise': 'first',
1511 'app.sync.window': 'reduce',
1612 'history.obs.location': 'first',
1713 'history.sync.push': 'first',
18- 'router.async.router': 'first',
19- 'router.sync.router': 'first', // TODO rm
20- 'styles.css': 'reduce',
2114 'settings.sync.get': 'first',
2215 'settings.sync.set': 'first'
2316 })
2417
@@ -27,62 +20,18 @@
2720
2821 function app () {
2922 console.log('STARTING app')
3023
31- api.app.sync.initialise()
32-
3324 window = api.app.sync.window(window)
3425
35- const css = values(api.styles.css()).join('\n')
36- insertCss(css)
26 + const initialTabs = ['/public', '/inbox', '/notifications'] // NB router converts these to { page: '/public' }
27 + const App = h('App', api.app.html.tabs(initialTabs))
3728
38- const initialTabs = ['/public', '/inbox', '/notifications']
39- // NB router converts these to { page: '/public' }
40- const tabs = api.app.html.tabs(initialTabs)
29 + api.app.sync.initialise(App)
30 + // runs all the functions in app/sync/initialise
4131
42- const App = h('App', tabs)
43-
44- // Catch user actions
45- api.app.sync.catchKeyboardShortcut(window, { tabs })
46- api.app.async.catchLinkClick(App)
47-
4832 api.history.obs.location()(loc => api.app.sync.goTo(loc || {}))
4933
50- // Catch errors
51- // TODO - change this error handler error page annomaly
52- // Note I'm still using a sync router just to ge this working (mix)
53- var { container: errorPage, addError } = api.router.sync.router('/errors')
54- window.addEventListener('error', ev => {
55- if (!tabs.has('/errors')) tabs.add(errorPage, true)
56-
57- addError(ev.error || ev)
58- })
59-
60- /// /// TODO - extract this to keep patch-lite isolated from electron
61- const { getCurrentWebContents, getCurrentWindow } = electron.remote
62- window.addEventListener('resize', () => {
63- var wc = getCurrentWebContents()
64- wc && wc.getZoomFactor((zf) => {
65- api.settings.sync.set({
66- electron: {
67- zoomFactor: zf,
68- windowBounds: getCurrentWindow().getBounds()
69- }
70- })
71- })
72- })
73-
74- var zoomFactor = api.settings.sync.get('electron.zoomFactor')
75- if (zoomFactor) { getCurrentWebContents().setZoomFactor(zoomFactor) }
76-
77- var bounds = api.settings.sync.get('electron.windowBounds')
78- if (bounds) { getCurrentWindow().setBounds(bounds) }
79- /// ///
80-
8134 return App
8235 }
8336 }
8437
85-function values (object) {
86- const keys = Object.keys(object)
87- return keys.map(k => object[k])
88-}
app/html/tabs.jsView
@@ -52,9 +52,15 @@
5252 onSelect,
5353 onClose,
5454 append: h('div.navExtra', [ search, menu ])
5555 })
56- _tabs.currentPage = () => _tabs.get(_tabs.selected[0]).firstChild
56 + _tabs.currentPage = () => {
57 + const currentPage = _tabs.get(_tabs.selected[0])
58 + return currentPage && currentPage.firstChild
59 + }
60 + _tabs.nextTab = () => _tabs.currentPage() && _tabs.selectRelative(1)
61 + _tabs.previousTab = () => _tabs.currentPage() && _tabs.selectRelative(-1)
62 + _tabs.closeCurrentTab = () => _tabs.currentPage() && _tabs.remove(_tabs.selected[0])
5763
5864 // # TODO: review - this works but is strange
5965 initialTabs.forEach(p => api.app.sync.goTo(p))
6066 api.app.sync.goTo(initialTabs[0])
app/page/notifications.jsView
@@ -37,20 +37,26 @@
3737 const id = api.keys.sync.id()
3838
3939 const { filterMenu, filterDownThrough, filterUpThrough, resetFeed } = api.app.html.filter(draw)
4040 const { container, content } = api.app.html.scroller({ prepend: [ filterMenu ] })
41 + const removeMyMessages = () => pull.filter(msg => msg.value.author !== id)
42 + const removePrivateMessages = () => pull.filter(msg => msg.value.private !== true)
4143
4244 function draw () {
4345 resetFeed({ container, content })
4446
4547 pull(
4648 next(api.feed.pull.mentions(id), {old: false, limit: 100}),
49 + removeMyMessages(),
50 + removePrivateMessages(),
4751 filterDownThrough(),
4852 Scroller(container, content, api.message.html.render, true, false)
4953 )
5054
5155 pull(
5256 next(api.feed.pull.mentions(id), {reverse: true, limit: 100, live: false}),
57 + removeMyMessages(),
58 + removePrivateMessages(),
5359 filterUpThrough(),
5460 Scroller(container, content, api.message.html.render, false, false)
5561 )
5662 }
app/page/thread.jsView
@@ -101,9 +101,9 @@
101101 if (!tabs.currentPage().scroll) return setTimeout(locateKey, 200)
102102
103103 tabs.currentPage().scroll('first')
104104 const msg = tabs.currentPage().querySelector(`[data-id='${id}']`)
105- if (msg === null) return setTimeout(locateKey, 200)
105 + if (msg === undefined) return setTimeout(locateKey, 200)
106106
107107 ;(msg.scrollIntoViewIfNeeded || msg.scrollIntoView).call(msg)
108108 msg.focus()
109109 }
app/sync/initialise/electronState.jsView
@@ -1,0 +1,38 @@
1 +const nest = require('depnest')
2 +const electron = require('electron')
3 +
4 +exports.gives = nest('app.sync.initialise')
5 +
6 +exports.needs = nest({
7 + 'settings.sync.get': 'first',
8 + 'settings.sync.set': 'first',
9 +})
10 +
11 +exports.create = function (api) {
12 + return nest('app.sync.initialise', errorCatcher)
13 +
14 + function errorCatcher () {
15 + /// /// TODO - extract this to keep patch-lite isolated from electron
16 + const { getCurrentWebContents, getCurrentWindow } = electron.remote
17 + window.addEventListener('resize', () => {
18 + var wc = getCurrentWebContents()
19 + wc && wc.getZoomFactor((zf) => {
20 + api.settings.sync.set({
21 + electron: {
22 + zoomFactor: zf,
23 + windowBounds: getCurrentWindow().getBounds()
24 + }
25 + })
26 + })
27 + })
28 +
29 + var zoomFactor = api.settings.sync.get('electron.zoomFactor')
30 + if (zoomFactor) { getCurrentWebContents().setZoomFactor(zoomFactor) }
31 +
32 + var bounds = api.settings.sync.get('electron.windowBounds')
33 + if (bounds) { getCurrentWindow().setBounds(bounds) }
34 + /// ///
35 + }
36 +}
37 +
38 +
app/sync/initialise/errorCatcher.jsView
@@ -1,0 +1,24 @@
1 +const nest = require('depnest')
2 +
3 +exports.gives = nest('app.sync.initialise')
4 +
5 +exports.needs = nest({
6 + 'router.sync.router': 'first',
7 + 'app.html.tabs': 'first'
8 +})
9 +
10 +exports.create = function (api) {
11 + return nest('app.sync.initialise', errorCatcher)
12 +
13 + function errorCatcher () {
14 + const tabs = api.app.html.tabs()
15 +
16 + var { container: errorPage, addError } = api.router.sync.router('/errors')
17 + window.addEventListener('error', ev => {
18 + if (!tabs.has('/errors')) tabs.add(errorPage, true)
19 +
20 + addError(ev.error || ev)
21 + })
22 + }
23 +}
24 +
app/sync/initialise/styles.jsView
@@ -1,0 +1,24 @@
1 +const nest = require('depnest')
2 +const insertCss = require('insert-css')
3 +
4 +exports.gives = nest('app.sync.initialise')
5 +
6 +exports.needs = nest({
7 + 'styles.css': 'reduce',
8 +})
9 +
10 +
11 +exports.create = function (api) {
12 + return nest('app.sync.initialise', styles)
13 +
14 + function styles () {
15 + const css = values(api.styles.css()).join('\n')
16 + insertCss(css)
17 + }
18 +}
19 +
20 +function values (object) {
21 + const keys = Object.keys(object)
22 + return keys.map(k => object[k])
23 +}
24 +
app/sync/initialise/userActionListeners.jsView
@@ -1,0 +1,35 @@
1 +const nest = require('depnest')
2 +
3 +exports.gives = nest('app.sync.initialise')
4 +
5 +exports.needs = nest({
6 + 'app.async.catchLinkClick': 'first',
7 + 'app.sync.catchKeyboardShortcut': 'first',
8 + 'app.html.tabs': 'first',
9 +})
10 +
11 +
12 +exports.create = function (api) {
13 + return nest('app.sync.initialise', userActionListeners)
14 +
15 + function userActionListeners (App) {
16 + const tabs = api.app.html.tabs()
17 +
18 + api.app.sync.catchKeyboardShortcut(window)
19 + api.app.async.catchLinkClick(App)
20 +
21 + electron.ipcRenderer.on('nextTab', () => {
22 + tabs.nextTab()
23 + })
24 +
25 + electron.ipcRenderer.on('previousTab', () => {
26 + tabs.previousTab()
27 + })
28 +
29 + electron.ipcRenderer.on('closeTab', () => {
30 + tabs.closeCurrentTab()
31 + })
32 +
33 + }
34 +}
35 +
index.jsView
@@ -21,18 +21,38 @@
2121 { role: 'zoomout' },
2222 { type: 'separator' },
2323 { role: 'togglefullscreen' }
2424 ]
25- if (process.platform === 'darwin') {
26- var win = menu.find(x => x.label === 'Window')
27- win.submenu = [
28- { role: 'minimize' },
29- { role: 'zoom' },
30- { role: 'close', label: 'Close' },
31- { type: 'separator' },
32- { role: 'front' }
33- ]
34- }
25 + var win = menu.find(x => x.label === 'Window')
26 + win.submenu = [
27 + { role: 'minimize' },
28 + { role: 'zoom' },
29 + { role: 'close', label: 'Close Window', accelerator: 'CmdOrCtrl+Shift+W' },
30 + { type: 'separator' },
31 + {
32 + label: 'Close Tab',
33 + accelerator: 'CmdOrCtrl+W',
34 + click() {
35 + windows.main.webContents.send('closeTab')
36 + }
37 + },
38 + {
39 + label: 'Select Next Tab',
40 + accelerator: 'CmdOrCtrl+Shift+]',
41 + click() {
42 + windows.main.webContents.send('nextTab')
43 + }
44 + },
45 + {
46 + label: 'Select Previous Tab',
47 + accelerator: 'CmdOrCtrl+Shift+[',
48 + click() {
49 + windows.main.webContents.send('previousTab')
50 + }
51 + },
52 + { type: 'separator' },
53 + { role: 'front' }
54 + ]
3555
3656 Menu.setApplicationMenu(Menu.buildFromTemplate(menu))
3757
3858 startBackgroundProcess()
message/html/render/blog.jsView
@@ -1,0 +1,99 @@
1 +const nest = require('depnest')
2 +const Blog = require('scuttle-blog')
3 +const isBlog = require('scuttle-blog/isBlog')
4 +const { h, Value, computed, when, resolve, onceTrue } = require('mutant')
5 +const isEmpty = require('lodash/isEmpty')
6 +
7 +exports.gives = nest('message.html.render')
8 +
9 +exports.needs = nest({
10 + 'about.obs.color': 'first',
11 + 'blob.sync.url': 'first',
12 + 'message.html.decorate': 'reduce',
13 + 'message.html.layout': 'first',
14 + 'message.html.markdown': 'first',
15 + 'sbot.obs.connection': 'first',
16 + // 'history.sync.push': 'first',
17 +})
18 +
19 +exports.create = function (api) {
20 + return nest('message.html.render', blogRenderer)
21 +
22 + function blogRenderer (msg, opts) {
23 + if (!isBlog(msg)) return
24 +
25 + var blog = Blog(api.sbot.obs.connection).obs.get(msg)
26 + var showBlog = Value(false)
27 +
28 + const element = api.message.html.layout(msg, Object.assign({}, {
29 + content: when(showBlog,
30 + BlogFull(blog, api.message.html.markdown),
31 + BlogCard({
32 + blog,
33 + onClick: () => showBlog.set(true),
34 + color: api.about.obs.color,
35 + blobUrl: api.blob.sync.url
36 + })
37 + // Sample(blog, api.blob.sync.url, showBlog)
38 + ),
39 + layout: 'default'
40 + }, opts))
41 +
42 + return api.message.html.decorate(element, { msg })
43 +
44 + }
45 +}
46 +
47 +function BlogFull (blog, renderMd) {
48 + return computed(blog.body, body => {
49 + if (!isEmpty(body)) {
50 + return h('BlogFull.Markdown', [
51 + h('h1', blog.title),
52 + renderMd(body)
53 + ])
54 + }
55 +
56 + return h('BlogFull.Markdown', [
57 + h('h1', blog.title),
58 + blog.summary,
59 + h('p', 'loading...')
60 + ])
61 + })
62 +}
63 +
64 +function BlogCard ({ blog, blobUrl, onClick, color }) {
65 + const thumbnail = when(blog.thumbnail,
66 + h('Thumbnail', {
67 + style: {
68 + 'background-image': `url("${blobUrl(resolve(blog.thumbnail))}")`,
69 + 'background-position': 'center',
70 + 'background-size': 'cover'
71 + }
72 + }),
73 + h('Thumbnail -empty', {
74 + style: { 'background-color': color(blog.title) }
75 + }, [
76 + h('i.fa.fa-file-text-o')
77 + ])
78 + )
79 +
80 + var b = h('BlogCard', { 'ev-click': onClick }, [
81 + // h('div.context', [
82 + // api.about.html.avatar(author, 'tiny'),
83 + // h('div.name', api.about.obs.name(author)),
84 + // api.message.html.timeago(blog)
85 + // ]),
86 + h('div.content', [
87 + thumbnail,
88 + h('div.text.Markdown', [
89 + h('h1', blog.title),
90 + // when(blog.channel, api.message.html.channel(blog.channel))
91 + h('div.summary', blog.summary),
92 + h('div.read', 'Read blog')
93 + ])
94 + ])
95 + ])
96 +
97 + return b
98 +}
99 +
message/html/render/blog.mcssView
@@ -1,0 +1,51 @@
1 +BlogCard {
2 + box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 10px
3 + margin: 2rem 0
4 + border: 1px solid rgba(0, 0, 0, 0.1)
5 +
6 + display: flex
7 + flex-direction: column
8 +
9 + div.content {
10 + display: flex
11 + flex-direction: row
12 + flex-grow: 1
13 +
14 + cursor: pointer
15 +
16 +
17 + div.Thumbnail {
18 + margin-right: 1rem
19 + }
20 +
21 + div.text {
22 + padding: .5rem
23 + div.summary {
24 + }
25 + div.read {
26 + margin-top: 1rem
27 + text-decoration: underline
28 + }
29 + }
30 +
31 + }
32 + background-color: #fff
33 +}
34 +
35 +Thumbnail {
36 + min-width: 20rem
37 + width: 20rem
38 + min-height: 10rem
39 + /* height: 10rem */
40 +
41 + -empty {
42 + color: #fff
43 + font-size: 1.8rem
44 + opacity: .8
45 +
46 + display: flex
47 + justify-content: center
48 + align-items: center
49 + }
50 +}
51 +
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 305638 bytes
New file size: 307075 bytes
package.jsonView
@@ -63,16 +63,17 @@
6363 "patch-settings": "^1.0.1",
6464 "patch-suggest": "^1.1.0",
6565 "patchbay-book": "^1.0.4",
6666 "patchbay-gatherings": "^2.0.0",
67- "patchcore": "^1.23.0",
67 + "patchcore": "^1.23.3",
6868 "pull-abortable": "^4.1.1",
6969 "pull-cat": "^1.1.11",
7070 "pull-next": "^1.0.0",
7171 "pull-scroll": "^1.0.9",
7272 "pull-stream": "^3.6.1",
7373 "read-directory": "^2.1.1",
7474 "require-style": "^1.0.1",
75 + "scuttle-blog": "^1.0.0",
7576 "scuttlebot": "^10.4.10",
7677 "setimmediate": "^1.0.5",
7778 "ssb-about": "^0.1.1",
7879 "ssb-backlinks": "^0.6.1",

Built with git-ssb-web