mirror of
https://github.com/autismjs/monorepo.git
synced 2026-01-10 05:08:07 -05:00
add reply
This commit is contained in:
@@ -8,6 +8,11 @@ export type UserProfileData = {
|
||||
meta: { [k: string]: string };
|
||||
};
|
||||
|
||||
export type PostMeta = {
|
||||
moderations: { [subtype: string]: number };
|
||||
replies: number;
|
||||
};
|
||||
|
||||
export interface BaseDBAdapter {
|
||||
insertMessage(message: Any): Promise<Any | null>;
|
||||
getPosts(options?: {
|
||||
@@ -42,10 +47,7 @@ export interface BaseDBAdapter {
|
||||
},
|
||||
): Promise<Any[]>;
|
||||
getProfile(user: string): Promise<UserProfileData>;
|
||||
getPostMeta(reference: string): Promise<{
|
||||
moderations: { [subtype: string]: number };
|
||||
replies: number;
|
||||
}>;
|
||||
getPostMeta(reference: string): Promise<PostMeta>;
|
||||
getUserMeta(user: string): Promise<{
|
||||
outgoingConnections: { [subtype: string]: number };
|
||||
incomingConnections: { [subtype: string]: number };
|
||||
|
||||
@@ -126,10 +126,14 @@ export default class LevelDBAdapter implements BaseDBAdapter {
|
||||
const msg = message as Post;
|
||||
|
||||
if (msg.reference) {
|
||||
await this.#indices.thread
|
||||
.sublevel(msg.reference.split('/')[1], { valueEncoding: 'json' })
|
||||
.sublevel(MessageType[msg.type], { valueEncoding: 'json' })
|
||||
.put(time, message.hash);
|
||||
const hash = msg.reference.split('/')[1];
|
||||
|
||||
if (hash) {
|
||||
await this.#indices.thread
|
||||
.sublevel(msg.reference.split('/')[1], { valueEncoding: 'json' })
|
||||
.sublevel(MessageType[msg.type], { valueEncoding: 'json' })
|
||||
.put(time, message.hash);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -319,8 +323,12 @@ export default class LevelDBAdapter implements BaseDBAdapter {
|
||||
}
|
||||
};
|
||||
|
||||
const hash = reference.split('/')[1] || reference;
|
||||
|
||||
if (!hash) return [];
|
||||
|
||||
const db = this.#indices.thread
|
||||
.sublevel(reference.split('/')[1])
|
||||
.sublevel(hash)
|
||||
.sublevel(MessageType[MessageType.Post]);
|
||||
|
||||
return this.#query(db, predicate, options);
|
||||
@@ -342,8 +350,12 @@ export default class LevelDBAdapter implements BaseDBAdapter {
|
||||
);
|
||||
};
|
||||
|
||||
const hash = reference.split('/')[1] || reference;
|
||||
|
||||
if (!hash) return [];
|
||||
|
||||
const db = this.#indices.thread
|
||||
.sublevel(reference.split('/')[1])
|
||||
.sublevel(hash)
|
||||
.sublevel(MessageType[MessageType.Moderation]);
|
||||
|
||||
return this.#query(db, predicate, options);
|
||||
|
||||
@@ -232,6 +232,22 @@ export class VNode {
|
||||
}
|
||||
}
|
||||
|
||||
if (newNode.classList.length) {
|
||||
for (const className of newNode.classList) {
|
||||
if (!lastEl.classList.contains(className)) {
|
||||
lastEl.classList.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastEl.classList.length) {
|
||||
for (const className of Array.from(lastEl.classList)) {
|
||||
if (!newNode.classList.includes(className)) {
|
||||
lastEl.classList.remove(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
lastEl.replaceWith(newNode.createElement());
|
||||
return;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
@import "../Post/index.scss";
|
||||
|
||||
.editor {
|
||||
.post {
|
||||
grid-template-columns: auto auto;
|
||||
grid-template-rows: auto auto auto auto;
|
||||
background-color: var(--white);
|
||||
|
||||
&__post {
|
||||
grid-template-columns: 4.5rem auto;
|
||||
grid-template-rows: 4.5rem auto auto auto;
|
||||
padding: 0;
|
||||
|
||||
profile-image {
|
||||
@@ -12,16 +14,18 @@
|
||||
grid-row-start: 1;
|
||||
grid-row-end: 2;
|
||||
--margin: 0 .5rem .5rem 0;
|
||||
--box-shadow: var(--shadow);
|
||||
padding: 1rem 0 0 1rem;
|
||||
}
|
||||
|
||||
.top {
|
||||
grid-column-start: 1;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-start;
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 3;
|
||||
grid-row-start: 2;
|
||||
grid-row-start: 1;
|
||||
grid-row-end: 3;
|
||||
padding: 0 1rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
@@ -45,7 +49,7 @@
|
||||
background-color: var(--white);
|
||||
font-size: var(--text-sm);
|
||||
font-family: var(--font-sans);
|
||||
margin: 0.5rem;
|
||||
margin: 1rem .5rem .5rem;
|
||||
border: 1px solid var(--slate-100);
|
||||
border-radius: 4px;
|
||||
resize: none;
|
||||
@@ -89,4 +93,80 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post-card.parent {
|
||||
--padding-top: 1rem;
|
||||
--bottom-display: none;
|
||||
--margin: 0 .5rem .5rem .5rem;
|
||||
}
|
||||
|
||||
.ref {
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__text {
|
||||
&--cancel {
|
||||
display: none;
|
||||
}
|
||||
&--reply {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__desc {
|
||||
transition: width 200ms ease-in-out, padding 200ms ease-in-out;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: var(--text-sm);
|
||||
margin-left: 4.5rem;
|
||||
color: var(--blue-500);
|
||||
font-weight: var(--font-medium);
|
||||
|
||||
.xmark {
|
||||
width: 0;
|
||||
margin-right: 0.25rem;
|
||||
margin-top: 0.125rem;
|
||||
flex: 0 0 auto;
|
||||
border-radius: 1.25rem;
|
||||
background: var(--red-100);
|
||||
height: 1.25rem;
|
||||
padding: 0.0625rem 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--red-500);
|
||||
|
||||
.xmark {
|
||||
width: 1.25rem;
|
||||
padding: 0.0625rem 0.125rem;
|
||||
}
|
||||
|
||||
.ref__text {
|
||||
&--reply {
|
||||
display: none;
|
||||
}
|
||||
&--cancel {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__connector {
|
||||
width: 2px;
|
||||
background: var(--slate-100);
|
||||
height: auto;
|
||||
position: absolute;
|
||||
top: 4.625rem;
|
||||
left: 2.375rem;
|
||||
bottom: -1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { MessageType, Post, PostSubtype } from '@message';
|
||||
import { Observable } from '../../../lib/state.ts';
|
||||
import css from './index.scss';
|
||||
import $editor from '../../state/editor.ts';
|
||||
import XmarkIcon from '../../../static/icons/xmark.svg';
|
||||
|
||||
@connect(() => {
|
||||
const content = new Observable('');
|
||||
@@ -60,15 +61,42 @@ export default class Editor extends CustomElement {
|
||||
const creator = this.state.creator;
|
||||
const name = userName(creator) || 'Anonymous';
|
||||
const handle = userId(creator) || '';
|
||||
const [_c, _h] = $editor.reference.$.split('/');
|
||||
const hash = _h || _c;
|
||||
const parentCreator = _h ? _c : '';
|
||||
const parentHandle = userId(parentCreator) || '';
|
||||
|
||||
return h(
|
||||
'div.editor',
|
||||
!!$editor.reference.$ &&
|
||||
h('post-card', {
|
||||
hash: $editor.reference.$,
|
||||
}),
|
||||
h(
|
||||
'div.post',
|
||||
'div.ref',
|
||||
{
|
||||
className: !hash ? 'ref--hidden' : '',
|
||||
},
|
||||
h('post-card.parent', {
|
||||
hash: hash,
|
||||
}),
|
||||
h(
|
||||
'div.ref__desc',
|
||||
// @ts-ignore
|
||||
{
|
||||
onclick: () => {
|
||||
$editor.reference.$ = '';
|
||||
},
|
||||
},
|
||||
h('img.xmark', {
|
||||
src: XmarkIcon,
|
||||
}),
|
||||
h(
|
||||
'span.ref__text.ref__text--cancel',
|
||||
`Cancel replying to ${parentHandle}`,
|
||||
),
|
||||
h('span.ref__text.ref__text--reply', `Replying to ${parentHandle}`),
|
||||
),
|
||||
h('div.ref__connector'),
|
||||
),
|
||||
h(
|
||||
'div.post.editor__post',
|
||||
h('profile-image', {
|
||||
creator: creator,
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.left-sidebar {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
width: 20rem;
|
||||
width: 24rem;
|
||||
flex: 1 0 auto;
|
||||
gap: .25rem;
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ import { connect, CustomElement, h, register } from '../../../lib/ui.ts';
|
||||
import css from './index.scss';
|
||||
import $signer from '../../state/signer.ts';
|
||||
import '../Editor';
|
||||
import { ProofType } from '@message';
|
||||
import { Post, PostSubtype, ProofType } from '@message';
|
||||
import $node from '../../state/node.ts';
|
||||
import $editor from '../../state/editor.ts';
|
||||
|
||||
@connect(() => ({
|
||||
ecdsa: $signer.$ecdsa,
|
||||
reference: $editor.reference,
|
||||
}))
|
||||
export default class LeftSidebar extends CustomElement {
|
||||
css = css.toString();
|
||||
@@ -14,14 +16,20 @@ export default class LeftSidebar extends CustomElement {
|
||||
onSubmit = async (e: CustomEvent) => {
|
||||
const { post, reset } = e.detail;
|
||||
|
||||
if (post) {
|
||||
const p = new Post({
|
||||
...post.json,
|
||||
subtype: $editor.reference.$ ? PostSubtype.Comment : PostSubtype.Default,
|
||||
reference: $editor.reference.$ || '',
|
||||
});
|
||||
|
||||
if (p) {
|
||||
if ($signer.$ecdsa.$?.privateKey) {
|
||||
post.commit({
|
||||
p.commit({
|
||||
type: ProofType.ECDSA,
|
||||
value: $signer.$ecdsa.$.sign(post.hash),
|
||||
value: $signer.$ecdsa.$.sign(p.hash),
|
||||
});
|
||||
|
||||
await $node.node.publish(post);
|
||||
await $node.node.publish(p);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
.post {
|
||||
display: grid;
|
||||
background: var(--white);
|
||||
grid-template-columns: 3.5rem auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
padding: .5rem;
|
||||
display: var(--display, grid);
|
||||
background: var(--background, var(--white));
|
||||
grid-template-columns: var(--grid-template-columns, 3.5rem auto);
|
||||
grid-template-rows: var(--grid-template-rows, auto auto auto);
|
||||
padding: var(--padding, .5rem);
|
||||
padding-top: var(--padding-top, .5rem);
|
||||
margin: var(--margin, 0);
|
||||
font-size: var(--font-size, var(--text-base));
|
||||
border: var(--border, none);
|
||||
cursor: default;
|
||||
cursor: var(--cursor, default);
|
||||
}
|
||||
|
||||
profile-image {
|
||||
@@ -58,11 +60,12 @@ profile-image {
|
||||
grid-row-end: 3;
|
||||
padding: .25rem 0;
|
||||
font-weight: var(--font-normal);
|
||||
color: var(--slate-900);
|
||||
line-height: 1.3125;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
display: var(--bottom-display, flex);
|
||||
flex-flow: row nowrap;
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 3;
|
||||
|
||||
@@ -19,10 +19,12 @@ import $editor from '../../state/editor.ts';
|
||||
const hash = el.state.hash;
|
||||
const post = $node.$posts.get(hash);
|
||||
const user = $node.$users.get(post.$?.creator || '');
|
||||
|
||||
return {
|
||||
post,
|
||||
user,
|
||||
reference: $editor.reference,
|
||||
postmeta: $node.$postmetas.get(hash),
|
||||
};
|
||||
})
|
||||
export default class Post extends CustomElement {
|
||||
@@ -33,12 +35,17 @@ export default class Post extends CustomElement {
|
||||
css = css.toString();
|
||||
|
||||
comment = () => {
|
||||
$editor.reference.$ = this.state.hash;
|
||||
const p = $node.$posts.get(this.state.hash);
|
||||
$editor.reference.$ =
|
||||
$editor.reference.$.split('/')[1] === this.state.hash
|
||||
? ''
|
||||
: p.$?.messageId || '';
|
||||
};
|
||||
|
||||
render() {
|
||||
const p = $node.$posts.get(this.state.hash);
|
||||
const u = $node.$users.get(p.$?.creator || '');
|
||||
const postmeta = $node.getPostMeta(this.state.hash);
|
||||
|
||||
const creator = p.$?.json.creator || '';
|
||||
const createat = fromNow(p.$?.json.createdAt) || '';
|
||||
@@ -46,6 +53,8 @@ export default class Post extends CustomElement {
|
||||
const name = u.$?.name || userName(p.$?.json.creator) || 'Anonymous';
|
||||
const handle = userId(p.$?.json.creator) || '';
|
||||
|
||||
const refHash = $editor.reference.$.split('/')[1] || $editor.reference.$;
|
||||
|
||||
return h(
|
||||
'div.post',
|
||||
h('profile-image', {
|
||||
@@ -64,11 +73,11 @@ export default class Post extends CustomElement {
|
||||
'c-button.comment-btn',
|
||||
// @ts-ignore
|
||||
{
|
||||
...boolAttr('active', $editor.reference.$ === this.state.hash),
|
||||
...boolAttr('active', refHash === this.state.hash),
|
||||
onclick: this.comment,
|
||||
},
|
||||
h('img', { src: CommentIcon }),
|
||||
h('span', '0'),
|
||||
h('span', `${postmeta?.replies || 0}`),
|
||||
),
|
||||
h('c-button.repost-btn', h('img', { src: RepostIcon }), h('span', '0')),
|
||||
h('c-button.like-btn', h('img', { src: LikeIcon }), h('span', '0')),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Observable, ObservableMap } from '../../lib/state.ts';
|
||||
import { Autism } from '@protocol/browser';
|
||||
import { Post } from '@message';
|
||||
import { UserProfileData } from '@autismjs/db/src/base.ts';
|
||||
import { PostMeta, UserProfileData } from '@autismjs/db/src/base.ts';
|
||||
import { equal } from '../utils/misc.ts';
|
||||
|
||||
export class NodeStore {
|
||||
node: Autism;
|
||||
@@ -10,11 +11,12 @@ export class NodeStore {
|
||||
$globalPosts = new Observable<string[]>([]);
|
||||
$posts = new ObservableMap<string, Post>();
|
||||
$users = new ObservableMap<string, UserProfileData>();
|
||||
$postmetas = new ObservableMap<string, PostMeta>();
|
||||
|
||||
constructor() {
|
||||
const node = new Autism({
|
||||
bootstrap: [
|
||||
'/ip4/192.168.86.24/tcp/63482/ws/p2p/12D3KooWJ4guEVPUD1zLBvbevx7h5aJHXfN9xokhUMKj4dM2pvUG',
|
||||
'/ip4/192.168.86.24/tcp/60336/ws/p2p/12D3KooWAeyUxK9NAudYufT2yadwDqK1KE5MTEYhkq2XMvzyFqs7',
|
||||
],
|
||||
});
|
||||
|
||||
@@ -57,6 +59,19 @@ export class NodeStore {
|
||||
});
|
||||
};
|
||||
|
||||
getPostMeta(messageId?: string) {
|
||||
if (!messageId) return null;
|
||||
|
||||
const store = this.$postmetas.get(messageId);
|
||||
|
||||
this.node.db.db.getPostMeta(messageId).then((meta) => {
|
||||
if (!equal(store.$, meta)) {
|
||||
store.$ = meta;
|
||||
}
|
||||
});
|
||||
return store.$;
|
||||
}
|
||||
|
||||
getPost(hash: string) {
|
||||
const store = this.$posts.get(hash);
|
||||
|
||||
|
||||
1
packages/web/static/icons/xmark.svg
Normal file
1
packages/web/static/icons/xmark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#EC7063" height="16" width="12" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>
|
||||
|
After Width: | Height: | Size: 548 B |
Reference in New Issue
Block a user