git ssb

16+

Dominic / patchbay



Commit f0535dd0a919de44e6900039da1cfe29882caa5a

Merge pull request #195 from ssbc/image_search

mvp image search
mix irving authored on 5/27/2018, 12:07:08 PM
GitHub committed on 5/27/2018, 12:07:08 PM
Parent: 439c5c1934e4781abb15490399e17e183f4e07db
Parent: 4d1674d8270affecae790df4de91734c0399dc25

Files changed

app/html/modal.mcsschanged
app/page/imageSearch.jsadded
app/page/imageSearch.mcssadded
background-process.jschanged
package-lock.jsonchanged
package.jsonchanged
router/sync/routes.jschanged
app/html/modal.mcssView
@@ -7,15 +7,19 @@
77 height: 100%
88 text-align: center
99 top: 0
1010 left: 0
11- background: rgba(0,0,0,0.8)
11 + background: rgba(0,0,0,0.1)
1212
1313 div.content {
14 + max-width: 90vw
15 + max-height: 90vh
16 + overflow: auto
17 +
18 + $backgroundPrimary
19 + padding: 30px
20 + box-shadow: rgba(0,0,0,.34) 2px 6px 10px
1421 margin: auto
15- padding: 30px
16- border-radius: 5px
17- $backgroundPrimary
1822 $dontSelect
1923
2024 div.dialog {
2125 div.message {
app/page/imageSearch.jsView
@@ -1,0 +1,135 @@
1 +const nest = require('depnest')
2 +const { h, Dict, Value, watch, throttle, computed, map, onceTrue } = require('mutant')
3 +
4 +exports.gives = nest({
5 + 'app.page.imageSearch': true,
6 + 'app.html.menuItem': true
7 +})
8 +
9 +exports.needs = nest({
10 + 'sbot.obs.connection': 'first',
11 + 'blob.sync.url': 'first',
12 + 'app.html.modal': 'first',
13 + 'app.sync.goTo': 'first',
14 + 'about.obs.name': 'first',
15 + 'backlinks.obs.for': 'first'
16 +})
17 +
18 +exports.create = function (api) {
19 + return nest({
20 + 'app.html.menuItem': menuItem,
21 + 'app.page.imageSearch': searchPage
22 + })
23 +
24 + function menuItem () {
25 + return h('a', {
26 + style: { order: 0 },
27 + 'ev-click': () => api.app.sync.goTo('/imageSearch')
28 + }, '/imageSearch')
29 + }
30 +
31 + function searchPage (location) {
32 + const query = Value('')
33 + const results = Dict({})
34 +
35 + watch(throttle(query, 300), q => {
36 + if (q && q.length < 3) return
37 + onceTrue(api.sbot.obs.connection, sbot => {
38 + sbot.meme.search(q, (err, data) => {
39 + if (err) return console.error(err)
40 + results.set(data)
41 + })
42 + })
43 + })
44 +
45 + const focusedBlob = Value()
46 + const modal = Modal({
47 + results,
48 + focusedBlob,
49 + blobUrl: api.blob.sync.url,
50 + name: api.about.obs.name,
51 + goTo: api.app.sync.goTo,
52 + createModal: api.app.html.modal,
53 + backlinks: api.backlinks.obs.for
54 + })
55 +
56 + const page = h('Page -imageSearch', [
57 + modal,
58 + h('section.settings', [
59 + h('input', {
60 + 'placeholder': 'search image by name',
61 + 'ev-input': ev => query.set(ev.target.value.toLowerCase())
62 + })
63 + ]),
64 + h('section.results', computed([results, query], (results, query) => {
65 + if (!Object.keys(results).length && query.length >= 3) return h('p', '0 results')
66 +
67 + return Object.keys(results).map(blob => {
68 + return h('div', { 'ev-click': () => focusedBlob.set(blob) }, [
69 + h('img', { src: api.blob.sync.url(blob) })
70 + ])
71 + })
72 + }))
73 + ])
74 +
75 + page.title = '/imageSearch'
76 +
77 + return page
78 + }
79 +}
80 +
81 +function Modal ({ results, focusedBlob, blobUrl, name, goTo, createModal, backlinks }) {
82 + const onClick = (link) => () => {
83 + isOpen.set(false)
84 + goTo(link)
85 + }
86 +
87 + const isOpen = Value(false)
88 + focusedBlob(blob => {
89 + if (blob) isOpen.set(true)
90 + })
91 + const modalContent = computed([focusedBlob], (blob) => {
92 + if (!blob) return
93 +
94 + const entries = computed(backlinks(blob), msgs => {
95 + return msgs.reduce((soFar, msg) => {
96 + const entries = getMentions(msg)
97 + .filter(mention => mention.link === blob)
98 + // .filter(mention => mention.name)
99 + .map(mention => { return { name: mention.name, author: msg.value.author, msg: msg.key, ts: msg.value.timestamp } })
100 + return [...soFar, ...entries]
101 + }, [])
102 + })
103 +
104 + const imageName = Value('CHOOSE YOUR OWN NAME')
105 +
106 + return [
107 + h('img', { src: blobUrl(blob) }),
108 + h('div.md', [
109 + 'Copy markdown: ',
110 + h('pre', ['![', imageName, '](', blob, ')'])
111 + ]),
112 + h('table', map(entries, entry => {
113 + return h('tr', { 'ev-mouseover': () => entry.name && imageName.set(entry.name) }, [
114 + h('td', entry.name),
115 + h('td.msg', h('a', { href: '#', 'ev-click': onClick(entry.msg) }, entry.msg.substring(0, 10) + '...')),
116 + h('td', h('a', { href: '#', 'ev-click': onClick(entry.author) }, ['@', name(entry.author)])),
117 + h('td', JSON.stringify(new Date(entry.ts)).substring(1, 11)) // I am a bad human D:
118 + ])
119 + })),
120 + h('button', {
121 + 'ev-click': () => {
122 + focusedBlob.set()
123 + isOpen.set(false)
124 + }
125 + }, 'Close')
126 + ]
127 + })
128 + return createModal(modalContent, { isOpen })
129 +}
130 +
131 +function getMentions (msg) {
132 + if (!msg.value.content.mentions) return []
133 + else if (!Array.isArray(msg.value.content.mentions)) return [msg.value.content.mentions]
134 + else return msg.value.content.mentions
135 +}
app/page/imageSearch.mcssView
@@ -1,0 +1,84 @@
1 +Page -imageSearch {
2 + overflow-y: auto
3 +
4 + display: grid
5 + grid-template-columns: 85vw
6 + grid-template-rows: 5rem auto
7 + justify-content: center
8 + align-items: start
9 +
10 + div.Modal {
11 + div.content {
12 + display: grid
13 + justify-items: center
14 +
15 + img {
16 + max-width: 90vw
17 + max-height: 80vh
18 +
19 + margin-bottom: 1rem
20 + }
21 +
22 + div.md {
23 + pre {
24 + user-select: all
25 + padding: .2rem
26 + padding-right: 2rem
27 + min-width: 39rem
28 + background-color: rgba(0,0,0,.1)
29 +
30 + text-align: right
31 + margin-top: .2rem
32 + display: inline-block
33 + }
34 + }
35 +
36 + table {
37 + tr {
38 + td {
39 + text-align: left
40 + padding-right: .5rem
41 + }
42 +
43 + td.msg {
44 + font-family: monospace
45 + }
46 + }
47 + }
48 + }
49 + }
50 +
51 + section.settings {
52 + padding: 1rem 0
53 + input {
54 + min-width: 20rem
55 + padding: .5rem
56 + }
57 + }
58 +
59 + section.results {
60 + display: flex
61 + align-items: center
62 + flex-wrap: wrap
63 +
64 + div {
65 + cursor: pointer
66 +
67 + min-width: 5rem
68 + min-height: 5rem
69 + max-width: 19rem
70 + max-height: 19rem
71 +
72 + display: flex
73 + justify-content: center
74 + align-items: center
75 +
76 + img {
77 + max-width: 19rem
78 + max-height: 19rem
79 + }
80 +
81 + margin: 0 1rem 1rem 0
82 + }
83 + }
84 +}
background-process.jsView
@@ -16,8 +16,9 @@
1616 .use(require('ssb-friends'))
1717 .use(require('ssb-private'))
1818 .use(require('ssb-blobs'))
1919 .use(require('ssb-search'))
20 + .use(require('ssb-meme'))
2021 .use(require('ssb-ebt'))
2122 .use(require('ssb-chess-db'))
2223 .use(require('ssb-query'))
2324 .use(require('ssb-ws'))
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 305890 bytes
New file size: 317388 bytes
package.jsonView
@@ -85,8 +85,9 @@
8585 "ssb-ebt": "^5.2.0",
8686 "ssb-friends": "^2.2.3",
8787 "ssb-horcrux": "^1.0.0",
8888 "ssb-keys": "^7.0.15",
89 + "ssb-meme": "^1.0.0",
8990 "ssb-mentions": "^0.4.1",
9091 "ssb-mutual": "^0.1.0",
9192 "ssb-private": "^0.1.2",
9293 "ssb-query": "^1.0.2",
router/sync/routes.jsView
@@ -10,8 +10,9 @@
1010 'private': 'first',
1111 'notifications': 'first',
1212 'profile': 'first',
1313 'search': 'first',
14 + 'imageSearch': 'first',
1415 'blob': 'first',
1516 'thread': 'first',
1617 'channel': 'first',
1718 'settings': 'first'
@@ -32,9 +33,10 @@
3233 [ loc => loc.page === 'notifications', pages.notifications ],
3334 [ loc => loc.page === 'errors', pages.errors ],
3435 [ loc => loc.page === 'profile', () => pages.profile({ feed: myId }) ],
3536 [ loc => loc.page === 'search' && loc.query, pages.search ],
36- [ loc => loc.page === 'settings', pages.settings],
37 + [ loc => loc.page === 'imageSearch', pages.imageSearch ],
38 + [ loc => loc.page === 'settings', pages.settings ],
3739
3840 [ loc => isBlob(loc.blob), pages.blob ],
3941 [ loc => isPresent(loc.channel), pages.channel ],
4042 [ loc => isFeed(loc.feed), pages.profile ],

Built with git-ssb-web