git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Commit 9b14c2770a1b327ee389c1c27a56ae36412130c0

add profile view

Matt McKegg committed on 2/15/2017, 2:38:34 PM
Parent: abf8335c9c9187ce7b3861f372ce512d210e8a7b

Files changed

modules/page/html/render/profile.jschanged
modules/profile/obs/following.jsdeleted
styles/split-view.mcsschanged
styles/profile-header.mcssadded
modules/page/html/render/profile.jsView
@@ -1,0 +1,138 @@
1+var nest = require('depnest')
2+var ref = require('ssb-ref')
3+var {Value, h, when, computed, map, send} = require('mutant')
4+var extend = require('xtend')
5+
6+exports.needs = nest({
7+ 'about.obs.name': 'first',
8+ 'about.html.image': 'first',
9+ 'feed.html.rollup': 'first',
10+ 'sbot.pull.userFeed': 'first',
11+ 'sbot.async.publish': 'first',
12+ 'keys.sync.id': 'first',
13+ 'profile.obs': {
14+ followers: 'first',
15+ following: 'first'
16+ }
17+})
18+exports.gives = nest('page.html.render')
19+
20+exports.create = function (api) {
21+ return nest('page.html.render', function profile (id) {
22+ if (!ref.isFeed(id)) return
23+
24+ var name = api.about.obs.name(id)
25+ var yourId = api.keys.sync.id()
26+ var yourFollows = api.profile.obs.following(yourId)
27+ var rawFollowers = api.profile.obs.followers(id)
28+ var rawFollowing = api.profile.obs.following(id)
29+ var doneWaiting = Value(false)
30+ setTimeout(() => doneWaiting.set(true), 1e3)
31+ var friendsLoaded = computed([rawFollowers, rawFollowing, doneWaiting], (...x) => x.every(Boolean))
32+
33+ var friends = computed([rawFollowing, rawFollowers], (following, followers) => {
34+ return Array.from(following).filter(follow => followers.has(follow))
35+ })
36+
37+ var following = computed([rawFollowing, friends], (following, friends) => {
38+ return Array.from(following).filter(follow => !friends.includes(follow))
39+ })
40+
41+ var followers = computed([rawFollowers, friends], (followers, friends) => {
42+ return Array.from(followers).filter(follower => !friends.includes(follower))
43+ })
44+
45+ var isFriends = computed([friends], function (friends) {
46+ return friends.includes(yourId)
47+ })
48+
49+ var followsYou = computed([following], function (followsYou) {
50+ return followsYou.includes(yourId)
51+ })
52+
53+ var youFollow = computed([yourFollows], function (youFollow) {
54+ return youFollow.has(id)
55+ })
56+
57+ var prepend = h('header', {className: 'ProfileHeader'}, [
58+ h('div.image', api.about.html.image(id)),
59+ h('div.title', [
60+ h('h1', ['@', name])
61+ ]),
62+ h('div.meta', [
63+ when(id === yourId, [
64+ h('a.-disabled', 'This is you!')
65+ ], [
66+ when(youFollow,
67+ h('a.ToggleButton.-unsubscribe', {
68+ 'href': '#',
69+ 'title': 'Click to unfollow',
70+ 'ev-click': send(unfollow, id)
71+ }, when(isFriends, 'Friends', 'Following')),
72+ h('a.ToggleButton.-subscribe', {
73+ 'href': '#',
74+ 'ev-click': send(follow, id)
75+ }, when(followsYou, 'Follow Back', 'Follow'))
76+ )
77+ ])
78+
79+ ])
80+ ])
81+
82+ return h('div', {className: 'SplitView'}, [
83+ h('div.main', [
84+ api.feed.html.rollup((opts) => {
85+ return api.sbot.pull.userFeed(extend(opts, {id}))
86+ }, { prepend })
87+ ]),
88+ h('div.side.-right', [
89+ when(friendsLoaded,
90+ h('div', [
91+ renderContactBlock('Friends', friends),
92+ renderContactBlock('Followers', followers),
93+ renderContactBlock('Following', following)
94+ ]),
95+ h('div', {className: 'Loading'})
96+ )
97+ ])
98+ ])
99+ })
100+
101+ function renderContactBlock (title, profiles) {
102+ return [
103+ when(computed(profiles, x => x.length), h('h2', title)),
104+ h('div', {
105+ classList: 'ProfileList'
106+ }, [
107+ map(profiles, (id) => {
108+ return h('a.profile', {
109+ href: id
110+ }, [
111+ h('div.avatar', [api.about.html.image(id)]),
112+ h('div.main', [
113+ h('div.name', [ api.about.obs.name(id) ])
114+ ])
115+ ])
116+ }, {
117+ idle: true
118+ })
119+ ])
120+ ]
121+ }
122+
123+ function follow (id) {
124+ api.sbot.async.publish({
125+ type: 'contact',
126+ contact: id,
127+ following: true
128+ })
129+ }
130+
131+ function unfollow (id) {
132+ api.sbot.async.publish({
133+ type: 'contact',
134+ contact: id,
135+ following: false
136+ })
137+ }
138+}
modules/profile/obs/following.jsView
@@ -1,61 +1,0 @@
1-var pull = require('pull-stream')
2-var computed = require('mutant/computed')
3-var MutantPullReduce = require('mutant-pull-reduce')
4-var throttle = require('mutant/throttle')
5-var nest = require('depnest')
6-
7-exports.needs = nest({
8- 'sbot.pull.userFeed': 'first'
9-})
10-
11-exports.gives = nest({
12- 'profile.obs': ['following']
13-})
14-
15-exports.create = function (api) {
16- var cache = {}
17-
18- return nest({
19- 'profile.obs': {following}
20- })
21-
22- function following (userId) {
23- if (cache[userId]) {
24- return cache[userId]
25- } else {
26- var stream = pull(
27- api.sbot.pull.userFeed({id: userId, live: true}),
28- pull.filter((msg) => {
29- return !msg.value || msg.value.content.type === 'contact'
30- })
31- )
32-
33- var result = MutantPullReduce(stream, (result, msg) => {
34- var c = msg.value.content
35- if (c.contact) {
36- if (typeof c.following === 'boolean') {
37- if (c.following) {
38- result.add(c.contact)
39- } else {
40- result.delete(c.contact)
41- }
42- }
43- }
44- return result
45- }, {
46- startValue: new Set(),
47- nextTick: true
48- })
49-
50- var instance = throttle(result, 2000)
51- instance.sync = result.sync
52-
53- instance.has = function (value) {
54- return computed(instance, x => x.has(value))
55- }
56-
57- cache[userId] = instance
58- return instance
59- }
60- }
61-}
styles/split-view.mcssView
@@ -7,14 +7,20 @@
77 flex: 1
88 overflow-y: scroll
99 }
1010 div.side {
11- min-width: 280px;
11+ width: 280px;
1212 padding: 20px;
1313 background: linear-gradient(100deg, #cee4ef, #efebeb);
1414 border-right: 1px solid #dcdcdc;
1515 overflow-y: auto;
1616
17+ -right {
18+ border: none
19+ border-left: 1px solid #dcdcdc
20+ background: linear-gradient(100deg, #ffffff, #efebeb)
21+ }
22+
1723 h2 {
1824 margin-top: 20px
1925 margin-bottom: 8px
2026 color: #527b90;
styles/profile-header.mcssView
@@ -1,0 +1,21 @@
1+ProfileHeader {
2+ display: flex;
3+ width: 100%;
4+ max-width: 700px;
5+ margin: 20px auto;
6+
7+ div.image {
8+ width: 200px;
9+ padding: 5px;
10+ background: white;
11+ box-shadow: 0 0 10px #AAA;
12+ margin-right: 20px;
13+ img {
14+ width: 100%
15+ }
16+ }
17+
18+ div.title {
19+ flex: 1
20+ }
21+}

Built with git-ssb-web