git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Commit 26d9fa8ec162c42e2fa61b44c63952e0f38b38c3

suggest on search box, also suggest channels

Matt McKegg committed on 2/19/2017, 9:47:23 AM
Parent: 5cfee7fcb845036b63555b7546206364a1700b7a

Files changed

main-window.jschanged
modules/channel/obs/recent.jschanged
modules/channel/obs/suggest.jsadded
modules/message/html/compose.jschanged
modules/page/html/render/public.jschanged
modules/profile/async/suggest.jschanged
styles/suggest-box.mcsschanged
main-window.jsView
@@ -10,8 +10,9 @@
1010 var MutantMap = require('mutant/map')
1111 var Url = require('url')
1212 var insertCss = require('insert-css')
1313 var nest = require('depnest')
14+var addSuggest = require('suggest-box')
1415
1516 module.exports = function (config) {
1617 var sockets = combine(
1718 overrideConfig(config),
@@ -23,18 +24,26 @@
2324
2425 var api = entry(sockets, nest({
2526 'page.html.render': 'first',
2627 'keys.sync.id': 'first',
27- 'blob.sync.url': 'first'
28+ 'blob.sync.url': 'first',
29+ 'profile.async.suggest': 'first',
30+ 'channel.async.suggest': 'first'
2831 }))
2932
3033 var renderPage = api.page.html.render
3134 var id = api.keys.sync.id()
35+ var getProfileSuggestions = api.profile.async.suggest()
36+ var getChannelSuggestions = api.channel.async.suggest()
3237
3338 var searchTimer = null
3439 var searchBox = h('input.search', {
3540 type: 'search',
36- placeholder: 'word, @key, #channel'
41+ placeholder: 'word, @key, #channel',
42+ 'ev-suggestselect': (ev) => {
43+ setView(ev.detail.id)
44+ searchBox.value = ev.detail.id
45+ }
3746 })
3847
3948 searchBox.oninput = function () {
4049 clearTimeout(searchTimer)
@@ -83,9 +92,9 @@
8392 }))
8493
8594 insertCss(require('./styles'))
8695
87- return h(`MainWindow -${process.platform}`, {
96+ var container = h(`MainWindow -${process.platform}`, {
8897 events: {
8998 click: catchLinks
9099 }
91100 }, [
@@ -113,8 +122,18 @@
113122 ]),
114123 mainElement
115124 ])
116125
126+ addSuggest(searchBox, (inputText, cb) => {
127+ if (inputText[0] === '@') {
128+ cb(null, getProfileSuggestions(inputText.slice(1)), {idOnly: true})
129+ } else if (inputText[0] === '#') {
130+ cb(null, getChannelSuggestions(inputText.slice(1)))
131+ }
132+ }, {cls: 'SuggestBox'})
133+
134+ return container
135+
117136 // scoped
118137
119138 function catchLinks (ev) {
120139 if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.defaultPrevented) {
@@ -210,11 +229,17 @@
210229
211230 function doSearch () {
212231 var value = searchBox.value.trim()
213232 if (value.startsWith('/') || value.startsWith('?') || value.startsWith('@') || value.startsWith('#') || value.startsWith('%')) {
214- setView(value)
233+ if (value.startsWith('@') && value.length < 30) {
234+ return // probably not a key
235+ } else if (value.length > 2) {
236+ setView(value)
237+ }
215238 } else if (value.trim()) {
216- setView(`?${value.trim()}`)
239+ if (value.length > 2) {
240+ setView(`?${value.trim()}`)
241+ }
217242 } else {
218243 setView('/public')
219244 }
220245 }
modules/channel/obs/recent.jsView
@@ -9,9 +9,9 @@
99 exports.create = function (api) {
1010 var channelsLookup = Dict()
1111
1212 var recentChannels = computed(channelsLookup, (lookup) => {
13- var values = Object.keys(lookup).map(x => lookup[x]).sort((a, b) => b.updatedAt - a.updatedAt)
13+ var values = Object.keys(lookup).map(x => lookup[x]).sort((a, b) => b.updatedAt - a.updatedAt).map(x => x.id)
1414 return values
1515 }, {nextTick: true})
1616
1717 return nest({
@@ -19,9 +19,9 @@
1919 if (msg.key && msg.value && msg.value.content) {
2020 var c = msg.value.content
2121 if (c.type === 'post' && typeof c.channel === 'string') {
2222 var name = c.channel.trim()
23- if (name) {
23+ if (name && name.length < 30) {
2424 var channel = channelsLookup.get(name)
2525 if (!channel) {
2626 channel = Struct({
2727 id: name,
modules/channel/obs/suggest.jsView
@@ -1,0 +1,67 @@
1+var nest = require('depnest')
2+var {Struct, map, computed, watch} = require('mutant')
3+
4+exports.needs = nest({
5+ 'channel.obs.recent': 'first',
6+ 'channel.obs.subscribed': 'first',
7+ 'keys.sync.id': 'first'
8+})
9+
10+exports.gives = nest('channel.async.suggest')
11+
12+exports.create = function (api) {
13+ var suggestions = null
14+ var subscribed = null
15+
16+ return nest('channel.async.suggest', function () {
17+ loadSuggestions()
18+ return function (word) {
19+ if (!word) {
20+ return suggestions().slice(0, 100)
21+ } else {
22+ return suggestions().filter((item) => {
23+ return item.title.toLowerCase().startsWith(word.toLowerCase())
24+ })
25+ }
26+ }
27+ })
28+
29+ function loadSuggestions () {
30+ if (!suggestions) {
31+ var id = api.keys.sync.id()
32+ subscribed = api.channel.obs.subscribed(id)
33+ var recentlyUpdated = api.channel.obs.recent()
34+ var contacts = computed([subscribed, recentlyUpdated], function (a, b) {
35+ var result = Array.from(a)
36+ b.forEach((item, i) => {
37+ if (!result.includes(item)) {
38+ result.push(item)
39+ }
40+ })
41+ return result
42+ })
43+
44+ suggestions = map(contacts, suggestion, {idle: true})
45+ watch(suggestions)
46+ }
47+ }
48+
49+ function suggestion (id) {
50+ return Struct({
51+ title: id,
52+ id: `#${id}`,
53+ subtitle: computed([id, subscribed], subscribedCaption),
54+ value: computed([id], mention)
55+ })
56+ }
57+}
58+
59+function subscribedCaption (id, subscribed) {
60+ if (subscribed.has(id)) {
61+ return 'subscribed'
62+ }
63+}
64+
65+function mention (id) {
66+ return `[#${id}](#${id})`
67+}
modules/message/html/compose.jsView
@@ -11,8 +11,9 @@
1111
1212 exports.needs = nest({
1313 'blob.html.input': 'first',
1414 'profile.async.suggest': 'first',
15+ 'channel.async.suggest': 'first',
1516 'message.async.publish': 'first',
1617 'emoji.sync.names': 'first',
1718 'emoji.sync.url': 'first'
1819 })
@@ -25,8 +26,10 @@
2526 var filesById = {}
2627 var focused = Value(false)
2728 var hasContent = Value(false)
2829 var getProfileSuggestions = api.profile.async.suggest()
30+ var getChannelSuggestions = api.channel.async.suggest()
31+
2932 var blurTimeout = null
3033
3134 var expanded = computed([shrink, focused, hasContent], (shrink, focused, hasContent) => {
3235 if (!shrink || hasContent) {
@@ -80,8 +83,10 @@
8083
8184 addSuggest(textArea, (inputText, cb) => {
8285 if (inputText[0] === '@') {
8386 cb(null, getProfileSuggestions(inputText.slice(1)))
87+ } else if (inputText[0] === '#') {
88+ cb(null, getChannelSuggestions(inputText.slice(1)))
8489 } else if (inputText[0] === ':') {
8590 // suggest emojis
8691 var word = inputText.slice(1)
8792 if (word[word.length - 1] === ':') {
modules/page/html/render/public.jsView
@@ -120,22 +120,22 @@
120120 classList: 'ChannelList',
121121 hidden: loading
122122 }, [
123123 map(channels, (channel) => {
124- var subscribed = subscribedChannels.has(channel.id)
124+ var subscribed = subscribedChannels.has(channel)
125125 return h('a.channel', {
126- href: `#${channel.id}`,
126+ href: `#${channel}`,
127127 classList: [
128128 when(subscribed, '-subscribed')
129129 ]
130130 }, [
131- h('span.name', '#' + channel.id),
131+ h('span.name', '#' + channel),
132132 when(subscribed,
133133 h('a.-unsubscribe', {
134- 'ev-click': send(unsubscribe, channel.id)
134+ 'ev-click': send(unsubscribe, channel)
135135 }, 'Unsubscribe'),
136136 h('a.-subscribe', {
137- 'ev-click': send(subscribe, channel.id)
137+ 'ev-click': send(subscribe, channel)
138138 }, 'Subscribe')
139139 )
140140 ])
141141 }, {maxTime: 5})
modules/profile/async/suggest.jsView
@@ -53,8 +53,9 @@
5353 function suggestion (id) {
5454 var name = api.about.obs.name(id)
5555 return Struct({
5656 title: name,
57+ id,
5758 subtitle: id.substring(0, 10),
5859 value: computed([name, id], mention),
5960 image: api.about.obs.imageUrl(id),
6061 showBoth: true
styles/suggest-box.mcssView
@@ -17,8 +17,9 @@
1717 align-items: center;
1818
1919 padding-right: .2rem;
2020 margin-bottom: .2rem;
21+ cursor: pointer
2122
2223 img {
2324 height: 36px;
2425 width: 36px;

Built with git-ssb-web