Redid lots of things about viewing an invidual user's feed, their profile, and following users.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3740 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
50d860183d
commit
700d09c730
@ -1 +1 @@
|
||||
{"type":"tildefriends-app","files":{"app.js":"&KdIfXHcm7xE6iBlJWoW8DixHbIPs+YPevJL+rF8eW4s=.sha256","index.html":"&ayK1muTik0h4UYNvT0QNm0WrygVrL8kxBoEyrQkHyHs=.sha256","vue-material.js":"&K5cdLqXYCENPak/TCINHQhyJhpS4G9DlZHGwoh/LF2g=.sha256","tf-user.js":"&fXhPpneuYuseoaYk25ODN3QecFAeOfuMOZd98OgvjsQ=.sha256","tf-message.js":"&oQggZN26PtRi4Ce9HY8TKVJ9jRrYWLRE5tN+3yHwEJE=.sha256","tf.js":"&EgrrFfINhqFL/Kj8qkJwH/DHTLrZ8CxhHDBxZjcyjUY=.sha256","commonmark.min.js":"&5x6ek3tFrKTZX6hXNNyFsjmhvrjmWpUkwuuaiyVV1Us=.sha256","vue.js":"&g1wvA+yHl1sVC+eufTsg9If7ZeVyMTBU+h0tks7ZNzE=.sha256","vue-material-theme-default-dark.css":"&RP2nr+2CR18BpHHw5ST9a5GJUCOG9n0G2kuGkcQioWE=.sha256","vue-material.min.css":"&kGbUM2QgFSyHZRzqQb0b+0S3EVIlZ0AXpdiAVjIhou8=.sha256","roboto.css":"&jJv43Om673mQO5JK0jj7714s5E+5Yrf82H6LcDx7wUs=.sha256","material-icons.css":"&a28PdcVvgq/DxyIvJAx/e+ZOEtOuHnr3kjLWKyzH11M=.sha256"}}
|
||||
{"type":"tildefriends-app","files":{"app.js":"&SJrK87ZgHkxZQDB1nm/Xej/bzajzWpiY9a384zqZVzE=.sha256","index.html":"&O8+Vr4YHP8NIORZzM3Ee9tyLXr2xCETT2Eva0rO5ebA=.sha256","vue-material.js":"&K5cdLqXYCENPak/TCINHQhyJhpS4G9DlZHGwoh/LF2g=.sha256","tf-user.js":"&DdJwZYEo7AqFyutYMvEjykoVXxdHVog0UXye6Sbo0TU=.sha256","tf-message.js":"&ox9wj2+P3BhqokfHPI3ZMpSYNpNhl91K5R6Z4GbWxCA=.sha256","tf.js":"&rUCI1QKOoyWcFegV72DfG/P7HykmkX4DcvWV87hCeBM=.sha256","commonmark.min.js":"&5x6ek3tFrKTZX6hXNNyFsjmhvrjmWpUkwuuaiyVV1Us=.sha256","vue.js":"&g1wvA+yHl1sVC+eufTsg9If7ZeVyMTBU+h0tks7ZNzE=.sha256","vue-material-theme-default-dark.css":"&RP2nr+2CR18BpHHw5ST9a5GJUCOG9n0G2kuGkcQioWE=.sha256","vue-material.min.css":"&kGbUM2QgFSyHZRzqQb0b+0S3EVIlZ0AXpdiAVjIhou8=.sha256","roboto.css":"&jJv43Om673mQO5JK0jj7714s5E+5Yrf82H6LcDx7wUs=.sha256","material-icons.css":"&a28PdcVvgq/DxyIvJAx/e+ZOEtOuHnr3kjLWKyzH11M=.sha256"}}
|
@ -3,6 +3,7 @@
|
||||
const k_posts_max = 20;
|
||||
const k_votes_max = 20;
|
||||
|
||||
var g_ready = false;
|
||||
var g_selected = null;
|
||||
|
||||
var g_following_cache = {};
|
||||
@ -240,6 +241,7 @@ async function getPosts(db, ids) {
|
||||
}
|
||||
|
||||
async function ready() {
|
||||
g_ready = true;
|
||||
return refresh(g_selected);
|
||||
}
|
||||
|
||||
@ -267,6 +269,7 @@ async function refresh(selected) {
|
||||
}
|
||||
await Promise.all([
|
||||
app.postMessage({whoami: whoami}),
|
||||
app.postMessage({hash: selected && selected.length == 1 ? selected[0] : null}),
|
||||
ssb.getBroadcasts().then(broadcasts => app.postMessage({broadcasts: broadcasts})),
|
||||
ssb.connections().then(connections => app.postMessage({connections: connections})),
|
||||
core.apps().then(apps => app.postMessage({apps: apps})),
|
||||
@ -293,6 +296,7 @@ async function refresh(selected) {
|
||||
];
|
||||
}))),
|
||||
]);
|
||||
await app.postMessage({ready: true});
|
||||
}
|
||||
|
||||
ssb.addEventListener('message', async function(id) {
|
||||
@ -308,7 +312,6 @@ ssb.addEventListener('message', async function(id) {
|
||||
});
|
||||
|
||||
core.register('message', async function(m) {
|
||||
print(JSON.stringify(m));
|
||||
if (m.message == 'ready') {
|
||||
await ready();
|
||||
} else if (m.message) {
|
||||
@ -321,9 +324,12 @@ core.register('message', async function(m) {
|
||||
}
|
||||
} else if (m.event == 'hashChange') {
|
||||
if (m.hash.length > 1) {
|
||||
refresh([m.hash.substring(1)]);
|
||||
g_selected = [m.hash.substring(1)];
|
||||
} else {
|
||||
refresh();
|
||||
g_selected = null;
|
||||
}
|
||||
if (g_ready) {
|
||||
await refresh(g_selected);
|
||||
}
|
||||
} else if (m.event == 'focus' || m.event == 'blur') {
|
||||
/* Shh. */
|
||||
|
@ -7,10 +7,10 @@
|
||||
<link rel="stylesheet" href="vue-material-theme-default-dark.css">
|
||||
<script src="vue.js"></script>
|
||||
<script src="vue-material.js"></script>
|
||||
<script src="commonmark.min.js"></script>
|
||||
<script src="tf-user.js"></script>
|
||||
<script src="tf-message.js"></script>
|
||||
<script src="tf.js"></script>
|
||||
<script src="commonmark.min.js"></script>
|
||||
</head>
|
||||
<body style="color: #fff">
|
||||
<div id="app">
|
||||
@ -38,8 +38,6 @@
|
||||
<md-subheader>Connections</md-subheader>
|
||||
<md-list-item v-for="connection in connections" v-bind:key="'connection-' + JSON.stringify(connection)"><tf-user :id="connection"></tf-user></md-list-item>
|
||||
<md-list-item @click="show_connect_dialog = true">Connect</md-list-item>
|
||||
<md-subheader>Users</md-subheader>
|
||||
<!-- <md-list-item v-for="user in Object.keys(users).sort((x, y) => (users[x].name || x).localeCompare(users[y].name || y))" v-bind:key="'user-' + user"><tf-user v-bind:id="user"/></md-list-item> -->
|
||||
</md-list>
|
||||
</md-app-drawer>
|
||||
<md-app-toolbar class="md-secondary" v-show="unread > 0">
|
||||
@ -74,6 +72,58 @@
|
||||
<md-button class="md-raised md-primary" v-on:click="post_message()">Submit Post</md-button>
|
||||
</md-card-actions>
|
||||
</md-card>
|
||||
|
||||
<md-button v-if="selected" class="md-raised md-primary" style="margin: 1em" @click="set_hash(null)">
|
||||
<md-icon>home</md-icon> Home
|
||||
</md-button>
|
||||
|
||||
<md-card v-if="selected && selected.charAt(0) == '@'" class="md-raised md-elevation-8" style="margin: 1em">
|
||||
<md-card-header>
|
||||
<md-card-header-text>
|
||||
<div class="md-title">{{users[selected] && users[selected].name ? users[selected].name : selected}}</div>
|
||||
<div class="md-subhead" v-if="users[selected] && users[selected].name">{{selected}}</div>
|
||||
</md-card-header-text>
|
||||
<md-card-media v-if="users[selected] && users[selected].image" class="md-medium">
|
||||
<div><img :src="'/' + (typeof(users[selected].image) == 'string' ? users[selected].image : users[selected].image.link) + '/view'"></div>
|
||||
</md-card-media>
|
||||
</md-card-header>
|
||||
<md-card-content>
|
||||
<div v-if="selected == whoami">
|
||||
<md-field>
|
||||
<label>Name</label>
|
||||
<md-input v-model="edit_profile_name"></md-input>
|
||||
</md-field>
|
||||
<md-field>
|
||||
<label>Description</label>
|
||||
<md-textarea v-model="edit_profile_description"></md-textarea>
|
||||
</md-field>
|
||||
</div>
|
||||
<template v-if="users[selected]">
|
||||
<div v-if="users[selected].name">{{selected}}</div>
|
||||
<div v-html="markdown(users[selected].description)"></div>
|
||||
</template>
|
||||
<md-card-actions>
|
||||
<md-menu md-size="small" v-if="users[selected] && users[selected].followers">
|
||||
<md-button md-menu-trigger>{{Object.keys(users[selected].followers).length}} followers</md-button>
|
||||
<md-menu-content>
|
||||
<md-menu-item v-for="id of Object.keys(users[selected].followers)"><tf-user :id="id"></tf-user></md-menu-item>
|
||||
</md-menu-content>
|
||||
</md-menu>
|
||||
<md-menu md-size="small" v-if="users[selected] && users[selected].following">
|
||||
<md-button md-menu-trigger>{{Object.keys(users[selected].following).length}} following</md-button>
|
||||
<md-menu-content>
|
||||
<md-menu-item v-for="id of Object.keys(users[selected].following)"><tf-user :id="id"></tf-user></md-menu-item>
|
||||
</md-menu-content>
|
||||
</md-menu>
|
||||
<template v-if="selected != whoami && users[whoami]">
|
||||
<md-button @click="follow(selected)" v-if="!users[whoami].following[selected]" class="md-raised md-secondary">Follow</md-button>
|
||||
<md-button @click="unfollow(selected)" v-else class="md-raised md-secondary">Unfollow</md-button>
|
||||
</template>
|
||||
<md-button @click="save_profile" v-if="selected == whoami" class="md-primary md-raised">Save Profile</md-button>
|
||||
</md-card-actions>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<template v-if="messages.length">
|
||||
<tf-message
|
||||
v-for="message in messages"
|
||||
|
@ -27,7 +27,10 @@ Vue.component('tf-message', {
|
||||
<h3>
|
||||
<tf-user :id="message.author"></tf-user>
|
||||
</h3>
|
||||
<div style="font-size: x-small">{{new Date(message.timestamp)}}</div>
|
||||
<div style="font-size: x-small">
|
||||
{{new Date(message.timestamp)}}
|
||||
<md-tooltip>{{message.id}}</md-tooltip>
|
||||
</div>
|
||||
<div class="md-toolbar-section-end">
|
||||
<md-menu>
|
||||
<md-button md-menu-trigger class="md-icon-button"><md-icon>more_vert</md-icon></md-button>
|
||||
|
@ -1,11 +1,5 @@
|
||||
"use strict";
|
||||
Vue.component('tf-user', {
|
||||
data: function() { return {
|
||||
show_user_dialog: false,
|
||||
show_follow_dialog: false,
|
||||
edit_profile_name: null,
|
||||
edit_profile_description: null,
|
||||
} },
|
||||
props: ['id'],
|
||||
computed: {
|
||||
following: {
|
||||
@ -15,91 +9,17 @@ Vue.component('tf-user', {
|
||||
g_data.users[g_data.whoami].following &&
|
||||
g_data.users[g_data.whoami].following[this.id];
|
||||
},
|
||||
set: function(newValue) {
|
||||
var already_following =
|
||||
g_data.users &&
|
||||
g_data.users[g_data.whoami] &&
|
||||
g_data.users[g_data.whoami].following &&
|
||||
g_data.users[g_data.whoami].following[this.id];
|
||||
if (newValue && !already_following) {
|
||||
if (confirm("Are you sure you want to follow " + this.id + "?")) {
|
||||
window.parent.postMessage({appendMessage: {type: "contact", following: true, contact: this.id}}, '*');
|
||||
}
|
||||
} else if (!newValue && already_following) {
|
||||
if (confirm("Are you sure you want to unfollow " + this.id + "?")) {
|
||||
window.parent.postMessage({appendMessage: {type: "contact", following: false, contact: this.id}}, '*');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
whoami: { get: function() { return g_data.whoami; } },
|
||||
users: { get: function() { return g_data.users; } },
|
||||
},
|
||||
methods: {
|
||||
save_profile: function() {
|
||||
var message = {appendMessage: {
|
||||
type: 'about',
|
||||
about: this.id,
|
||||
name: this.edit_profile_name,
|
||||
description: this.edit_profile_description,
|
||||
}};
|
||||
window.parent.postMessage(message, '*');
|
||||
},
|
||||
show_user: function() {
|
||||
window.parent.postMessage({action: 'setHash', hash: this.id}, '*');
|
||||
if (this.id == this.whoami) {
|
||||
this.edit_profile_name = this.users[this.id].name;
|
||||
this.edit_profile_description = this.users[this.id].description;
|
||||
}
|
||||
},
|
||||
},
|
||||
template: `<span>
|
||||
<md-chip md-clickable :class="following ? 'md-accent' : ''" @click="show_user()">
|
||||
template: `<md-chip md-clickable :class="following ? 'md-accent' : ''" @click="show_user()">
|
||||
{{users[id] && users[id].name ? users[id].name : id}}
|
||||
</md-chip>
|
||||
<md-tooltip v-if="users[id] && users[id].name">{{id}}</md-tooltip>
|
||||
<md-dialog :md-active.sync="show_user_dialog">
|
||||
<md-dialog-title>{{users[id] && users[id].name ? users[id].name : id}}</md-dialog-title>
|
||||
<md-dialog-content>
|
||||
<div v-if="id == whoami">
|
||||
<md-field>
|
||||
<label>Name</label>
|
||||
<md-input v-model="edit_profile_name"></md-input>
|
||||
</md-field>
|
||||
<md-field>
|
||||
<label>Description</label>
|
||||
<md-textarea v-model="edit_profile_description"></md-textarea>
|
||||
</md-field>
|
||||
</div>
|
||||
<template v-if="users[id]">
|
||||
<div v-if="users[id].image"><img :src="'/' + users[id].image + '/view'"></div>
|
||||
<div v-if="users[id].name">{{id}}</div>
|
||||
<div>{{users[id].description}}</div>
|
||||
<div><md-switch v-model="following">Following</md-switch></div>
|
||||
<div class="md-layout">
|
||||
<div class="md-layout-item">
|
||||
<md-list>
|
||||
<md-subheader>Followers</md-subheader>
|
||||
<md-list-item v-for="follower in Object.keys((users[id] && users[id].followers) ? users[id].followers : {})" v-bind:key="'follower-' + follower">
|
||||
<tf-user :id="follower"></tf-user>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</div>
|
||||
<div class="md-layout-item">
|
||||
<md-list>
|
||||
<md-subheader>Following</md-subheader>
|
||||
<md-list-item v-for="user in Object.keys((users[id] && users[id].following) ? users[id].following : {})" v-bind:key="'following-' + user">
|
||||
<tf-user :id="user"></tf-user>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</md-dialog-content>
|
||||
<md-dialog-actions>
|
||||
<md-button @click="save_profile" v-if="id == whoami">Save Profile</md-button>
|
||||
<md-button @click="show_user_dialog = false">Close</md-button>
|
||||
</md-dialog-actions>
|
||||
</md-dialog>
|
||||
</span>`,
|
||||
<md-tooltip v-if="users[id] && users[id].name">{{id}}</md-tooltip>
|
||||
</md-chip>`,
|
||||
});
|
@ -16,12 +16,20 @@ var g_data = {
|
||||
mentions: {},
|
||||
unread: 0,
|
||||
loading: true,
|
||||
selected: null,
|
||||
edit_profile_name: null,
|
||||
edit_profile_description: null,
|
||||
};
|
||||
|
||||
var g_data_initial = JSON.parse(JSON.stringify(g_data));
|
||||
var g_message_queue = [];
|
||||
var g_process_pending = false;
|
||||
|
||||
function updateEditUser() {
|
||||
g_data.edit_profile_name = g_data.users[g_data.whoami] ? g_data.users[g_data.whoami].name : null;
|
||||
g_data.edit_profile_description = g_data.users[g_data.whoami] ? g_data.users[g_data.whoami].description : null;
|
||||
}
|
||||
|
||||
function processMessages() {
|
||||
for (let event of g_message_queue) {
|
||||
var key = Object.keys(event.data)[0];
|
||||
@ -56,6 +64,9 @@ function processMessages() {
|
||||
g_data[key + 's'].push(event.data[key]);
|
||||
} else if (key == 'user') {
|
||||
Vue.set(g_data.users, event.data.user.user, Object.assign({}, g_data.users[event.data.user.user] || {}, event.data.user.about));
|
||||
if (event.data.user.user == g_data.whoami) {
|
||||
updateEditUser();
|
||||
}
|
||||
} else if (key == 'followers') {
|
||||
if (!g_data.users[event.data.followers.id]) {
|
||||
Vue.set(g_data.users, event.data.followers.id, {});
|
||||
@ -104,7 +115,10 @@ function processMessages() {
|
||||
} else if (key == 'unread') {
|
||||
g_data.unread += event.data.unread;
|
||||
} else if (key == 'hash') {
|
||||
console.log(event.data);
|
||||
g_data.selected = event.data.hash;
|
||||
if (g_data.selected == g_data.whoami) {
|
||||
updateEditUser();
|
||||
}
|
||||
} else {
|
||||
g_data[key] = event.data[key];
|
||||
}
|
||||
@ -155,6 +169,14 @@ window.addEventListener('load', function() {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
markdown: function(md) {
|
||||
if (!md) {
|
||||
return;
|
||||
}
|
||||
var reader = new commonmark.Parser({safe: true});
|
||||
var writer = new commonmark.HtmlRenderer();
|
||||
return writer.render(reader.parse(md));
|
||||
},
|
||||
refresh: function() {
|
||||
window.parent.postMessage({refresh: true}, '*');
|
||||
},
|
||||
@ -168,6 +190,28 @@ window.addEventListener('load', function() {
|
||||
remove_from_mentions: function(link) {
|
||||
Vue.delete(g_data.mentions, link);
|
||||
},
|
||||
save_profile: function() {
|
||||
var message = {appendMessage: {
|
||||
type: 'about',
|
||||
about: g_data.selected,
|
||||
name: g_data.edit_profile_name,
|
||||
description: g_data.edit_profile_description,
|
||||
}};
|
||||
window.parent.postMessage(message, '*');
|
||||
},
|
||||
follow: function(id) {
|
||||
if (confirm('Are you sure you want to follow ' + id + '?')) {
|
||||
window.parent.postMessage({appendMessage: {type: "contact", following: true, contact: id}}, '*');
|
||||
}
|
||||
},
|
||||
unfollow: function(id) {
|
||||
if (confirm('Are you sure you want to unfollow ' + id + '?')) {
|
||||
window.parent.postMessage({appendMessage: {type: "contact", following: false, contact: id}}, '*');
|
||||
}
|
||||
},
|
||||
set_hash(hash) {
|
||||
window.parent.postMessage({action: 'setHash', hash: hash ? hash : '#'}, '*');
|
||||
},
|
||||
}
|
||||
});
|
||||
window.parent.postMessage('ready', '*');
|
||||
|
Loading…
Reference in New Issue
Block a user