add basic editor state

This commit is contained in:
tsukino
2023-12-27 19:28:16 -08:00
parent 024939396e
commit fca5b6e4bd
13 changed files with 155 additions and 54 deletions

4
.gitignore vendored
View File

@@ -8,5 +8,5 @@ coverage/
dev-build/
bin/
.autism/
#**/src/**/*.css
#**/src/**/*.css.map
**/src/**/*.css
**/src/**/*.css.map

View File

@@ -95,7 +95,7 @@ export class ObservableMap<keyType = string, ValueType = any> {
}
}
export function obervable(target: any, key: string) {
export function observable(target: any, key: string) {
const currentValue = target[key];
let currentStore = new Observable(currentValue);

View File

@@ -311,7 +311,7 @@ type DOMOptions = {
onclick?(): void;
} & { [key: string]: string };
type VNodeOption = VNode | string | (() => VNode | VNode[]);
type VNodeOption = VNode | string | (() => VNode | VNode[]) | boolean;
type VNodeOptions = VNodeOption | VNodeOption[];
export const h = (
@@ -391,6 +391,10 @@ export const h = (
return [new VNode({ tagName: 'text', content: nodeOrText })];
}
if (typeof nodeOrText === 'boolean') {
return [];
}
const nodes = nodeOrText();
let retNodes = nodes;
@@ -480,3 +484,9 @@ export function disabled(bool?: any): { disabled?: 'true' } {
if (!!bool) obj.disabled = 'true';
return obj;
}
export function boolAttr(key: string, bool?: any): { [key: string]: 'true' } {
const obj: { [key: string]: 'true' } = {};
if (!!bool) obj[key] = 'true';
return obj;
}

View File

@@ -1,11 +1,12 @@
button {
transition: opacity 100ms ease-in-out;
button,
.button {
display: var(--display, flex);
flex-flow: var(--flex-flow, row);
align-items: var(--align-items, center);
padding: var(--padding, .25rem .5rem);
cursor: pointer;
border: var(--border, 1px solid var(--slate-200));
border-radius: var(--border-radius);
gap: var(--gap, 0);
font-family: var(--font-family, var(--font-sans));
font-size: var(--font-size, .875rem);
@@ -13,6 +14,7 @@ button {
background-color: var(--background-color, var(--slate-100));
width: var(--width);
opacity: var(--opacity, .9);
font-weight: var(--font-weight, var(--font-normal));
&:hover {
opacity: var(--opacity, .7);

View File

@@ -5,8 +5,11 @@ export default class Button extends CustomElement {
css = css.toString();
render() {
const { disabled } = this.state;
const btnProps: any = { disabled };
const { disabled, active } = this.state;
const btnProps: any = {
disabled,
className: active ? 'button--active' : '',
};
if (!disabled) {
delete btnProps.disabled;
@@ -14,7 +17,7 @@ export default class Button extends CustomElement {
}
// @ts-ignore
return h('button', btnProps, h('slot'));
return h('button.button', btnProps, h('slot'));
}
}

View File

@@ -37,7 +37,6 @@
}
textarea.content {
transition: background-color 100ms ease-in-out, border 100ms ease-in-out;
outline: none;
grid-column-start: 1;
grid-column-end: 3;

View File

@@ -11,19 +11,20 @@ import { userId, userName } from '../../utils/misc.ts';
import { MessageType, Post, PostSubtype } from '@message';
import { Observable } from '../../../lib/state.ts';
import css from './index.scss';
import $editor from '../../state/editor.ts';
@connect(() => {
const content = new Observable('');
return {
content,
ecdsa: $signer.$ecdsa,
reference: $editor.reference,
};
})
export default class Editor extends CustomElement {
css = css.toString();
onSubmit = () => {
const creator = $signer.publicKey;
const creator = this.state.creator;
const content = this.$.content.$;
const post = new Post({
@@ -56,12 +57,16 @@ export default class Editor extends CustomElement {
};
render(): VNode {
const creator = $signer.publicKey;
const name = userName($signer.publicKey) || 'Anonymous';
const handle = userId($signer.publicKey) || '';
const creator = this.state.creator;
const name = userName(creator) || 'Anonymous';
const handle = userId(creator) || '';
return h(
'div.editor',
!!$editor.reference.$ &&
h('post-card', {
hash: $editor.reference.$,
}),
h(
'div.post',
h('profile-image', {

View File

@@ -1,33 +1,39 @@
import { CustomElement, h, register } from '../../../lib/ui.ts';
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 $node from '../../state/node.ts';
@connect(() => ({
ecdsa: $signer.$ecdsa,
}))
export default class LeftSidebar extends CustomElement {
css = css.toString();
onSubmit = async (e: CustomEvent) => {
const { post, reset } = e.detail;
if (post) {
if ($signer.$ecdsa.$?.privateKey) {
post.commit({
type: ProofType.ECDSA,
value: $signer.$ecdsa.$.sign(post.hash),
});
await $node.node.publish(post);
reset();
}
}
};
render() {
return h(
`div.left-sidebar`,
// @ts-ignore
h('post-editor', {
onsubmit: async (e: CustomEvent) => {
const { post, reset } = e.detail;
if (post) {
if ($signer.$ecdsa.$?.privateKey) {
post.commit({
type: ProofType.ECDSA,
value: $signer.$ecdsa.$.sign(post.hash),
});
await $node.node.publish(post);
reset();
}
}
},
creator: $signer.$ecdsa.$?.publicKey,
onsubmit: this.onSubmit,
}),
h('c-button[disabled=true]', 'Import Private Key'),
h(

View File

@@ -73,41 +73,79 @@ profile-image {
.comment-btn,
.like-btn,
.repost-btn {
--background-color: transparent;
--border: 1px solid transparent;
--gap: .5rem;
--font-family: var(--font-mono);
--font-size: var(--text-base);
--padding: .125rem .25rem;
--opacity: .85;
--padding: .25rem .375rem;
--border-radius: .25rem;
--opacity: 1;
img {
width: var(--text-base);
height: var(--text-base);
opacity: .5;
}
&:hover {
--opacity: .7;
img {
opacity: .75;
}
}
&[active=true],
&:active {
--opacity: 1;
--font-weight: var(--font-medium);
img {
opacity: 1;
}
}
}
}
.comment-btn {
--color: var(--blue-600);
--background-color: var(--blue-100);
--border: 1px solid var(--blue-400);
--color: var(--blue-200);
&:hover {
--background-color: var(--blue-100);
--color: var(--blue-400);
}
&[active=true],
&:active {
--background-color: var(--blue-200);
--color: var(--blue-600);
}
}
.repost-btn {
--color: var(--green-600);
--background-color: var(--green-100);
--border: 1px solid var(--green-400);
--color: var(--green-200);
&:hover {
--background-color: var(--green-100);
--color: var(--green-400);
}
&[active=true],
&:active {
--background-color: var(--green-200);
--color: var(--green-600);
}
}
.like-btn {
--color: var(--red-600);
--background-color: var(--red-100);
--border: 1px solid var(--red-400);
--color: var(--red-200);
&:hover {
--background-color: var(--red-100);
--color: var(--red-400);
}
&[active=true],
&:active {
--background-color: var(--red-200);
--color: var(--red-600);
}
}

View File

@@ -1,4 +1,10 @@
import { connect, CustomElement, h, register } from '../../../lib/ui.ts';
import {
boolAttr,
connect,
CustomElement,
h,
register,
} from '../../../lib/ui.ts';
import { fromNow, userId, userName } from '../../utils/misc.ts';
import CommentIcon from '../../../static/icons/comment.svg';
import RepostIcon from '../../../static/icons/repost.svg';
@@ -7,6 +13,7 @@ import '../ProfileImage';
import '../Button';
import css from './index.scss';
import $node from '../../state/node.ts';
import $editor from '../../state/editor.ts';
@connect((el) => {
const hash = el.state.hash;
@@ -15,6 +22,7 @@ import $node from '../../state/node.ts';
return {
post,
user,
reference: $editor.reference,
};
})
export default class Post extends CustomElement {
@@ -25,18 +33,18 @@ export default class Post extends CustomElement {
css = css.toString();
comment = () => {
console.log('comment button clicked: ', this.state.hash);
$editor.reference.$ = this.state.hash;
};
render() {
const p = this.$.post?.$;
const u = this.$.user?.$;
const p = $node.$posts.get(this.state.hash);
const u = $node.$users.get(p.$?.creator || '');
const creator = p?.json.creator || '';
const createat = fromNow(p?.json.createdAt) || '';
const content = p?.json.content || '';
const name = u?.name || userName(p?.json.creator) || 'Anonymous';
const handle = userId(p?.json.creator) || '';
const creator = p.$?.json.creator || '';
const createat = fromNow(p.$?.json.createdAt) || '';
const content = p.$?.json.content || '';
const name = u.$?.name || userName(p.$?.json.creator) || 'Anonymous';
const handle = userId(p.$?.json.creator) || '';
return h(
'div.post',
@@ -56,6 +64,7 @@ export default class Post extends CustomElement {
'c-button.comment-btn',
// @ts-ignore
{
...boolAttr('active', $editor.reference.$ === this.state.hash),
onclick: this.comment,
},
h('img', { src: CommentIcon }),

View File

@@ -0,0 +1,10 @@
import { Observable, ObservableMap } from '../../lib/state';
export class Editor {
reference = new Observable('');
drafts = new ObservableMap<string, { content: string }>();
}
const $editor = new Editor();
export default $editor;

View File

@@ -1,5 +1,5 @@
import { Observable } from '../../lib/state.ts';
import { ECDSA } from '@autismjs/crypto/src';
import { ECDSA } from '@crypto';
export class Signer {
$ecdsa: Observable<ECDSA | null> = new Observable<ECDSA | null>(null);

View File

@@ -1,6 +1,25 @@
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import updateLocale from 'dayjs/plugin/updateLocale';
dayjs.extend(relativeTime);
dayjs.extend(updateLocale);
dayjs.updateLocale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
s: '%ds',
m: '1m',
mm: '%dm',
h: '1h',
hh: '%dh',
d: '1d',
dd: '%dd',
M: '1mo',
MM: '%dmo',
y: '1y',
yy: '%dy',
},
});
export function userId(pubkey?: string) {
if (!pubkey) return null;