diff --git a/pkg/interface/src/logic/api/base.ts b/pkg/interface/src/logic/api/base.ts index 085c4cc32..01b70fabc 100644 --- a/pkg/interface/src/logic/api/base.ts +++ b/pkg/interface/src/logic/api/base.ts @@ -1,6 +1,5 @@ -import _ from "lodash"; -import { uuid } from "../lib/util"; -import { Patp, Path } from "~/types/noun"; +import _ from 'lodash'; +import { Patp, Path } from '@urbit/api'; import BaseStore from '../store/base'; export default class BaseApi { @@ -26,8 +25,8 @@ export default class BaseApi { data: event, from: { ship, - path, - }, + path + } }); }, (qui) => { @@ -50,8 +49,12 @@ export default class BaseApi { appl, mark, data, - (json) => { resolve(json); }, - (err) => { reject(err); } + (json) => { + resolve(json); +}, + (err) => { + reject(err); +} ); }); } @@ -69,5 +72,4 @@ export default class BaseApi { return res.json(); } - } diff --git a/pkg/interface/src/logic/api/contacts.ts b/pkg/interface/src/logic/api/contacts.ts index ca1f0a338..ae6a6972a 100644 --- a/pkg/interface/src/logic/api/contacts.ts +++ b/pkg/interface/src/logic/api/contacts.ts @@ -1,8 +1,7 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; -import { Patp, Path, Enc } from '~/types/noun'; -import { Contact, ContactEdit } from '~/types/contact-update'; -import { GroupPolicy, Resource } from '~/types/group-update'; +import { Patp } from '@urbit/api'; +import { ContactEdit } from '@urbit/api/contacts'; export default class ContactsApi extends BaseApi { add(ship: Patp, contact: any) { @@ -31,7 +30,7 @@ export default class ContactsApi extends BaseApi { ship, 'edit-field': editField, timestamp: Date.now() - }, + } }); } @@ -62,7 +61,7 @@ export default class ContactsApi extends BaseApi { return this.action( 'contact-push-hook', 'contact-share', - { share: recipient }, + { share: recipient } ); } @@ -85,7 +84,7 @@ export default class ContactsApi extends BaseApi { } private storeAction(action: any): Promise { - return this.action('contact-store', 'contact-update', action) + return this.action('contact-store', 'contact-update', action); } private viewAction(threadName: string, action: any) { diff --git a/pkg/interface/src/logic/api/global.ts b/pkg/interface/src/logic/api/global.ts index 8ed02020b..cc7c1291c 100644 --- a/pkg/interface/src/logic/api/global.ts +++ b/pkg/interface/src/logic/api/global.ts @@ -1,4 +1,4 @@ -import { Patp } from '~/types/noun'; +import { Patp } from '@urbit/api'; import BaseApi from './base'; import { StoreState } from '../store/type'; import GlobalStore from '../store/store'; @@ -10,7 +10,7 @@ import GroupsApi from './groups'; import LaunchApi from './launch'; import GraphApi from './graph'; import S3Api from './s3'; -import {HarkApi} from './hark'; +import { HarkApi } from './hark'; import SettingsApi from './settings'; export default class GlobalApi extends BaseApi { diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index 0606a2c1e..58b330da1 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -1,14 +1,14 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; -import { Patp, Path, PatpNoSig } from '~/types/noun'; +import { Patp, Path } from '@urbit/api'; import _ from 'lodash'; -import {makeResource, resourceFromPath} from '../lib/group'; -import {GroupPolicy, Enc, Post, NodeMap, Content, Resource} from '~/types'; +import { makeResource, resourceFromPath } from '../lib/group'; +import { GroupPolicy, Enc, Post, Content } from '@urbit/api'; import { numToUd, unixToDa, decToUd, deSig, resourceAsPath } from '~/logic/lib/util'; export const createBlankNodeWithChildPost = ( - parentIndex: string = '', - childIndex: string = '', + parentIndex = '', + childIndex = '', contents: Content[] ) => { const date = unixToDa(Date.now()).toString(); @@ -37,11 +37,11 @@ export const createBlankNodeWithChildPost = ( signatures: [] }, children: childGraph - }; + }; }; function markPending(nodes: any) { - _.forEach(nodes, node => { + _.forEach(nodes, (node) => { node.post.author = deSig(node.post.author); node.post.pending = true; markPending(node.children || {}); @@ -50,8 +50,8 @@ function markPending(nodes: any) { export const createPost = ( contents: Content[], - parentIndex: string = '', - childIndex:string = 'DATE_PLACEHOLDER' + parentIndex = '', + childIndex = 'DATE_PLACEHOLDER' ) => { if (childIndex === 'DATE_PLACEHOLDER') { childIndex = unixToDa(Date.now()).toString(); @@ -80,11 +80,10 @@ function moduleToMark(mod: string): string | undefined { } export default class GraphApi extends BaseApi { - joiningGraphs = new Set(); private storeAction(action: any): Promise { - return this.action('graph-store', 'graph-update', action) + return this.action('graph-store', 'graph-update', action); } private viewAction(threadName: string, action: any) { @@ -106,12 +105,12 @@ export default class GraphApi extends BaseApi { const resource = makeResource(`~${window.ship}`, name); return this.viewAction('graph-create', { - "create": { + 'create': { resource, title, description, associated, - "module": mod, + 'module': mod, mark: moduleToMark(mod) } }); @@ -127,12 +126,12 @@ export default class GraphApi extends BaseApi { const resource = makeResource(`~${window.ship}`, name); return this.viewAction('graph-create', { - "create": { + 'create': { resource, title, description, associated: { policy }, - "module": mod, + 'module': mod, mark: moduleToMark(mod) } }); @@ -148,9 +147,9 @@ export default class GraphApi extends BaseApi { return this.viewAction('graph-join', { join: { resource, - ship, + ship } - }).then(res => { + }).then((res) => { this.joiningGraphs.delete(rid); return res; }); @@ -159,7 +158,7 @@ export default class GraphApi extends BaseApi { deleteGraph(name: string) { const resource = makeResource(`~${window.ship}`, name); return this.viewAction('graph-delete', { - "delete": { + 'delete': { resource } }); @@ -168,7 +167,7 @@ export default class GraphApi extends BaseApi { leaveGraph(ship: Patp, name: string) { const resource = makeResource(ship, name); return this.viewAction('graph-leave', { - "leave": { + 'leave': { resource } }); @@ -203,7 +202,7 @@ export default class GraphApi extends BaseApi { } addPost(ship: Patp, name: string, post: Post) { - let nodes = {}; + const nodes = {}; nodes[post.index] = { post, children: null @@ -212,7 +211,7 @@ export default class GraphApi extends BaseApi { } addNode(ship: Patp, name: string, node: Object) { - let nodes = {}; + const nodes = {}; nodes[node.post.index] = node; return this.addNodes(ship, name, nodes); @@ -300,7 +299,6 @@ export default class GraphApi extends BaseApi { this.store.handleEvent({ data }); } - getGraphSubset(ship: string, resource: string, start: string, end: string) { return this.scry( 'graph-store', diff --git a/pkg/interface/src/logic/api/groups.ts b/pkg/interface/src/logic/api/groups.ts index de2f4b488..f0bab91f0 100644 --- a/pkg/interface/src/logic/api/groups.ts +++ b/pkg/interface/src/logic/api/groups.ts @@ -1,14 +1,14 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; -import { Path, Patp, Enc } from '~/types/noun'; +import { Path, Patp, Enc } from '@urbit/api'; import { GroupAction, GroupPolicy, Resource, Tag, - GroupPolicyDiff, -} from '~/types/group-update'; -import {makeResource} from '../lib/group'; + GroupPolicyDiff +} from '@urbit/api/groups'; +import { makeResource } from '../lib/group'; export default class GroupsApi extends BaseApi { remove(resource: Resource, ships: Patp[]) { @@ -38,7 +38,7 @@ export default class GroupsApi extends BaseApi { join(ship: string, name: string) { const resource = makeResource(ship, name); - return this.viewAction({ join: { resource, ship }}); + return this.viewAction({ join: { resource, ship } }); } create(name: string, policy: Enc, title: string, description: string) { @@ -76,7 +76,6 @@ export default class GroupsApi extends BaseApi { description } }); - } private proxyAction(action: GroupAction) { @@ -93,6 +92,5 @@ export default class GroupsApi extends BaseApi { private viewAction(action: any) { return this.action('group-view', 'group-view-action', action); - } } diff --git a/pkg/interface/src/logic/api/hark.ts b/pkg/interface/src/logic/api/hark.ts index 4dadeb169..b807201a1 100644 --- a/pkg/interface/src/logic/api/hark.ts +++ b/pkg/interface/src/logic/api/hark.ts @@ -1,24 +1,23 @@ -import BaseApi from "./base"; -import { StoreState } from "../store/type"; -import { dateToDa, decToUd } from "../lib/util"; -import {NotifIndex, IndexedNotification, Association, GraphNotifDescription} from "~/types"; +import BaseApi from './base'; +import { StoreState } from '../store/type'; +import { dateToDa, decToUd } from '../lib/util'; +import { NotifIndex, IndexedNotification, Association, GraphNotifDescription } from '@urbit/api'; import { BigInteger } from 'big-integer'; -import {getParentIndex} from "../lib/notification"; +import { getParentIndex } from '../lib/notification'; export class HarkApi extends BaseApi { private harkAction(action: any): Promise { - return this.action("hark-store", "hark-action", action); + return this.action('hark-store', 'hark-action', action); } private graphHookAction(action: any) { - return this.action("hark-graph-hook", "hark-graph-hook-action", action); + return this.action('hark-graph-hook', 'hark-graph-hook-action', action); } private groupHookAction(action: any) { - return this.action("hark-group-hook", "hark-group-hook-action", action); + return this.action('hark-group-hook', 'hark-group-hook-action', action); } - private actOnNotification(frond: string, intTime: BigInteger, index: NotifIndex) { const time = decToUd(intTime.toString()); return this.harkAction({ @@ -74,12 +73,10 @@ export class HarkApi extends BaseApi { module: association.metadata.module, description, index: parent - } }, + } } }); } - - markEachAsRead(association: Association, parent: string, child: string, description: GraphNotifDescription, mod: string) { return this.harkAction({ 'read-each': { @@ -116,7 +113,7 @@ export class HarkApi extends BaseApi { mute(notif: IndexedNotification) { if('graph' in notif.index && 'graph' in notif.notification.contents) { const { index } = notif; - const parentIndex = getParentIndex(index.graph, notif.notification.contents.graph) + const parentIndex = getParentIndex(index.graph, notif.notification.contents.graph); if(!parentIndex) { return Promise.resolve(); } @@ -132,7 +129,7 @@ export class HarkApi extends BaseApi { unmute(notif: IndexedNotification) { if('graph' in notif.index && 'graph' in notif.notification.contents) { const { index } = notif; - const parentIndex = getParentIndex(index.graph, notif.notification.contents.graph) + const parentIndex = getParentIndex(index.graph, notif.notification.contents.graph); if(!parentIndex) { return Promise.resolve(); } @@ -147,7 +144,7 @@ export class HarkApi extends BaseApi { ignoreGroup(group: string) { return this.groupHookAction({ ignore: group - }) + }); } ignoreGraph(graph: string, index: string) { @@ -156,13 +153,13 @@ export class HarkApi extends BaseApi { graph, index } - }) + }); } listenGroup(group: string) { return this.groupHookAction({ listen: group - }) + }); } listenGraph(graph: string, index: string) { @@ -171,7 +168,7 @@ export class HarkApi extends BaseApi { graph, index } - }) + }); } async getMore(): Promise { @@ -183,16 +180,16 @@ export class HarkApi extends BaseApi { async getSubset(offset:number, count:number, isArchive: boolean) { const where = isArchive ? 'archive' : 'inbox'; - const data = await this.scry("hark-store", `/recent/${where}/${offset}/${count}`); + const data = await this.scry('hark-store', `/recent/${where}/${offset}/${count}`); this.store.handleEvent({ data }); } async getTimeSubset(start?: Date, end?: Date) { - const s = start ? dateToDa(start) : "-"; - const e = end ? dateToDa(end) : "-"; - const result = await this.scry("hark-hook", `/recent/${s}/${e}`); + const s = start ? dateToDa(start) : '-'; + const e = end ? dateToDa(end) : '-'; + const result = await this.scry('hark-hook', `/recent/${s}/${e}`); this.store.handleEvent({ - data: result, + data: result }); } } diff --git a/pkg/interface/src/logic/api/invite.ts b/pkg/interface/src/logic/api/invite.ts index 89a730768..dc390d12d 100644 --- a/pkg/interface/src/logic/api/invite.ts +++ b/pkg/interface/src/logic/api/invite.ts @@ -1,6 +1,6 @@ -import BaseApi from "./base"; -import { StoreState } from "../store/type"; -import { Serial, Path } from "~/types/noun"; +import BaseApi from './base'; +import { StoreState } from '../store/type'; +import { Serial, Path } from '@urbit/api'; export default class InviteApi extends BaseApi { accept(app: string, uid: Serial) { diff --git a/pkg/interface/src/logic/api/launch.ts b/pkg/interface/src/logic/api/launch.ts index ce0a09a1c..3bba3b3cc 100644 --- a/pkg/interface/src/logic/api/launch.ts +++ b/pkg/interface/src/logic/api/launch.ts @@ -2,7 +2,7 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; export default class LaunchApi extends BaseApi { - add(name: string, tile = { basic : { title: '', linkedUrl: '', iconUrl: '' }}) { + add(name: string, tile = { basic : { title: '', linkedUrl: '', iconUrl: '' } }) { return this.launchAction({ add: { name, tile } }); } @@ -15,7 +15,7 @@ export default class LaunchApi extends BaseApi { } changeIsShown(name: string, isShown = true) { - return this.launchAction({ 'change-is-shown': { name, isShown }}); + return this.launchAction({ 'change-is-shown': { name, isShown } }); } weather(location: string) { diff --git a/pkg/interface/src/logic/api/local.ts b/pkg/interface/src/logic/api/local.ts index 8446f27ce..e5bad49a3 100644 --- a/pkg/interface/src/logic/api/local.ts +++ b/pkg/interface/src/logic/api/local.ts @@ -1,9 +1,9 @@ -import BaseApi from "./base"; -import { StoreState } from "../store/type"; +import BaseApi from './base'; +import { StoreState } from '../store/type'; export default class LocalApi extends BaseApi { getBaseHash() { - this.scry('file-server', '/clay/base/hash').then(baseHash => { + this.scry('file-server', '/clay/base/hash').then((baseHash) => { this.store.handleEvent({ data: { local: { baseHash } } }); }); } @@ -11,5 +11,4 @@ export default class LocalApi extends BaseApi { dehydrate() { this.store.dehydrate(); } - } diff --git a/pkg/interface/src/logic/api/metadata.ts b/pkg/interface/src/logic/api/metadata.ts index c4e20ae2f..c2d388dfc 100644 --- a/pkg/interface/src/logic/api/metadata.ts +++ b/pkg/interface/src/logic/api/metadata.ts @@ -1,12 +1,10 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; -import { Path, Patp, Association, Metadata, MetadataUpdatePreview } from '~/types'; -import {uxToHex} from '../lib/util'; +import { Path, Patp, Association, Metadata, MetadataUpdatePreview } from '@urbit/api'; +import { uxToHex } from '../lib/util'; export default class MetadataApi extends BaseApi { - - metadataAdd(appName: string, resource: Path, group: Path, title: string, description: string, dateCreated: string, color: string, moduleName: string) { const creator = `~${this.ship}`; return this.metadataAction({ @@ -44,9 +42,9 @@ export default class MetadataApi extends BaseApi { } update(association: Association, newMetadata: Partial) { - const metadata = {...association.metadata, ...newMetadata }; + const metadata = { ...association.metadata, ...newMetadata }; metadata.color = uxToHex(metadata.color); - return this.metadataAction({ + return this.metadataAction({ add: { group: association.group, resource: { @@ -69,10 +67,10 @@ export default class MetadataApi extends BaseApi { } done = true; tempChannel.delete(); - reject(new Error("offline")) + reject(new Error('offline')); }, 15000); - tempChannel.subscribe(window.ship, "metadata-pull-hook", `/preview${group}`, + tempChannel.subscribe(window.ship, 'metadata-pull-hook', `/preview${group}`, (err) => { console.error(err); reject(err); @@ -88,24 +86,22 @@ export default class MetadataApi extends BaseApi { } else { done = true; tempChannel.delete(); - reject(new Error("no-permissions")); + reject(new Error('no-permissions')); } }, (quit) => { tempChannel.delete(); if(!done) { - reject(new Error("offline")) + reject(new Error('offline')); } }, (a) => { console.log(a); } ); - }) + }); } - - private metadataAction(data) { return this.action('metadata-push-hook', 'metadata-update', data); } diff --git a/pkg/interface/src/logic/api/s3.ts b/pkg/interface/src/logic/api/s3.ts index 834cc6ffe..1d775ef77 100644 --- a/pkg/interface/src/logic/api/s3.ts +++ b/pkg/interface/src/logic/api/s3.ts @@ -1,10 +1,8 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; -import {S3Update} from '../../types/s3-update'; - +import { S3Update } from '../../types/s3-update'; export default class S3Api extends BaseApi { - setCurrentBucket(bucket: string) { return this.s3Action({ 'set-current-bucket': bucket }); } @@ -32,6 +30,5 @@ export default class S3Api extends BaseApi { private s3Action(data: any) { return this.action('s3-store', 's3-action', data); } - } diff --git a/pkg/interface/src/logic/api/settings.ts b/pkg/interface/src/logic/api/settings.ts index cd0cd736f..3f98d801c 100644 --- a/pkg/interface/src/logic/api/settings.ts +++ b/pkg/interface/src/logic/api/settings.ts @@ -1,13 +1,9 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; -import { - SettingsUpdate, - SettingsData, - Key, +import { Key, Value, - Bucket, -} from '~/types/settings'; - + Bucket +} from '@urbit/api/settings'; export default class SettingsApi extends BaseApi { private storeAction(action: SettingsEvent): Promise { @@ -16,59 +12,59 @@ export default class SettingsApi extends BaseApi { putBucket(key: Key, bucket: Bucket) { this.storeAction({ - "put-bucket": { - "bucket-key": key, - "bucket": bucket, + 'put-bucket': { + 'bucket-key': key, + 'bucket': bucket } }); } delBucket(key: Key) { this.storeAction({ - "del-bucket": { - "bucket-key": key, + 'del-bucket': { + 'bucket-key': key } }); } putEntry(buc: Key, key: Key, val: Value) { return this.storeAction({ - "put-entry": { - "bucket-key": buc, - "entry-key": key, - "value": val, + 'put-entry': { + 'bucket-key': buc, + 'entry-key': key, + 'value': val } }); } delEntry(buc: Key, key: Key) { this.storeAction({ - "put-entry": { - "bucket-key": buc, - "entry-key": key, + 'put-entry': { + 'bucket-key': buc, + 'entry-key': key } }); } async getAll() { - const data = await this.scry("settings-store", "/all"); - this.store.handleEvent({data: {"settings-data": data.all}}); + const data = await this.scry('settings-store', '/all'); + this.store.handleEvent({ data: { 'settings-data': data.all } }); } async getBucket(bucket: Key) { const data = await this.scry('settings-store', `/bucket/${bucket}`); - this.store.handleEvent({data: {"settings-data": { - "bucket-key": bucket, - "bucket": data.bucket, - }}}); + this.store.handleEvent({ data: { 'settings-data': { + 'bucket-key': bucket, + 'bucket': data.bucket + } } }); } async getEntry(bucket: Key, entry: Key) { const data = await this.scry('settings-store', `/entry/${bucket}/${entry}`); - this.store.handleEvent({data: {"settings-data": { - "bucket-key": bucket, - "entry-key": entry, - "entry": data.entry, - }}}); + this.store.handleEvent({ data: { 'settings-data': { + 'bucket-key': bucket, + 'entry-key': entry, + 'entry': data.entry + } } }); } } diff --git a/pkg/interface/src/logic/lib/BigIntOrderedMap.ts b/pkg/interface/src/logic/lib/BigIntOrderedMap.ts index 069d52002..21fc1c8ca 100644 --- a/pkg/interface/src/logic/lib/BigIntOrderedMap.ts +++ b/pkg/interface/src/logic/lib/BigIntOrderedMap.ts @@ -1,4 +1,4 @@ -import bigInt, { BigInteger } from "big-integer"; +import bigInt, { BigInteger } from 'big-integer'; interface NonemptyNode { n: [BigInteger, V]; @@ -14,7 +14,7 @@ type MapNode = NonemptyNode | null; */ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { private root: MapNode = null; - size: number = 0; + size = 0; constructor(initial: [BigInteger, V][] = []) { initial.forEach(([key, val]) => { @@ -48,13 +48,12 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { * Put an item by a key */ set(key: BigInteger, value: V): void { - const inner = (node: MapNode) => { if (!node) { return { n: [key, value], l: null, - r: null, + r: null }; } const [k] = node.n; @@ -62,22 +61,22 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { this.size--; return { ...node, - n: [k, value], + n: [k, value] }; } if (key.gt(k)) { const l = inner(node.l); if (!l) { - throw new Error("invariant violation"); + throw new Error('invariant violation'); } return { ...node, - l, + l }; } const r = inner(node.r); if (!r) { - throw new Error("invariant violation"); + throw new Error('invariant violation'); } return { ...node, r }; @@ -133,8 +132,8 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { bool, { ...node, - l, - }, + l + } ]; } @@ -143,8 +142,8 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { bool, { ...node, - r, - }, + r + } ]; }; const [ret, newRoot] = inner(this.root); @@ -165,12 +164,12 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { } return { ...node.l, - r: inner(node.r), + r: inner(node.r) }; }; return inner(nod); } - + peekLargest(): [BigInteger, V] | undefined { const inner = (node: MapNode) => { if(!node) { @@ -180,7 +179,7 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { return inner(node.l); } return node.n; - } + }; return inner(this.root); } @@ -193,7 +192,7 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { return inner(node.r); } return node.n; - } + }; return inner(this.root); } @@ -208,7 +207,7 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { } [Symbol.iterator](): IterableIterator<[BigInteger, V]> { - let result: [BigInteger, V][] = []; + const result: [BigInteger, V][] = []; const inner = (node: MapNode) => { if (!node) { return; @@ -227,7 +226,7 @@ export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { return { value: result[idx++], done: false }; } return { done: true, value: null }; - }, + } }; } } diff --git a/pkg/interface/src/logic/lib/OrderedMap.ts b/pkg/interface/src/logic/lib/OrderedMap.ts index d66346ff6..91ec52b78 100644 --- a/pkg/interface/src/logic/lib/OrderedMap.ts +++ b/pkg/interface/src/logic/lib/OrderedMap.ts @@ -1,7 +1,6 @@ export class OrderedMap extends Map implements Iterable<[number, V]> { - [Symbol.iterator](): IterableIterator<[number, V]> { const sorted = Array.from(super[Symbol.iterator]()).sort( ([a], [b]) => b - a @@ -15,7 +14,7 @@ export class OrderedMap extends Map } else { return { done: true, value: null }; } - }, + } }; } } diff --git a/pkg/interface/src/logic/lib/bigInt.ts b/pkg/interface/src/logic/lib/bigInt.ts index ecc6dda0a..01da28b45 100644 --- a/pkg/interface/src/logic/lib/bigInt.ts +++ b/pkg/interface/src/logic/lib/bigInt.ts @@ -1,4 +1,4 @@ -import bigInt, { BigInteger } from "big-integer"; +import bigInt, { BigInteger } from 'big-integer'; export function max(a: BigInteger, b: BigInteger) { return a.gt(b) ? a : b; diff --git a/pkg/interface/src/logic/lib/group.ts b/pkg/interface/src/logic/lib/group.ts index edc525d89..2eb81e015 100644 --- a/pkg/interface/src/logic/lib/group.ts +++ b/pkg/interface/src/logic/lib/group.ts @@ -1,7 +1,7 @@ -import _ from "lodash"; -import { roleTags, RoleTags, Group, Resource } from "~/types/group-update"; -import { PatpNoSig, Path } from "~/types/noun"; -import {deSig} from "./util"; +import _ from 'lodash'; +import { roleTags, RoleTags, Group, Resource } from '@urbit/api/groups'; +import { PatpNoSig, Path } from '@urbit/api'; +import { deSig } from './util'; export function roleForShip( group: Group, @@ -14,7 +14,7 @@ export function roleForShip( } export function resourceFromPath(path: Path): Resource { - const [, , ship, name] = path.split("/"); + const [, , ship, name] = path.split('/'); return { ship, name }; } @@ -25,7 +25,7 @@ export function makeResource(ship: string, name: string) { export function isWriter(group: Group, resource: string) { const writers: Set | undefined = _.get( group, - ["tags", "graph", resource, "writers"], + ['tags', 'graph', resource, 'writers'], undefined ); const admins = group?.tags?.role?.admin ?? new Set(); @@ -36,18 +36,18 @@ export function isWriter(group: Group, resource: string) { } } -export function isChannelAdmin(group: Group, resource: string, ship: string = `~${window.ship}`) { +export function isChannelAdmin(group: Group, resource: string, ship = `~${window.ship}`) { const role = roleForShip(group, ship.slice(1)); return ( isHost(resource, ship) || - role === "admin" || - role === "moderator" + role === 'admin' || + role === 'moderator' ); } -export function isHost(resource: string, ship: string = `~${window.ship}`) { - const [, , host] = resource.split("/"); +export function isHost(resource: string, ship = `~${window.ship}`) { + const [, , host] = resource.split('/'); return ship === host; } diff --git a/pkg/interface/src/logic/lib/hark.ts b/pkg/interface/src/logic/lib/hark.ts index ee5ecd761..b65a09603 100644 --- a/pkg/interface/src/logic/lib/hark.ts +++ b/pkg/interface/src/logic/lib/hark.ts @@ -1,6 +1,6 @@ -import bigInt, { BigInteger } from "big-integer"; -import f from "lodash/fp"; -import { Unreads } from "~/types"; +import bigInt, { BigInteger } from 'big-integer'; +import f from 'lodash/fp'; +import { Unreads } from '@urbit/api'; export function getLastSeen( unreads: Unreads, @@ -8,10 +8,10 @@ export function getLastSeen( index: string ): BigInteger | undefined { const lastSeenIdx = unreads.graph?.[path]?.[index]?.unreads; - if (!(typeof lastSeenIdx === "string")) { + if (!(typeof lastSeenIdx === 'string')) { return bigInt.zero; } - return f.flow(f.split("/"), f.last, (x) => (!!x ? bigInt(x) : undefined))( + return f.flow(f.split('/'), f.last, x => (x ? bigInt(x) : undefined))( lastSeenIdx ); } diff --git a/pkg/interface/src/logic/lib/notification.ts b/pkg/interface/src/logic/lib/notification.ts index 87288d83d..bd2979f40 100644 --- a/pkg/interface/src/logic/lib/notification.ts +++ b/pkg/interface/src/logic/lib/notification.ts @@ -1,19 +1,19 @@ -import { GraphNotifIndex, GraphNotificationContents } from "~/types"; +import { GraphNotifIndex, GraphNotificationContents } from '@urbit/api'; export function getParentIndex( idx: GraphNotifIndex, contents: GraphNotificationContents ) { - const origIndex = contents[0].index.slice(1).split("/"); - const ret = (i: string[]) => `/${i.join("/")}`; + const origIndex = contents[0].index.slice(1).split('/'); + const ret = (i: string[]) => `/${i.join('/')}`; switch (idx.description) { - case "link": - return "/"; - case "comment": + case 'link': + return '/'; + case 'comment': return ret(origIndex.slice(0, 1)); - case "note": - return "/"; - case "mention": + case 'note': + return '/'; + case 'mention': return undefined; default: return undefined; diff --git a/pkg/interface/src/logic/lib/post.ts b/pkg/interface/src/logic/lib/post.ts index 1a311de55..397563283 100644 --- a/pkg/interface/src/logic/lib/post.ts +++ b/pkg/interface/src/logic/lib/post.ts @@ -1,4 +1,4 @@ -import { Post, GraphNode } from "~/types"; +import { Post, GraphNode } from '@urbit/api'; export const buntPost = (): Post => ({ author: '', @@ -10,7 +10,7 @@ export const buntPost = (): Post => ({ }); export function makeNodeMap(posts: Post[]): Record { - let nodes = {}; + const nodes = {}; posts.forEach((p) => { nodes[p.index] = { children: { empty: null }, post: p }; }); diff --git a/pkg/interface/src/logic/lib/publish.ts b/pkg/interface/src/logic/lib/publish.ts index 10c64aecf..ce616b3cf 100644 --- a/pkg/interface/src/logic/lib/publish.ts +++ b/pkg/interface/src/logic/lib/publish.ts @@ -1,8 +1,8 @@ -import { Post, GraphNode, TextContent, Graph, NodeMap } from "~/types"; +import { Post, GraphNode, TextContent, Graph, NodeMap } from '@urbit/api'; import { buntPost } from '~/logic/lib/post'; -import { unixToDa } from "~/logic/lib/util"; -import {BigIntOrderedMap} from "./BigIntOrderedMap"; -import bigInt, {BigInteger} from 'big-integer'; +import { unixToDa } from '~/logic/lib/util'; +import { BigIntOrderedMap } from './BigIntOrderedMap'; +import bigInt, { BigInteger } from 'big-integer'; export function newPost( title: string, @@ -12,20 +12,20 @@ export function newPost( const nowDa = unixToDa(now); const root: Post = { author: `~${window.ship}`, - index: "/" + nowDa.toString(), - "time-sent": now, + index: '/' + nowDa.toString(), + 'time-sent': now, contents: [], hash: null, - signatures: [], + signatures: [] }; - const revContainer: Post = { ...root, index: root.index + "/1" }; - const commentsContainer = { ...root, index: root.index + "/2" }; + const revContainer: Post = { ...root, index: root.index + '/1' }; + const commentsContainer = { ...root, index: root.index + '/2' }; const firstRevision: Post = { ...revContainer, - index: revContainer.index + "/1", - contents: [{ text: title }, { text: body }], + index: revContainer.index + '/1', + contents: [{ text: title }, { text: body }] }; const nodes = { @@ -37,16 +37,16 @@ export function newPost( children: { 1: { post: firstRevision, - children: null, - }, - }, + children: null + } + } }, 2: { post: commentsContainer, children: null - }, - }, - }, + } + } + } }; return [nowDa, nodes]; @@ -57,15 +57,15 @@ export function editPost(rev: number, noteId: BigInteger, title: string, body: s const newRev: Post = { author: `~${window.ship}`, index: `/${noteId.toString()}/1/${rev}`, - "time-sent": now, + 'time-sent': now, contents: [{ text: title }, { text: body }], hash: null, - signatures: [], + signatures: [] }; const nodes = { [newRev.index]: { post: newRev, - children: null + children: null } }; @@ -74,7 +74,7 @@ export function editPost(rev: number, noteId: BigInteger, title: string, body: s export function getLatestRevision(node: GraphNode): [number, string, string, Post] { const revs = node.children.get(bigInt(1)); - const empty = [1, "", "", buntPost()] as [number, string, string, Post]; + const empty = [1, '', '', buntPost()] as [number, string, string, Post]; if(!revs) { return empty; } @@ -98,17 +98,16 @@ export function getLatestCommentRevision(node: GraphNode): [number, Post] { return [revNum.toJSNumber(), rev.post]; } - export function getComments(node: GraphNode): GraphNode { const comments = node.children.get(bigInt(2)); if(!comments) { - return { post: buntPost(), children: new BigIntOrderedMap() } + return { post: buntPost(), children: new BigIntOrderedMap() }; } return comments; } export function getSnippet(body: string) { const start = body.slice(0, body.indexOf('\n', 2)); - return (start === body || start.startsWith("![")) ? start : `${start}...`; + return (start === body || start.startsWith('![')) ? start : `${start}...`; } diff --git a/pkg/interface/src/logic/lib/relativePosition.tsx b/pkg/interface/src/logic/lib/relativePosition.tsx index 8ad1618e5..b2c4b751e 100644 --- a/pkg/interface/src/logic/lib/relativePosition.tsx +++ b/pkg/interface/src/logic/lib/relativePosition.tsx @@ -1,16 +1,16 @@ -import _ from "lodash"; +import _ from 'lodash'; -export const alignY = ["top", "bottom"] as const; +export const alignY = ['top', 'bottom'] as const; export type AlignY = typeof alignY[number]; -export const alignX = ["left", "right"] as const; +export const alignX = ['left', 'right'] as const; export type AlignX = typeof alignX[number]; export function getRelativePosition( relativeTo: HTMLElement | null, alignX: AlignX | AlignX[], alignY: AlignY | AlignY[], - offsetX: number = 0, - offsetY: number = 0 + offsetX = 0, + offsetY = 0 ) { const rect = relativeTo?.getBoundingClientRect(); if (!rect) { @@ -20,7 +20,7 @@ export function getRelativePosition( top: rect.top - offsetY, left: rect.left - offsetX, bottom: document.documentElement.clientHeight - rect.bottom - offsetY, - right: document.documentElement.clientWidth - rect.right - offsetX, + right: document.documentElement.clientWidth - rect.right - offsetX }; const alignXArr = _.isArray(alignX) ? alignX : [alignX]; const alignYArr = _.isArray(alignY) ? alignY : [alignY]; @@ -34,7 +34,7 @@ export function getRelativePosition( [...Array(idx), `${bounds[a]}px`], acc[a] || [], (a, b) => a || b || null - ), + ) }), {} ), @@ -46,10 +46,10 @@ export function getRelativePosition( [...Array(idx), `${bounds[a]}px`], acc[a] || [], (a, b) => a || b || null - ), + ) }), {} - ), + ) } as Record; } diff --git a/pkg/interface/src/logic/lib/tutorialModal.ts b/pkg/interface/src/logic/lib/tutorialModal.ts index ef1d753b1..53e759838 100644 --- a/pkg/interface/src/logic/lib/tutorialModal.ts +++ b/pkg/interface/src/logic/lib/tutorialModal.ts @@ -1,6 +1,6 @@ -import { TutorialProgress, Associations } from "~/types"; -import { AlignX, AlignY } from "~/logic/lib/relativePosition"; -import { Direction } from "~/views/components/Triangle"; +import { TutorialProgress, Associations } from '@urbit/api'; +import { AlignX, AlignY } from '~/logic/lib/relativePosition'; +import { Direction } from '~/views/components/Triangle'; export const MODAL_WIDTH = 256; export const MODAL_HEIGHT = 256; @@ -43,7 +43,7 @@ export const getTrianglePosition = (dir: Direction) => { return { top: midY, left: '-32px' - } + }; case 'North': return { top: '-32px', @@ -55,117 +55,117 @@ export const getTrianglePosition = (dir: Direction) => { left: midX }; } -} +}; export const progressDetails: Record = { hidden: {} as any, exit: {} as any, done: { - title: "End", + title: 'End', description: - "This tutorial is finished. Would you like to leave Beginner Island?", - url: "/", - alignX: "right", - alignY: "top", + 'This tutorial is finished. Would you like to leave Beginner Island?', + url: '/', + alignX: 'right', + alignY: 'top', offsetX: MODAL_WIDTH + 8, - offsetY: 0, + offsetY: 0 }, start: { - title: "New Group added", + title: 'New Group added', description: - "We just added you to the Beginner island group to show you around. This group is public, but other groups can be private", - url: "/", - alignX: "right", - alignY: "top", - arrow: "West", + 'We just added you to the Beginner island group to show you around. This group is public, but other groups can be private', + url: '/', + alignX: 'right', + alignY: 'top', + arrow: 'West', offsetX: MODAL_WIDTH + 24, - offsetY: 64, + offsetY: 64 }, - "group-desc": { - title: "What's a group", + 'group-desc': { + title: 'What\'s a group', description: - "A group contains members and tends to be centered around a topic or multiple topics.", + 'A group contains members and tends to be centered around a topic or multiple topics.', url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`, - alignX: "left", - alignY: "top", - arrow: "East", + alignX: 'left', + alignY: 'top', + arrow: 'East', offsetX: MODAL_WIDTH + 24, - offsetY: MODAL_HEIGHT / 2 - 8, + offsetY: MODAL_HEIGHT / 2 - 8 }, channels: { - title: "Channels", + title: 'Channels', description: - "Inside a group you have three types of Channels: Chat, Collection, or Notebook. Mix and match these depending on your group context!", + 'Inside a group you have three types of Channels: Chat, Collection, or Notebook. Mix and match these depending on your group context!', url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`, - alignY: "top", - alignX: "right", - arrow: "West", + alignY: 'top', + alignX: 'right', + arrow: 'West', offsetX: MODAL_WIDTH + 24, - offsetY: -8, + offsetY: -8 }, chat: { - title: "Chat", + title: 'Chat', description: - "Chat channels are for messaging within your group. Direct Messages are also supported, and are accessible from the “DMs” tile on the homescreen", + 'Chat channels are for messaging within your group. Direct Messages are also supported, and are accessible from the “DMs” tile on the homescreen', url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/chat/ship/${TUTORIAL_HOST}/${TUTORIAL_CHAT}`, - alignY: "top", - arrow: "North", - alignX: "right", + alignY: 'top', + arrow: 'North', + alignX: 'right', offsetY: -56, - offsetX: -8, + offsetX: -8 }, link: { - title: "Collection", + title: 'Collection', description: - "A collection is where you can share and view links, images, and other media within your group. Every item in a Collection can have it’s own comment thread.", + 'A collection is where you can share and view links, images, and other media within your group. Every item in a Collection can have it’s own comment thread.', url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/link/ship/${TUTORIAL_HOST}/${TUTORIAL_LINKS}`, - alignY: "top", - alignX: "right", - arrow: "North", + alignY: 'top', + alignX: 'right', + arrow: 'North', offsetX: -8, - offsetY: -56, + offsetY: -56 }, publish: { - title: "Notebook", + title: 'Notebook', description: - "Notebooks are for creating long-form content within your group. Use markdown to create rich posts with headers, lists and images.", + 'Notebooks are for creating long-form content within your group. Use markdown to create rich posts with headers, lists and images.', url: `/~landscape/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}/resource/publish/ship/${TUTORIAL_HOST}/${TUTORIAL_BOOK}`, - alignY: "top", - alignX: "right", - arrow: "North", + alignY: 'top', + alignX: 'right', + arrow: 'North', offsetX: -8, - offsetY: -56, + offsetY: -56 }, notifications: { - title: "Notifications", - description: "You will get updates from subscribed channels and mentions here. You can access Notifications through Leap.", + title: 'Notifications', + description: 'You will get updates from subscribed channels and mentions here. You can access Notifications through Leap.', url: '/~notifications', - alignY: "top", - alignX: "left", - arrow: "North", + alignY: 'top', + alignX: 'left', + arrow: 'North', offsetX: (MODAL_WIDTH / 2) - 16, - offsetY: -48, + offsetY: -48 }, profile: { - title: "Profile", + title: 'Profile', description: - "Your profile is customizable and can be shared with other ships. Enter as much or as little information as you’d like.", + 'Your profile is customizable and can be shared with other ships. Enter as much or as little information as you’d like.', url: `/~profile/~${window.ship}`, - alignY: "top", - alignX: "right", - arrow: "South", + alignY: 'top', + alignX: 'right', + arrow: 'South', offsetX: -300 + MODAL_WIDTH / 2, - offsetY: -120 + MODAL_HEIGHT / 2, + offsetY: -120 + MODAL_HEIGHT / 2 }, leap: { - title: "Leap", + title: 'Leap', description: - "Leap allows you to go to a specific channel, message, collection, profile or group simply by typing in a command or selecting a shortcut from the dropdown menu.", + 'Leap allows you to go to a specific channel, message, collection, profile or group simply by typing in a command or selecting a shortcut from the dropdown menu.', url: `/~profile/~${window.ship}`, - alignY: "top", - alignX: "left", - arrow: "North", + alignY: 'top', + alignX: 'left', + arrow: 'North', offsetX: 0.3 *MODAL_HEIGHT, - offsetY: -48, - }, + offsetY: -48 + } }; diff --git a/pkg/interface/src/logic/lib/useDrag.ts b/pkg/interface/src/logic/lib/useDrag.ts index e2aa496a8..fb17928e9 100644 --- a/pkg/interface/src/logic/lib/useDrag.ts +++ b/pkg/interface/src/logic/lib/useDrag.ts @@ -1,4 +1,4 @@ -import { useState, useCallback, useMemo, useEffect } from "react"; +import { useState, useCallback, useMemo, useEffect } from 'react'; function validateDragEvent(e: DragEvent): FileList | File[] | true | null { const files: File[] = []; @@ -8,8 +8,8 @@ function validateDragEvent(e: DragEvent): FileList | File[] | true | null { } if (e.dataTransfer?.items) { Array.from(e.dataTransfer.items || []) - .filter((i) => i.kind === 'file') - .forEach(f => { + .filter(i => i.kind === 'file') + .forEach((f) => { valid = true; // Valid if file exists, but on DragOver, won't reveal its contents for security const data = f.getAsFile(); if (data) { @@ -89,14 +89,14 @@ export function useFileDrag(dragged: (f: FileList | File[], e: DragEvent) => voi document.body.addEventListener('mouseout', mouseleave); return () => { document.body.removeEventListener('mouseout', mouseleave); - } + }; }, []); const bind = { onDragLeave, onDragOver, onDrop, - onDragEnter, + onDragEnter }; return { bind, dragging }; diff --git a/pkg/interface/src/logic/lib/useDropdown.ts b/pkg/interface/src/logic/lib/useDropdown.ts index 0e0b37e0d..98a7359f3 100644 --- a/pkg/interface/src/logic/lib/useDropdown.ts +++ b/pkg/interface/src/logic/lib/useDropdown.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo, useCallback } from "react"; +import { useState, useEffect, useMemo, useCallback } from 'react'; export function useDropdown( candidates: C[], @@ -12,10 +12,10 @@ export function useDropdown( (s: string) => { const exactMatch = isExact(s); const exact = exactMatch ? [exactMatch] : []; - const opts = [...new Set([...exact, ...candidates.filter((c) => searchPred(s, c))])]; + const opts = [...new Set([...exact, ...candidates.filter(c => searchPred(s, c))])]; setOptions(opts); if (selected) { - const idx = opts.findIndex((c) => key(c) === key(selected)); + const idx = opts.findIndex(c => key(c) === key(selected)); if (idx < 0) { setSelected(undefined); } @@ -29,9 +29,11 @@ export function useDropdown( const select = (idx: number) => { setSelected(options[idx]); }; - if(!selected) { select(0); return false; } + if(!selected) { + select(0); return false; +} - const idx = options.findIndex((c) => key(c) === key(selected)); + const idx = options.findIndex(c => key(c) === key(selected)); if ( idx === -1 || (options.length - 1 <= idx && !backward) @@ -55,6 +57,6 @@ export function useDropdown( back, search, selected, - options, + options }; } diff --git a/pkg/interface/src/logic/lib/useHashLink.ts b/pkg/interface/src/logic/lib/useHashLink.ts index 77c91be2e..7d93ce5d4 100644 --- a/pkg/interface/src/logic/lib/useHashLink.ts +++ b/pkg/interface/src/logic/lib/useHashLink.ts @@ -1,6 +1,5 @@ import { useEffect } from 'react'; -import {useLocation} from "react-router-dom"; - +import { useLocation } from 'react-router-dom'; export function useHashLink() { const location = useLocation(); @@ -10,8 +9,5 @@ export function useHashLink() { return; } document.querySelector(location.hash)?.scrollIntoView({ behavior: 'smooth', block: 'start' }); - }, [location.hash]); - - } diff --git a/pkg/interface/src/logic/lib/useLazyScroll.ts b/pkg/interface/src/logic/lib/useLazyScroll.ts index bd1c31a32..03a3ce432 100644 --- a/pkg/interface/src/logic/lib/useLazyScroll.ts +++ b/pkg/interface/src/logic/lib/useLazyScroll.ts @@ -1,6 +1,6 @@ -import { useEffect, RefObject, useRef, useState } from "react"; -import _ from "lodash"; -import usePreviousValue from "./usePreviousValue"; +import { useEffect, RefObject, useRef, useState } from 'react'; +import _ from 'lodash'; +import usePreviousValue from './usePreviousValue'; export function distanceToBottom(el: HTMLElement) { const { scrollTop, scrollHeight, clientHeight } = el; @@ -40,7 +40,6 @@ export function useLazyScroll( } }, [count]); - useEffect(() => { if (!ref.current) { return; @@ -54,13 +53,12 @@ export function useLazyScroll( loadUntil(el); }; - ref.current.addEventListener("scroll", onScroll, { passive: true }); + ref.current.addEventListener('scroll', onScroll, { passive: true }); return () => { - ref.current?.removeEventListener("scroll", onScroll); + ref.current?.removeEventListener('scroll', onScroll); }; }, [ref?.current, count]); - return { isDone, isLoading }; } diff --git a/pkg/interface/src/logic/lib/useLocalStorageState.ts b/pkg/interface/src/logic/lib/useLocalStorageState.ts index b81214a67..13de71afe 100644 --- a/pkg/interface/src/logic/lib/useLocalStorageState.ts +++ b/pkg/interface/src/logic/lib/useLocalStorageState.ts @@ -1,4 +1,4 @@ -import { useState, useCallback, useEffect } from "react"; +import { useState, useCallback, useEffect } from 'react'; function retrieve(key: string, initial: T): T { const s = localStorage.getItem(key); @@ -25,7 +25,7 @@ export function useLocalStorageState(key: string, initial: T) { const setState = useCallback( (s: SetState) => { - const updated = typeof s === "function" ? s(state) : s; + const updated = typeof s === 'function' ? s(state) : s; _setState(updated); localStorage.setItem(key, JSON.stringify(updated)); }, diff --git a/pkg/interface/src/logic/lib/useModal.tsx b/pkg/interface/src/logic/lib/useModal.tsx index c7cb9be7d..3d19343cd 100644 --- a/pkg/interface/src/logic/lib/useModal.tsx +++ b/pkg/interface/src/logic/lib/useModal.tsx @@ -5,15 +5,15 @@ import React, { SyntheticEvent, useMemo, useEffect, - useRef, -} from "react"; + useRef +} from 'react'; -import { Box } from "@tlon/indigo-react"; -import { useOutsideClick } from "./useOutsideClick"; -import { ModalOverlay } from "~/views/components/ModalOverlay"; -import {Portal} from "~/views/components/Portal"; -import {ModalPortal} from "~/views/components/ModalPortal"; -import {PropFunc} from "~/types"; +import { Box } from '@tlon/indigo-react'; +import { useOutsideClick } from './useOutsideClick'; +import { ModalOverlay } from '~/views/components/ModalOverlay'; +import { Portal } from '~/views/components/Portal'; +import { ModalPortal } from '~/views/components/ModalPortal'; +import { PropFunc } from '@urbit/api'; type ModalFunc = (dismiss: () => void) => JSX.Element; interface UseModalProps { @@ -42,7 +42,7 @@ export function useModal(props: UseModalProps & PropFunc): UseModalR () => !modalShown ? null - : typeof modal === "function" + : typeof modal === 'function' ? modal(dismiss) : modal, [modalShown, modal, dismiss] @@ -59,7 +59,7 @@ export function useModal(props: UseModalProps & PropFunc): UseModalR bg="white" borderRadius={2} border={[0, 1]} - borderColor={["washedGray", "washedGray"]} + borderColor={['washedGray', 'washedGray']} display="flex" alignItems="stretch" flexDirection="column" @@ -76,6 +76,6 @@ export function useModal(props: UseModalProps & PropFunc): UseModalR return { showModal, - modal: modalComponent, + modal: modalComponent }; } diff --git a/pkg/interface/src/logic/lib/useOutsideClick.ts b/pkg/interface/src/logic/lib/useOutsideClick.ts index 4d4f95ca6..481fa2a90 100644 --- a/pkg/interface/src/logic/lib/useOutsideClick.ts +++ b/pkg/interface/src/logic/lib/useOutsideClick.ts @@ -1,8 +1,8 @@ -import { useEffect, RefObject } from "react"; +import { useEffect, RefObject } from 'react'; export function useOutsideClick( ref: RefObject, - onClick: () => void, + onClick: () => void ) { useEffect(() => { function handleClick(event: MouseEvent) { @@ -16,17 +16,16 @@ export function useOutsideClick( } function handleKeyDown(ev) { - if(ev.key === "Escape") { + if(ev.key === 'Escape') { onClick(); - } } - document.addEventListener("mousedown", handleClick); - document.addEventListener("keydown", handleKeyDown); + document.addEventListener('mousedown', handleClick); + document.addEventListener('keydown', handleKeyDown); return () => { - document.removeEventListener("mousedown", handleClick); - document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener('mousedown', handleClick); + document.removeEventListener('keydown', handleKeyDown); }; }, [ref.current, onClick]); } diff --git a/pkg/interface/src/logic/lib/usePreviousValue.ts b/pkg/interface/src/logic/lib/usePreviousValue.ts index e24097810..109f9ffa8 100644 --- a/pkg/interface/src/logic/lib/usePreviousValue.ts +++ b/pkg/interface/src/logic/lib/usePreviousValue.ts @@ -1,7 +1,5 @@ -import { useRef } from "react"; -import { Primitive } from "~/types"; - - +import { useRef } from 'react'; +import { Primitive } from '@urbit/api'; export default function usePreviousValue(value: T): T { const prev = useRef(null); diff --git a/pkg/interface/src/logic/lib/useQuery.ts b/pkg/interface/src/logic/lib/useQuery.ts index 0957ca098..735060a2d 100644 --- a/pkg/interface/src/logic/lib/useQuery.ts +++ b/pkg/interface/src/logic/lib/useQuery.ts @@ -1,5 +1,5 @@ -import { useMemo, useCallback } from "react"; -import { useLocation } from "react-router-dom"; +import { useMemo, useCallback } from 'react'; +import { useLocation } from 'react-router-dom'; import _ from 'lodash'; export function useQuery() { @@ -25,6 +25,6 @@ export function useQuery() { return { query, - appendQuery, + appendQuery }; } diff --git a/pkg/interface/src/logic/lib/useS3.ts b/pkg/interface/src/logic/lib/useS3.ts index 0717d52f7..418e4594f 100644 --- a/pkg/interface/src/logic/lib/useS3.ts +++ b/pkg/interface/src/logic/lib/useS3.ts @@ -1,7 +1,7 @@ -import { useCallback, useMemo, useEffect, useRef, useState } from "react"; -import { S3State } from "../../types/s3-update"; -import S3 from "aws-sdk/clients/s3"; -import { dateToDa, deSig } from "./util"; +import { useCallback, useMemo, useEffect, useRef, useState } from 'react'; +import { S3State } from '../../types/s3-update'; +import S3 from 'aws-sdk/clients/s3'; +import { dateToDa, deSig } from './util'; export interface IuseS3 { canUpload: boolean; @@ -28,14 +28,14 @@ const useS3 = (s3: S3State, { accept = '*' } = { accept: '*' }): IuseS3 => { const canUpload = useMemo( () => - (client && s3.credentials && s3.configuration.currentBucket !== "") || false, + (client && s3.credentials && s3.configuration.currentBucket !== '') || false, [s3.credentials, s3.configuration.currentBucket, client] ); const upload = useCallback( async (file: File, bucket: string) => { if (!client.current) { - throw new Error("S3 not ready"); + throw new Error('S3 not ready'); } const fileParts = file.name.split('.'); @@ -47,8 +47,8 @@ const useS3 = (s3: S3State, { accept = '*' } = { accept: '*' }): IuseS3 => { Bucket: bucket, Key: `${window.ship}/${timestamp}-${fileName}.${fileExtension}`, Body: file, - ACL: "public-read", - ContentType: file.type, + ACL: 'public-read', + ContentType: file.type }; setUploading(true); @@ -63,8 +63,8 @@ const useS3 = (s3: S3State, { accept = '*' } = { accept: '*' }): IuseS3 => { ); const uploadDefault = useCallback(async (file: File) => { - if (s3.configuration.currentBucket === "") { - throw new Error("current bucket not set"); + if (s3.configuration.currentBucket === '') { + throw new Error('current bucket not set'); } return upload(file, s3.configuration.currentBucket); }, [s3]); @@ -84,11 +84,10 @@ const useS3 = (s3: S3State, { accept = '*' } = { accept: '*' }): IuseS3 => { } uploadDefault(files[0]).then(resolve); document.body.removeChild(fileSelector); - }) + }); document.body.appendChild(fileSelector); fileSelector.click(); - }) - + }); }, [uploadDefault] ); @@ -96,4 +95,4 @@ const useS3 = (s3: S3State, { accept = '*' } = { accept: '*' }): IuseS3 => { return { canUpload, upload, uploadDefault, uploading, promptUpload }; }; -export default useS3; \ No newline at end of file +export default useS3; diff --git a/pkg/interface/src/logic/lib/useStatelessAsyncClickable.ts b/pkg/interface/src/logic/lib/useStatelessAsyncClickable.ts index b92fdaa03..154f903ba 100644 --- a/pkg/interface/src/logic/lib/useStatelessAsyncClickable.ts +++ b/pkg/interface/src/logic/lib/useStatelessAsyncClickable.ts @@ -1,23 +1,23 @@ -import { MouseEvent, useCallback, useState, useEffect } from "react"; -export type AsyncClickableState = "waiting" | "error" | "loading" | "success"; +import { MouseEvent, useCallback, useState, useEffect } from 'react'; +export type AsyncClickableState = 'waiting' | 'error' | 'loading' | 'success'; export function useStatelessAsyncClickable( onClick: (e: MouseEvent) => Promise, name: string ) { - const [state, setState] = useState("waiting"); + const [state, setState] = useState('waiting'); const handleClick = useCallback( async (e: MouseEvent) => { try { - setState("loading"); + setState('loading'); await onClick(e); - setState("success"); + setState('success'); } catch (e) { console.error(e); - setState("error"); + setState('error'); } finally { setTimeout(() => { - setState("waiting"); + setState('waiting'); }, 3000); } }, @@ -26,7 +26,7 @@ export function useStatelessAsyncClickable( // When name changes, reset button useEffect(() => { - setState("waiting"); + setState('waiting'); }, [name]); return { buttonState: state, onClick: handleClick }; diff --git a/pkg/interface/src/logic/lib/useWaitForProps.ts b/pkg/interface/src/logic/lib/useWaitForProps.ts index 80e92e92a..8a81b0b0a 100644 --- a/pkg/interface/src/logic/lib/useWaitForProps.ts +++ b/pkg/interface/src/logic/lib/useWaitForProps.ts @@ -1,12 +1,11 @@ import { useState, useEffect, useCallback } from 'react'; - -export function useWaitForProps

(props: P, timeout: number = 0) { +export function useWaitForProps

(props: P, timeout = 0) { const [resolve, setResolve] = useState<() => void>(() => () => {}); const [ready, setReady] = useState<(p: P) => boolean | undefined>(); useEffect(() => { - if (typeof ready === "function" && ready(props)) { + if (typeof ready === 'function' && ready(props)) { resolve(); } }, [props, ready, resolve]); @@ -26,7 +25,7 @@ export function useWaitForProps

(props: P, timeout: number = 0) { setResolve(() => resolve); if(timeout > 0) { setTimeout(() => { - reject(new Error("Timed out")); + reject(new Error('Timed out')); }, timeout); } }); diff --git a/pkg/interface/src/logic/lib/util.ts b/pkg/interface/src/logic/lib/util.ts index 670c83d43..8cc6969d9 100644 --- a/pkg/interface/src/logic/lib/util.ts +++ b/pkg/interface/src/logic/lib/util.ts @@ -1,27 +1,27 @@ import { useEffect, useState } from 'react'; -import _ from "lodash"; -import f, { memoize } from "lodash/fp"; -import bigInt, { BigInteger } from "big-integer"; -import { Contact } from '~/types'; +import _ from 'lodash'; +import f, { memoize } from 'lodash/fp'; +import bigInt, { BigInteger } from 'big-integer'; +import { Contact } from '@urbit/api'; import useLocalState from '../state/local'; export const MOBILE_BROWSER_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i; export const MOMENT_CALENDAR_DATE = { - sameDay: "[Today]", - nextDay: "[Tomorrow]", - nextWeek: "dddd", - lastDay: "[Yesterday]", - lastWeek: "[Last] dddd", - sameElse: "~YYYY.M.D", + sameDay: '[Today]', + nextDay: '[Tomorrow]', + nextWeek: 'dddd', + lastDay: '[Yesterday]', + lastWeek: '[Last] dddd', + sameElse: '~YYYY.M.D' }; export const getModuleIcon = (mod: string) => { - if (mod === "link") { - return "Collection"; + if (mod === 'link') { + return 'Collection'; } return _.capitalize(mod); -} +}; export function wait(ms: number) { return new Promise((resolve, reject) => { @@ -37,8 +37,8 @@ export function parentPath(path: string) { return _.dropRight(path.split('/'), 1).join('/'); } -const DA_UNIX_EPOCH = bigInt("170141184475152167957503069145530368000"); // `@ud` ~1970.1.1 -const DA_SECOND = bigInt("18446744073709551616"); // `@ud` ~s1 +const DA_UNIX_EPOCH = bigInt('170141184475152167957503069145530368000'); // `@ud` ~1970.1.1 +const DA_SECOND = bigInt('18446744073709551616'); // `@ud` ~s1 export function daToUnix(da: BigInteger) { // ported from +time:enjs:format in hoon.hoon const offset = DA_SECOND.divide(bigInt(2000)); @@ -59,20 +59,20 @@ export function makePatDa(patda: string) { } export function udToDec(ud: string): string { - return ud.replace(/\./g, ""); + return ud.replace(/\./g, ''); } export function decToUd(str: string): string { return _.trimStart( f.flow( - f.split(""), + f.split(''), f.reverse, f.chunk(3), - f.map(f.flow(f.reverse, f.join(""))), + f.map(f.flow(f.reverse, f.join(''))), f.reverse, - f.join(".") + f.join('.') )(str), - "0." + '0.' ); } @@ -86,12 +86,12 @@ export function clamp(x: number, min: number, max: number) { // color is a #000000 color export function adjustHex(color: string, amount: number): string { return f.flow( - f.split(""), + f.split(''), f.chunk(2), // get RGB channels - f.map((c) => parseInt(c.join(""), 16)), // as hex - f.map((c) => clamp(c + amount, 0, 255).toString(16)), // adjust - f.join(""), - (res) => `#${res}` //format + f.map(c => parseInt(c.join(''), 16)), // as hex + f.map(c => clamp(c + amount, 0, 255).toString(16)), // adjust + f.join(''), + res => `#${res}` // format )(color.slice(1)); } @@ -101,12 +101,12 @@ export function resourceAsPath(resource: any) { } export function uuid() { - let str = "0v"; - str += Math.ceil(Math.random() * 8) + "."; + let str = '0v'; + str += Math.ceil(Math.random() * 8) + '.'; for (let i = 0; i < 5; i++) { let _str = Math.ceil(Math.random() * 10000000).toString(32); - _str = ("00000" + _str).substr(-5, 5); - str += _str + "."; + _str = ('00000' + _str).substr(-5, 5); + str += _str + '.'; } return str.slice(0, -1); @@ -120,11 +120,11 @@ export function uuid() { */ export function daToDate(st: string) { const dub = function (n: string) { - return parseInt(n) < 10 ? "0" + parseInt(n) : n.toString(); + return parseInt(n) < 10 ? '0' + parseInt(n) : n.toString(); }; - const da = st.split(".."); - const bigEnd = da[0].split("."); - const lilEnd = da[1].split("."); + const da = st.split('..'); + const bigEnd = da[0].split('.'); + const lilEnd = da[1].split('.'); const ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub( lilEnd[0] )}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`; @@ -138,9 +138,9 @@ export function daToDate(st: string) { ~2018.7.17..23.15.09..5be5 // urbit @da */ -export function dateToDa(d: Date, mil: boolean = false) { +export function dateToDa(d: Date, mil = false) { const fil = function (n: number) { - return n >= 10 ? n : "0" + n; + return n >= 10 ? n : '0' + n; }; return ( `~${d.getUTCFullYear()}.` + @@ -149,7 +149,7 @@ export function dateToDa(d: Date, mil: boolean = false) { `${fil(d.getUTCHours())}.` + `${fil(d.getUTCMinutes())}.` + `${fil(d.getUTCSeconds())}` + - `${mil ? "..0000" : ""}` + `${mil ? '..0000' : ''}` ); } @@ -157,16 +157,16 @@ export function deSig(ship: string) { if (!ship) { return null; } - return ship.replace("~", ""); + return ship.replace('~', ''); } export function uxToHex(ux: string) { - if (ux.length > 2 && ux.substr(0, 2) === "0x") { - const value = ux.substr(2).replace(".", "").padStart(6, "0"); + if (ux.length > 2 && ux.substr(0, 2) === '0x') { + const value = ux.substr(2).replace('.', '').padStart(6, '0'); return value; } - const value = ux.replace(".", "").padStart(6, "0"); + const value = ux.replace('.', '').padStart(6, '0'); return value; } @@ -187,13 +187,13 @@ export function writeText(str: string) { let success = false; function listener(e) { - e.clipboardData.setData("text/plain", str); + e.clipboardData.setData('text/plain', str); e.preventDefault(); success = true; } - document.addEventListener("copy", listener); - document.execCommand("copy"); - document.removeEventListener("copy", listener); + document.addEventListener('copy', listener); + document.execCommand('copy'); + document.removeEventListener('copy', listener); document?.getSelection()?.removeAllRanges(); @@ -206,21 +206,21 @@ export function writeText(str: string) { // trim patps to match dojo, chat-cli export function cite(ship: string) { let patp = ship, - shortened = ""; - if (patp === null || patp === "") { + shortened = ''; + if (patp === null || patp === '') { return null; } - if (patp.startsWith("~")) { + if (patp.startsWith('~')) { patp = patp.substr(1); } // comet if (patp.length === 56) { - shortened = "~" + patp.slice(0, 6) + "_" + patp.slice(50, 56); + shortened = '~' + patp.slice(0, 6) + '_' + patp.slice(50, 56); return shortened; } // moon if (patp.length === 27) { - shortened = "~" + patp.slice(14, 20) + "^" + patp.slice(21, 27); + shortened = '~' + patp.slice(14, 20) + '^' + patp.slice(21, 27); return shortened; } return `~${patp}`; @@ -232,7 +232,6 @@ export function alphabeticalOrder(a: string, b: string) { export function lengthOrder(a: string, b: string) { return b.length - a.length; - } // TODO: deprecated @@ -244,13 +243,13 @@ export function alphabetiseAssociations(associations: any) { let bName = b.substr(1); if (associations[a].metadata && associations[a].metadata.title) { aName = - associations[a].metadata.title !== "" + associations[a].metadata.title !== '' ? associations[a].metadata.title : a.substr(1); } if (associations[b].metadata && associations[b].metadata.title) { bName = - associations[b].metadata.title !== "" + associations[b].metadata.title !== '' ? associations[b].metadata.title : b.substr(1); } @@ -266,41 +265,42 @@ export function alphabetiseAssociations(associations: any) { // for example, 'some Chars!' becomes '~.some.~43.hars~21.' // export function stringToTa(str: string) { - let out = ""; + let out = ''; for (let i = 0; i < str.length; i++) { const char = str[i]; - let add = ""; + let add = ''; switch (char) { - case " ": - add = "."; + case ' ': + add = '.'; break; - case ".": - add = "~."; + case '.': + add = '~.'; break; - case "~": - add = "~~"; + case '~': + add = '~~'; break; default: const charCode = str.charCodeAt(i); if ( (charCode >= 97 && charCode <= 122) || // a-z (charCode >= 48 && charCode <= 57) || // 0-9 - char === "-" + char === '-' ) { add = char; } else { // TODO behavior for unicode doesn't match +wood's, // but we can probably get away with that for now. - add = "~" + charCode.toString(16) + "."; + add = '~' + charCode.toString(16) + '.'; } } out = out + add; } - return "~." + out; + return '~.' + out; } export function amOwnerOfGroup(groupPath: string) { - if (!groupPath) return false; + if (!groupPath) +return false; const groupOwner = /(\/~)?\/~([a-z-]{3,})\/.*/.exec(groupPath)?.[2]; return window.ship === groupOwner; } @@ -308,18 +308,18 @@ export function amOwnerOfGroup(groupPath: string) { export function getContactDetails(contact: any) { const member = !contact; contact = contact || { - nickname: "", + nickname: '', avatar: null, - color: "0x0", + color: '0x0' }; - const nickname = contact.nickname || ""; - const color = uxToHex(contact.color || "0x0"); + const nickname = contact.nickname || ''; + const color = uxToHex(contact.color || '0x0'); const avatar = contact.avatar || null; return { nickname, color, member, avatar }; } export function stringToSymbol(str: string) { - let result = ""; + let result = ''; for (let i = 0; i < str.length; i++) { const n = str.charCodeAt(i); if ((n >= 97 && n <= 122) || (n >= 48 && n <= 57)) { @@ -327,19 +327,17 @@ export function stringToSymbol(str: string) { } else if (n >= 65 && n <= 90) { result += String.fromCharCode(n + 32); } else { - result += "-"; + result += '-'; } } - result = result.replace(/^[\-\d]+|\-+/g, "-"); - result = result.replace(/^\-+|\-+$/g, ""); - if (result === "") { + result = result.replace(/^[\-\d]+|\-+/g, '-'); + result = result.replace(/^\-+|\-+$/g, ''); + if (result === '') { return dateToDa(new Date()); } return result; } - - /** * Formats a numbers as a `@ud` inserting dot where needed */ @@ -351,23 +349,24 @@ export function numToUd(num: number) { f.reverse, f.map(s => s.join('')), f.join('.') - )(num.toString()) + )(num.toString()); } -export function usePreventWindowUnload(shouldPreventDefault: boolean, message = "You have unsaved changes. Are you sure you want to exit?") { +export function usePreventWindowUnload(shouldPreventDefault: boolean, message = 'You have unsaved changes. Are you sure you want to exit?') { useEffect(() => { - if (!shouldPreventDefault) return; - const handleBeforeUnload = event => { + if (!shouldPreventDefault) +return; + const handleBeforeUnload = (event) => { event.preventDefault(); return message; - } - window.addEventListener("beforeunload", handleBeforeUnload); + }; + window.addEventListener('beforeunload', handleBeforeUnload); window.onbeforeunload = handleBeforeUnload; return () => { - window.removeEventListener("beforeunload", handleBeforeUnload); + window.removeEventListener('beforeunload', handleBeforeUnload); // @ts-ignore window.onbeforeunload = undefined; - } + }; }, [shouldPreventDefault]); } @@ -378,7 +377,7 @@ export function pluralize(text: string, isPlural = false, vowel = false) { // Hide is an optional second parameter for when this function is used in class components export function useShowNickname(contact: Contact | null, hide?: boolean): boolean { const hideNicknames = typeof hide !== 'undefined' ? hide : useLocalState(state => state.hideNicknames); - return !!(contact && contact.nickname && !hideNicknames); + return Boolean(contact && contact.nickname && !hideNicknames); } interface useHoveringInterface { @@ -406,7 +405,6 @@ export function getItemTitle(association: Association) { return cite(`~${name.slice(4)}`); } return cite(ship); - } - return association.metadata.title || association.resource -}; + return association.metadata.title || association.resource; +} diff --git a/pkg/interface/src/logic/lib/workspace.ts b/pkg/interface/src/logic/lib/workspace.ts index e17b81e7b..f13df6195 100644 --- a/pkg/interface/src/logic/lib/workspace.ts +++ b/pkg/interface/src/logic/lib/workspace.ts @@ -1,24 +1,24 @@ -import { Associations, Workspace } from "~/types"; +import { Associations, Workspace } from '@urbit/api'; export function getTitleFromWorkspace( associations: Associations, workspace: Workspace ) { switch (workspace.type) { - case "home": - return "My Channels"; - case "messages": - return "Messages"; - case "group": + case 'home': + return 'My Channels'; + case 'messages': + return 'Messages'; + case 'group': const association = associations.groups[workspace.group]; - return association?.metadata?.title || ""; + return association?.metadata?.title || ''; } } export function getGroupFromWorkspace( workspace: Workspace ): string | undefined { - if (workspace.type === "group") { + if (workspace.type === 'group') { return workspace.group; } diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index 44cbc6cfb..1d2e45e35 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -1,8 +1,8 @@ import _ from 'lodash'; import { StoreState } from '../../store/type'; import { Cage } from '~/types/cage'; -import { ContactUpdate } from '~/types/contact-update'; -import {resourceAsPath} from '../lib/util'; +import { ContactUpdate } from '@urbit/api/contacts'; +import { resourceAsPath } from '../lib/util'; type ContactState = Pick; @@ -62,9 +62,9 @@ const edit = (json: ContactUpdate, state: S) => { const contact = state.contacts?.[ship]; const value = data['edit-field'][field]; if(!contact) { - return; + return; } - + if(field === 'add-group') { contact.groups.push(value); } else if (field === 'remove-group') { @@ -80,4 +80,3 @@ const setPublic = (json: ContactUpdate, state: S) => { state.isContactPublic = data; }; - diff --git a/pkg/interface/src/logic/reducers/group-update.ts b/pkg/interface/src/logic/reducers/group-update.ts index 169b30d56..c4aeddb0a 100644 --- a/pkg/interface/src/logic/reducers/group-update.ts +++ b/pkg/interface/src/logic/reducers/group-update.ts @@ -10,9 +10,9 @@ import { OpenPolicyDiff, OpenPolicy, InvitePolicyDiff, - InvitePolicy, -} from '~/types/group-update'; -import { Enc, PatpNoSig } from '~/types/noun'; + InvitePolicy +} from '@urbit/api/groups'; +import { Enc, PatpNoSig } from '@urbit/api'; import { resourceAsPath } from '../lib/util'; type GroupState = Pick; @@ -23,7 +23,7 @@ function decodeGroup(group: Enc): Group { ...group, members, tags: decodeTags(group.tags), - policy: decodePolicy(group.policy), + policy: decodePolicy(group.policy) }; return res; } @@ -35,7 +35,7 @@ function decodePolicy(policy: Enc): GroupPolicy { } else { const { open } = policy; return { - open: { banned: new Set(open.banned), banRanks: new Set(open.banRanks) }, + open: { banned: new Set(open.banned), banRanks: new Set(open.banRanks) } }; } } @@ -98,7 +98,7 @@ export default class GroupReducer { members: new Set(), tags: { role: { admin: new Set([window.ship]) } }, policy: decodePolicy(policy), - hidden, + hidden }; } } @@ -189,7 +189,6 @@ export default class GroupReducer { } } - private inviteChangePolicy(diff: InvitePolicyDiff, policy: InvitePolicy) { if ('addInvites' in diff) { const { addInvites } = diff; diff --git a/pkg/interface/src/logic/reducers/group-view.ts b/pkg/interface/src/logic/reducers/group-view.ts index 92be7c0f5..dd8763c6d 100644 --- a/pkg/interface/src/logic/reducers/group-view.ts +++ b/pkg/interface/src/logic/reducers/group-view.ts @@ -1,25 +1,24 @@ -import { resourceAsPath } from "~/logic/lib/util"; - +import { resourceAsPath } from '~/logic/lib/util'; const initial = (json: any, state: any) => { const data = json.initial; if(data) { state.pendingJoin = data; } -} +}; const progress = (json: any, state: any) => { const data = json.progress; if(data) { const { progress, resource } = data; - state.pendingJoin = {...state.pendingJoin, [resource]: progress }; + state.pendingJoin = { ...state.pendingJoin, [resource]: progress }; if(progress === 'done') { setTimeout(() => { delete state.pendingJoin[resource]; }, 10000); } } -} +}; export const GroupViewReducer = (json: any, state: any) => { const data = json['group-view-update']; @@ -27,4 +26,4 @@ export const GroupViewReducer = (json: any, state: any) => { progress(data, state); initial(data, state); } -} +}; diff --git a/pkg/interface/src/logic/reducers/hark-update.ts b/pkg/interface/src/logic/reducers/hark-update.ts index 6928e4508..288b3bed5 100644 --- a/pkg/interface/src/logic/reducers/hark-update.ts +++ b/pkg/interface/src/logic/reducers/hark-update.ts @@ -3,22 +3,21 @@ import { NotifIndex, NotificationGraphConfig, GroupNotificationsConfig, - UnreadStats, -} from "~/types"; -import { makePatDa } from "~/logic/lib/util"; -import _ from "lodash"; -import {StoreState} from "../store/type"; + UnreadStats +} from '@urbit/api'; +import { makePatDa } from '~/logic/lib/util'; +import _ from 'lodash'; +import { StoreState } from '../store/type'; import { BigIntOrderedMap } from '../lib/BigIntOrderedMap'; -type HarkState = Pick; - +type HarkState = Pick; export const HarkReducer = (json: any, state: HarkState) => { - const data = _.get(json, "harkUpdate", false); + const data = _.get(json, 'harkUpdate', false); if (data) { reduce(data, state); } - const graphHookData = _.get(json, "hark-graph-hook-update", false); + const graphHookData = _.get(json, 'hark-graph-hook-update', false); if (graphHookData) { graphInitial(graphHookData, state); graphIgnore(graphHookData, state); @@ -26,7 +25,7 @@ export const HarkReducer = (json: any, state: HarkState) => { graphWatchSelf(graphHookData, state); graphMentions(graphHookData, state); } - const groupHookData = _.get(json, "hark-group-hook-update", false); + const groupHookData = _.get(json, 'hark-group-hook-update', false); if (groupHookData) { groupInitial(groupHookData, state); groupListen(groupHookData, state); @@ -35,31 +34,31 @@ export const HarkReducer = (json: any, state: HarkState) => { }; function groupInitial(json: any, state: HarkState) { - const data = _.get(json, "initial", false); + const data = _.get(json, 'initial', false); if (data) { state.notificationsGroupConfig = data; } } function graphInitial(json: any, state: HarkState) { - const data = _.get(json, "initial", false); + const data = _.get(json, 'initial', false); if (data) { state.notificationsGraphConfig = data; } } function graphListen(json: any, state: HarkState) { - const data = _.get(json, "listen", false); + const data = _.get(json, 'listen', false); if (data) { state.notificationsGraphConfig.watching = [ ...state.notificationsGraphConfig.watching, - data, + data ]; } } function graphIgnore(json: any, state: HarkState) { - const data = _.get(json, "ignore", false); + const data = _.get(json, 'ignore', false); if (data) { state.notificationsGraphConfig.watching = state.notificationsGraphConfig.watching.filter( ({ graph, index }) => !(graph === data.graph && index === data.index) @@ -68,30 +67,30 @@ function graphIgnore(json: any, state: HarkState) { } function groupListen(json: any, state: HarkState) { - const data = _.get(json, "listen", false); + const data = _.get(json, 'listen', false); if (data) { state.notificationsGroupConfig = [...state.notificationsGroupConfig, data]; } } function groupIgnore(json: any, state: HarkState) { - const data = _.get(json, "ignore", false); + const data = _.get(json, 'ignore', false); if (data) { state.notificationsGroupConfig = state.notificationsGroupConfig.filter( - (n) => n !== data + n => n !== data ); } } function graphMentions(json: any, state: HarkState) { - const data = _.get(json, "set-mentions", undefined); + const data = _.get(json, 'set-mentions', undefined); if (!_.isUndefined(data)) { state.notificationsGraphConfig.mentions = data; } } function graphWatchSelf(json: any, state: HarkState) { - const data = _.get(json, "set-watch-on-self", undefined); + const data = _.get(json, 'set-watch-on-self', undefined); if (!_.isUndefined(data)) { state.notificationsGraphConfig.watchOnSelf = data; } @@ -131,14 +130,14 @@ function seenIndex(json: any, state: HarkState) { function readEach(json: any, state: HarkState) { const data = _.get(json, 'read-each'); if(data) { - updateUnreads(state, data.index, u => u.delete(data.target)) + updateUnreads(state, data.index, u => u.delete(data.target)); } } function readSince(json: any, state: HarkState) { const data = _.get(json, 'read-count'); if(data) { - updateUnreadCount(state, data, () => 0) + updateUnreadCount(state, data, () => 0); } } @@ -152,7 +151,7 @@ function unreadSince(json: any, state: HarkState) { function unreadEach(json: any, state: HarkState) { const data = _.get(json, 'unread-each'); if(data) { - updateUnreads(state, data.index, us => us.add(data.target)) + updateUnreads(state, data.index, us => us.add(data.target)); } } @@ -175,15 +174,15 @@ function unreads(json: any, state: HarkState) { } } -function clearState(state){ - let initialState = { +function clearState(state) { + const initialState = { notifications: new BigIntOrderedMap(), archivedNotifications: new BigIntOrderedMap(), notificationsGroupConfig: [], notificationsGraphConfig: { watchOnSelf: false, mentions: false, - watching: [], + watching: [] }, unreads: { graph: {}, @@ -192,7 +191,7 @@ function clearState(state){ notificationsCount: 0 }; - Object.keys(initialState).forEach(key => { + Object.keys(initialState).forEach((key) => { state[key] = initialState[key]; }); } @@ -203,7 +202,7 @@ function updateUnreadCount(state: HarkState, index: NotifIndex, count: (c: numbe } const property = [index.graph.graph, index.graph.index, 'unreads']; const curr = _.get(state.unreads.graph, property, 0); - const newCount = count(curr) + const newCount = count(curr); _.set(state.unreads.graph, property, newCount); } @@ -218,7 +217,6 @@ function updateUnreads(state: HarkState, index: NotifIndex, f: (us: Set) _.set(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], unreads); } - function updateNotificationStats(state: HarkState, index: NotifIndex, statField: 'notifications' | 'unreads' | 'last', f: (x: number) => number) { if(statField === 'notifications') { state.notificationsCount = f(state.notificationsCount); @@ -233,13 +231,13 @@ function updateNotificationStats(state: HarkState, index: NotifIndex, statField: } function added(json: any, state: HarkState) { - const data = _.get(json, "added", false); + const data = _.get(json, 'added', false); if (data) { const { index, notification } = data; const time = makePatDa(data.time); const timebox = state.notifications.get(time) || []; - const arrIdx = timebox.findIndex((idxNotif) => + const arrIdx = timebox.findIndex(idxNotif => notifIdxEqual(index, idxNotif.index) ); if (arrIdx !== -1) { @@ -256,14 +254,14 @@ function added(json: any, state: HarkState) { } const dnd = (json: any, state: HarkState) => { - const data = _.get(json, "set-dnd", undefined); + const data = _.get(json, 'set-dnd', undefined); if (!_.isUndefined(data)) { state.doNotDisturb = data; } }; const timebox = (json: any, state: HarkState) => { - const data = _.get(json, "timebox", false); + const data = _.get(json, 'timebox', false); if (data) { const time = makePatDa(data.time); if (!data.archive) { @@ -273,21 +271,21 @@ const timebox = (json: any, state: HarkState) => { }; function more(json: any, state: HarkState) { - const data = _.get(json, "more", false); + const data = _.get(json, 'more', false); if (data) { - _.forEach(data, (d) => reduce(d, state)); + _.forEach(data, d => reduce(d, state)); } } function notifIdxEqual(a: NotifIndex, b: NotifIndex) { - if ("graph" in a && "graph" in b) { + if ('graph' in a && 'graph' in b) { return ( a.graph.graph === b.graph.graph && a.graph.group === b.graph.group && a.graph.module === b.graph.module && a.graph.description === b.graph.description ); - } else if ("group" in a && "group" in b) { + } else if ('group' in a && 'group' in b) { return ( a.group.group === b.group.group && a.group.description === b.group.description @@ -305,14 +303,14 @@ function setRead( const patDa = makePatDa(time); const timebox = state.notifications.get(patDa); if (_.isNull(timebox)) { - console.warn("Modifying nonexistent timebox"); + console.warn('Modifying nonexistent timebox'); return; } - const arrIdx = timebox.findIndex((idxNotif) => + const arrIdx = timebox.findIndex(idxNotif => notifIdxEqual(index, idxNotif.index) ); if (arrIdx === -1) { - console.warn("Modifying nonexistent index"); + console.warn('Modifying nonexistent index'); return; } timebox[arrIdx].notification.read = read; @@ -320,7 +318,7 @@ function setRead( } function read(json: any, state: HarkState) { - const data = _.get(json, "read-note", false); + const data = _.get(json, 'read-note', false); if (data) { const { time, index } = data; updateNotificationStats(state, index, 'notifications', x => x-1); @@ -329,7 +327,7 @@ function read(json: any, state: HarkState) { } function unread(json: any, state: HarkState) { - const data = _.get(json, "unread-note", false); + const data = _.get(json, 'unread-note', false); if (data) { const { time, index } = data; updateNotificationStats(state, index, 'notifications', x => x+1); @@ -338,16 +336,16 @@ function unread(json: any, state: HarkState) { } function archive(json: any, state: HarkState) { - const data = _.get(json, "archive", false); + const data = _.get(json, 'archive', false); if (data) { const { index } = data; const time = makePatDa(data.time); const timebox = state.notifications.get(time); if (!timebox) { - console.warn("Modifying nonexistent timebox"); + console.warn('Modifying nonexistent timebox'); return; } - const [archived, unarchived] = _.partition(timebox, (idxNotif) => + const [archived, unarchived] = _.partition(timebox, idxNotif => notifIdxEqual(index, idxNotif.index) ); if(unarchived.length === 0) { @@ -357,6 +355,6 @@ function archive(json: any, state: HarkState) { state.notifications.set(time, unarchived); } const newlyRead = archived.filter(x => !x.notification.read).length; - updateNotificationStats(state, index, 'notifications', (x) => x - newlyRead); + updateNotificationStats(state, index, 'notifications', x => x - newlyRead); } } diff --git a/pkg/interface/src/logic/reducers/invite-update.ts b/pkg/interface/src/logic/reducers/invite-update.ts index 30fb61042..c4f472be0 100644 --- a/pkg/interface/src/logic/reducers/invite-update.ts +++ b/pkg/interface/src/logic/reducers/invite-update.ts @@ -1,10 +1,9 @@ import _ from 'lodash'; import { StoreState } from '../../store/type'; import { Cage } from '~/types/cage'; -import { InviteUpdate } from '~/types/invite-update'; - -type InviteState = Pick; +import { InviteUpdate } from '@urbit/api/invite'; +type InviteState = Pick; export default class InviteReducer { reduce(json: Cage, state: S) { diff --git a/pkg/interface/src/logic/reducers/launch-update.ts b/pkg/interface/src/logic/reducers/launch-update.ts index d05145d78..56a00efce 100644 --- a/pkg/interface/src/logic/reducers/launch-update.ts +++ b/pkg/interface/src/logic/reducers/launch-update.ts @@ -51,12 +51,11 @@ export default class LaunchReducer { changeIsShown(json: LaunchUpdate, state: S) { const data = _.get(json, 'changeIsShown', false); if (data) { - let tile = state.launch.tiles[data.name]; + const tile = state.launch.tiles[data.name]; console.log(tile); if (tile) { tile.isShown = data.isShown; } } } - } diff --git a/pkg/interface/src/logic/reducers/metadata-update.ts b/pkg/interface/src/logic/reducers/metadata-update.ts index 00070b87e..41e6d7985 100644 --- a/pkg/interface/src/logic/reducers/metadata-update.ts +++ b/pkg/interface/src/logic/reducers/metadata-update.ts @@ -2,14 +2,14 @@ import _ from 'lodash'; import { StoreState } from '../../store/type'; -import { MetadataUpdate } from '~/types/metadata-update'; +import { MetadataUpdate } from '@urbit/api/metadata'; import { Cage } from '~/types/cage'; type MetadataState = Pick; export default class MetadataReducer { reduce(json: Cage, state: S) { - let data = json['metadata-update'] + const data = json['metadata-update']; if (data) { console.log(data); this.associations(data, state); @@ -29,13 +29,13 @@ export default class MetadataReducer { } associations(json: MetadataUpdate, state: S) { - let data = _.get(json, 'associations', false); + const data = _.get(json, 'associations', false); if (data) { - let metadata = state.associations; + const metadata = state.associations; Object.keys(data).forEach((key) => { - let val = data[key]; - let appName = val['app-name']; - let rid = val.resource; + const val = data[key]; + const appName = val['app-name']; + const rid = val.resource; if (!(appName in metadata)) { metadata[appName] = {}; } @@ -50,11 +50,11 @@ export default class MetadataReducer { } add(json: MetadataUpdate, state: S) { - let data = _.get(json, 'add', false); + const data = _.get(json, 'add', false); if (data) { - let metadata = state.associations; - let appName = data['app-name']; - let appPath = data.resource; + const metadata = state.associations; + const appName = data['app-name']; + const appPath = data.resource; if (!(appName in metadata)) { metadata[appName] = {}; @@ -69,11 +69,11 @@ export default class MetadataReducer { } update(json: MetadataUpdate, state: S) { - let data = _.get(json, 'update-metadata', false); + const data = _.get(json, 'update-metadata', false); if (data) { - let metadata = state.associations; - let appName = data['app-name']; - let rid = data.resource; + const metadata = state.associations; + const appName = data['app-name']; + const rid = data.resource; if (!(appName in metadata)) { metadata[appName] = {}; @@ -88,11 +88,11 @@ export default class MetadataReducer { } remove(json: MetadataUpdate, state: S) { - let data = _.get(json, 'remove', false); + const data = _.get(json, 'remove', false); if (data) { - let metadata = state.associations; - let appName = data['app-name']; - let rid = data.resource; + const metadata = state.associations; + const appName = data['app-name']; + const rid = data.resource; if (appName in metadata && rid in metadata[appName]) { delete metadata[appName][rid]; diff --git a/pkg/interface/src/logic/reducers/settings-update.ts b/pkg/interface/src/logic/reducers/settings-update.ts index 9716cbb85..7afef84ec 100644 --- a/pkg/interface/src/logic/reducers/settings-update.ts +++ b/pkg/interface/src/logic/reducers/settings-update.ts @@ -1,21 +1,21 @@ import _ from 'lodash'; import { StoreState } from '../../store/type'; import { - SettingsUpdate, -} from '~/types/settings'; + SettingsUpdate +} from '@urbit/api/settings'; type SettingsState = Pick; -export default class SettingsReducer{ +export default class SettingsReducer { reduce(json: Cage, state: S) { - let data = json["settings-event"]; + let data = json['settings-event']; if (data) { this.putBucket(data, state); this.delBucket(data, state); this.putEntry(data, state); this.delEntry(data, state); } - data = json["settings-data"]; + data = json['settings-data']; if (data) { this.getAll(data, state); this.getBucket(data, state); @@ -26,31 +26,31 @@ export default class SettingsReducer{ putBucket(json: SettingsUpdate, state: S) { const data = _.get(json, 'put-bucket', false); if (data) { - state.settings[data["bucket-key"]] = data.bucket; + state.settings[data['bucket-key']] = data.bucket; } } delBucket(json: SettingsUpdate, state: S) { const data = _.get(json, 'del-bucket', false); if (data) { - delete state.settings[data["bucket-key"]]; + delete state.settings[data['bucket-key']]; } } putEntry(json: SettingsUpdate, state: S) { const data = _.get(json, 'put-entry', false); if (data) { - if (!state.settings[data["bucket-key"]]) { - state.settings[data["bucket-key"]] = {}; + if (!state.settings[data['bucket-key']]) { + state.settings[data['bucket-key']] = {}; } - state.settings[data["bucket-key"]][data["entry-key"]] = data.value; + state.settings[data['bucket-key']][data['entry-key']] = data.value; } } delEntry(json: SettingsUpdate, state: S) { const data = _.get(json, 'del-entry', false); if (data) { - delete state.settings[data["bucket-key"]][data["entry-key"]]; + delete state.settings[data['bucket-key']][data['entry-key']]; } } diff --git a/pkg/interface/src/logic/state/local.tsx b/pkg/interface/src/logic/state/local.tsx index 64e68a0ca..70e76c468 100644 --- a/pkg/interface/src/logic/state/local.tsx +++ b/pkg/interface/src/logic/state/local.tsx @@ -1,10 +1,9 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode } from 'react'; import f from 'lodash/fp'; import create, { State } from 'zustand'; import { persist } from 'zustand/middleware'; import produce from 'immer'; -import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress } from "~/types/local-update"; - +import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress } from '~/types/local-update'; export interface LocalState extends State { hideAvatars: boolean; @@ -22,8 +21,8 @@ export interface LocalState extends State { suspendedFocus?: HTMLElement; toggleOmnibox: () => void; set: (fn: (state: LocalState) => void) => void -}; -export const selectLocalState = +} +export const selectLocalState = (keys: K[]) => f.pick(keys); const useLocalState = create(persist((set, get) => ({ @@ -33,21 +32,21 @@ const useLocalState = create(persist((set, get) => ({ hideNicknames: false, tutorialProgress: 'hidden', tutorialRef: null, - setTutorialRef: (el: HTMLElement | null) => set(produce(state => { + setTutorialRef: (el: HTMLElement | null) => set(produce((state) => { state.tutorialRef = el; })), - hideTutorial: () => set(produce(state => { + hideTutorial: () => set(produce((state) => { state.tutorialProgress = 'hidden'; state.tutorialRef = null; })), - nextTutStep: () => set(produce(state => { - const currIdx = tutorialProgress.findIndex(p => p === state.tutorialProgress) + nextTutStep: () => set(produce((state) => { + const currIdx = tutorialProgress.findIndex(p => p === state.tutorialProgress); if(currIdx < tutorialProgress.length) { state.tutorialProgress = tutorialProgress[currIdx + 1]; } })), - prevTutStep: () => set(produce(state => { - const currIdx = tutorialProgress.findIndex(p => p === state.tutorialProgress) + prevTutStep: () => set(produce((state) => { + const currIdx = tutorialProgress.findIndex(p => p === state.tutorialProgress); if(currIdx > 0) { state.tutorialProgress = tutorialProgress[currIdx - 1]; } @@ -56,11 +55,11 @@ const useLocalState = create(persist((set, get) => ({ imageShown: true, audioShown: true, videoShown: true, - oembedShown: true, + oembedShown: true }, omniboxShown: false, suspendedFocus: undefined, - toggleOmnibox: () => set(produce(state => { + toggleOmnibox: () => set(produce((state) => { state.omniboxShown = !state.omniboxShown; if (typeof state.suspendedFocus?.focus === 'function') { state.suspendedFocus.focus(); @@ -86,7 +85,7 @@ function withLocalState(Component: any, stateMemb (object, key) => ({ ...object, [key]: state[key] }), {} ) ): useLocalState(); - return + return ; }); } diff --git a/pkg/interface/src/logic/store/base.ts b/pkg/interface/src/logic/store/base.ts index 9a48ebd80..faeacca1e 100644 --- a/pkg/interface/src/logic/store/base.ts +++ b/pkg/interface/src/logic/store/base.ts @@ -19,7 +19,7 @@ export default class BaseStore { clear() { this.handleEvent({ - data: { clear: true }, + data: { clear: true } }); } @@ -30,7 +30,7 @@ export default class BaseStore { return; } - if ("clear" in json && json.clear) { + if ('clear' in json && json.clear) { this.setState(this.initialState()); return; } diff --git a/pkg/interface/src/logic/store/store.ts b/pkg/interface/src/logic/store/store.ts index b2ab60c6c..e56723ace 100644 --- a/pkg/interface/src/logic/store/store.ts +++ b/pkg/interface/src/logic/store/store.ts @@ -6,7 +6,7 @@ import MetadataReducer from '../reducers/metadata-update'; import LocalReducer from '../reducers/local'; import { StoreState } from './type'; -import { Timebox } from '~/types'; +import { Timebox } from '@urbit/api'; import { Cage } from '~/types/cage'; import S3Reducer from '../reducers/s3-update'; import { GraphReducer } from '../reducers/graph-update'; @@ -16,10 +16,9 @@ import GroupReducer from '../reducers/group-update'; import LaunchReducer from '../reducers/launch-update'; import ConnectionReducer from '../reducers/connection'; import SettingsReducer from '../reducers/settings-update'; -import {OrderedMap} from '../lib/OrderedMap'; +import { OrderedMap } from '../lib/OrderedMap'; import { BigIntOrderedMap } from '../lib/BigIntOrderedMap'; -import {GroupViewReducer} from '../reducers/group-view'; - +import { GroupViewReducer } from '../reducers/group-view'; export default class GlobalStore extends BaseStore { inviteReducer = new InviteReducer(); @@ -58,7 +57,7 @@ export default class GlobalStore extends BaseStore { invites: {}, associations: { groups: {}, - graph: {}, + graph: {} }, groups: {}, groupKeys: new Set(), @@ -67,7 +66,7 @@ export default class GlobalStore extends BaseStore { launch: { firstTime: false, tileOrdering: [], - tiles: {}, + tiles: {} }, weather: {}, userLocation: null, @@ -87,7 +86,7 @@ export default class GlobalStore extends BaseStore { notificationsGraphConfig: { watchOnSelf: false, mentions: false, - watching: [], + watching: [] }, unreads: { graph: {}, @@ -95,7 +94,7 @@ export default class GlobalStore extends BaseStore { }, notificationsCount: 0, settings: {}, - pendingJoin: {}, + pendingJoin: {} }; } diff --git a/pkg/interface/src/logic/store/type.ts b/pkg/interface/src/logic/store/type.ts index 96b68f8a2..2348aedf9 100644 --- a/pkg/interface/src/logic/store/type.ts +++ b/pkg/interface/src/logic/store/type.ts @@ -1,20 +1,20 @@ -import { Path } from '~/types/noun'; -import { Invites } from '~/types/invite-update'; -import { Associations } from '~/types/metadata-update'; -import { Rolodex } from '~/types/contact-update'; -import { Groups } from '~/types/group-update'; +import { Path } from '@urbit/api'; +import { Invites } from '@urbit/api/invite'; +import { Associations } from '@urbit/api/metadata'; +import { Rolodex } from '@urbit/api/contacts'; +import { Groups } from '@urbit/api/groups'; import { S3State } from '~/types/s3-update'; import { LaunchState, WeatherState } from '~/types/launch-update'; import { ConnectionStatus } from '~/types/connection'; -import {Graphs} from '~/types/graph-update'; +import { Graphs } from '@urbit/api/graph'; import { Notifications, - NotificationGraphConfig, + NotificationGraphConfig, GroupNotificationsConfig, Unreads, JoinRequests, Patp -} from "~/types"; +} from '@urbit/api'; export interface StoreState { // local state @@ -35,7 +35,6 @@ export interface StoreState { graphs: Graphs; graphKeys: Set; - // App specific states // launch state launch: LaunchState; diff --git a/pkg/interface/src/logic/subscription/base.ts b/pkg/interface/src/logic/subscription/base.ts index eb4b3b3af..debf66541 100644 --- a/pkg/interface/src/logic/subscription/base.ts +++ b/pkg/interface/src/logic/subscription/base.ts @@ -1,6 +1,6 @@ -import BaseStore from "../store/base"; -import BaseApi from "../api/base"; -import { Path } from "~/types/noun"; +import BaseStore from '../store/base'; +import BaseApi from '../api/base'; +import { Path } from '@urbit/api'; export default class BaseSubscription { private errorCount = 0; @@ -19,24 +19,24 @@ export default class BaseSubscription { // Exists to allow subclasses to hook restart() { - this.handleEvent({ data: { connection: 'reconnecting' }}); + this.handleEvent({ data: { connection: 'reconnecting' } }); this.start(); } onChannelOpen(e: any) { this.errorCount = 0; - this.handleEvent({ data: { connection: 'connected' }}); + this.handleEvent({ data: { connection: 'connected' } }); } onChannelError(err) { console.error('event source error: ', err); this.errorCount++; if(this.errorCount >= 5) { - console.error("bailing out, too many retries"); - this.handleEvent({ data: { connection: 'disconnected' }}); + console.error('bailing out, too many retries'); + this.handleEvent({ data: { connection: 'disconnected' } }); return; } - this.handleEvent({ data: { connection: 'reconnecting' }}); + this.handleEvent({ data: { connection: 'reconnecting' } }); setTimeout(() => { this.restart(); }, Math.pow(2,this.errorCount - 1) * 750); diff --git a/pkg/interface/src/logic/subscription/global.ts b/pkg/interface/src/logic/subscription/global.ts index a434c41e0..5b444f06d 100644 --- a/pkg/interface/src/logic/subscription/global.ts +++ b/pkg/interface/src/logic/subscription/global.ts @@ -1,9 +1,8 @@ import BaseSubscription from './base'; import { StoreState } from '../store/type'; -import { Path } from '~/types/noun'; +import { Path } from '@urbit/api'; import _ from 'lodash'; - /** * Path to subscribe on and app to subscribe to */ @@ -68,7 +67,7 @@ export default class GlobalSubscription extends BaseSubscription { } stopApp(app: AppName) { - this.openSubscriptions[app].map(id => this.unsubscribe(id)) + this.openSubscriptions[app].map(id => this.unsubscribe(id)); this.openSubscriptions[app] = []; } } diff --git a/pkg/interface/src/types/cage.ts b/pkg/interface/src/types/cage.ts index 266188543..044fb4621 100644 --- a/pkg/interface/src/types/cage.ts +++ b/pkg/interface/src/types/cage.ts @@ -1,21 +1,17 @@ -import { ContactUpdate } from "./contact-update"; -import { InviteUpdate } from "./invite-update"; -import { LocalUpdate } from "./local-update"; -import { MetadataUpdate } from "./metadata-update"; -import { GroupUpdate } from "./group-update"; -import { LaunchUpdate, WeatherState } from "./launch-update"; -import { ConnectionStatus } from "./connection"; -import { SettingsUpdate } from "./settings"; +import { LocalUpdate } from './local-update'; +import { LaunchUpdate, WeatherState } from './launch-update'; +import { ConnectionStatus } from './connection'; +import { ContactUpdate, GroupUpdate, InviteUpdate, MetadataUpdate } from '@urbit/api'; +import { SettingsUpdate } from '@urbit/api/settings'; interface MarksToTypes { readonly json: any; - readonly "contact-update": ContactUpdate; - readonly "invite-update": InviteUpdate; - readonly "metadata-update": MetadataUpdate; + readonly 'contact-update': ContactUpdate; + readonly 'invite-update': InviteUpdate; + readonly 'metadata-update': MetadataUpdate; readonly groupUpdate: GroupUpdate; - readonly "launch-update": LaunchUpdate; - readonly "link-listen-update": LinkListenUpdate; - readonly "settings-event": SettingsUpdate; + readonly 'launch-update': LaunchUpdate; + readonly 'settings-event': SettingsUpdate; // not really marks but w/e readonly 'local': LocalUpdate; readonly 'weather': WeatherState | {}; diff --git a/pkg/interface/src/types/connection.ts b/pkg/interface/src/types/connection.ts index 6f624238e..38b06b09a 100644 --- a/pkg/interface/src/types/connection.ts +++ b/pkg/interface/src/types/connection.ts @@ -1,2 +1 @@ - export type ConnectionStatus = 'reconnecting' | 'disconnected' | 'connected'; diff --git a/pkg/interface/src/types/contact-update.ts b/pkg/interface/src/types/contact-update.ts deleted file mode 100644 index e48afc739..000000000 --- a/pkg/interface/src/types/contact-update.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Path, Patp } from "./noun"; - -export type ContactUpdate = - | ContactUpdateCreate - | ContactUpdateDelete - | ContactUpdateAdd - | ContactUpdateRemove - | ContactUpdateEdit - | ContactUpdateInitial - | ContactUpdateContacts; - -interface ContactUpdateCreate { - create: Path; -} - -interface ContactUpdateDelete { - delete: Path; -} - -interface ContactUpdateAdd { - add: { - path: Path; - ship: Patp; - contact: Contact; - }; -} - -interface ContactUpdateRemove { - remove: { - path: Path; - ship: Patp; - }; -} - -interface ContactUpdateEdit { - edit: { - path: Path; - ship: Patp; - "edit-field": ContactEdit; - }; -} - -interface ContactUpdateInitial { - initial: Rolodex; -} - -interface ContactUpdateContacts { - contacts: { - path: Path; - contacts: Contacts; - }; -} - -// - -type ContactAvatar = ContactAvatarUrl | ContactAvatarOcts; - -export type Rolodex = { - [p in Path]: Contacts; -}; - -export type Contacts = { - [p in Patp]: Contact; -}; - -interface ContactAvatarUrl { - url: string; -} - -interface ContactAvatarOcts { - octs: string; -} -export interface Contact { - nickname: string; - email: string; - phone: string; - website: string; - notes: string; - color: string; - avatar: string | null; -} - -export type ContactEdit = { - [k in keyof Contact]: Contact[k]; -}; diff --git a/pkg/interface/src/types/global.ts b/pkg/interface/src/types/global.ts index d43a77b1b..4294f574f 100644 --- a/pkg/interface/src/types/global.ts +++ b/pkg/interface/src/types/global.ts @@ -1,4 +1,4 @@ -import { PatpNoSig } from "./noun"; +import { PatpNoSig } from '@urbit/api'; declare global { interface Window { diff --git a/pkg/interface/src/types/graph-update.ts b/pkg/interface/src/types/graph-update.ts deleted file mode 100644 index ad071b97d..000000000 --- a/pkg/interface/src/types/graph-update.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Patp } from "./noun"; -import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap"; - -export interface TextContent { - text: string; -} -export interface UrlContent { - url: string; -} -export interface CodeContent { - code: { - expresssion: string; - output: string | undefined; - } -} - -export interface ReferenceContent { - uid: string; -} -export interface MentionContent { - mention: string; -} -export type Content = - | TextContent - | UrlContent - | CodeContent - | ReferenceContent - | MentionContent; - -export interface Post { - author: Patp; - contents: Content[]; - hash: string | null; - index: string; - pending?: boolean; - signatures: string[]; - "time-sent": number; -} - -export interface GraphNode { - children: Graph; - post: Post; -} - -export type Graph = BigIntOrderedMap; - -export type Graphs = { [rid: string]: Graph }; diff --git a/pkg/interface/src/types/group-update.ts b/pkg/interface/src/types/group-update.ts deleted file mode 100644 index ad7c35004..000000000 --- a/pkg/interface/src/types/group-update.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { PatpNoSig, Path, Jug, ShipRank, Enc } from './noun'; - -export const roleTags = ['janitor', 'moderator', 'admin'] as const; -export type RoleTags = typeof roleTags[number]; -interface RoleTag { - tag: 'admin' | 'moderator' | 'janitor'; -} - -interface AppTag { - app: string; - resource: string; - tag: string; -} - -export type Tag = AppTag | RoleTag; - -export interface InvitePolicy { - invite: { - pending: Set; - }; -} - -export interface OpenPolicy { - open: { - banned: Set; - banRanks: Set; - }; -} - -export interface Resource { - name: string; - ship: PatpNoSig; -} - -export type OpenPolicyDiff = - | AllowRanksDiff - | BanRanksDiff - | AllowShipsDiff - | BanShipsDiff; - -interface AllowRanksDiff { - allowRanks: ShipRank[]; -} - -interface BanRanksDiff { - banRanks: ShipRank[]; -} - -interface AllowShipsDiff { - allowShips: PatpNoSig[]; -} - -interface BanShipsDiff { - banShips: PatpNoSig[]; -} - -export type InvitePolicyDiff = AddInvitesDiff | RemoveInvitesDiff; - -interface AddInvitesDiff { - addInvites: PatpNoSig[]; -} - -interface RemoveInvitesDiff { - removeInvites: PatpNoSig[]; -} - -interface ReplacePolicyDiff { - replace: GroupPolicy; -} - -export type GroupPolicyDiff = - | { open: OpenPolicyDiff } - | { invite: InvitePolicyDiff } - | ReplacePolicyDiff; - -export type GroupPolicy = OpenPolicy | InvitePolicy; - -interface TaggedShips { - [tag: string]: Set; -} - -export interface Tags { - role: TaggedShips; - [app: string]: TaggedShips; -} - -export interface Group { - members: Set; - tags: Tags; - policy: GroupPolicy; - hidden: boolean; -} - -export type Groups = { - [p in Path]: Group; -}; - -interface GroupUpdateInitial { - initial: Enc; -} - -interface GroupUpdateAddGroup { - addGroup: { - resource: Resource; - policy: Enc; - hidden: boolean; - }; -} - -interface GroupUpdateAddMembers { - addMembers: { - ships: PatpNoSig[]; - resource: Resource; - }; -} - -interface GroupUpdateRemoveMembers { - removeMembers: { - ships: PatpNoSig[]; - resource: Resource; - }; -} - -interface GroupUpdateAddTag { - addTag: { - tag: Tag; - resource: Resource; - ships: PatpNoSig[]; - }; -} - -interface GroupUpdateRemoveTag { - removeTag: { - tag: Tag; - resource: Resource; - ships: PatpNoSig; - }; -} - -interface GroupUpdateChangePolicy { - changePolicy: { resource: Resource; diff: GroupPolicyDiff }; -} - -interface GroupUpdateRemoveGroup { - removeGroup: { - resource: Resource; - }; -} - -interface GroupUpdateExpose { - expose: { - resource: Resource; - }; -} - -interface GroupUpdateInitialGroup { - initialGroup: { - resource: Resource; - group: Enc; - }; -} - -export type GroupUpdate = - | GroupUpdateInitial - | GroupUpdateAddGroup - | GroupUpdateAddMembers - | GroupUpdateRemoveMembers - | GroupUpdateAddTag - | GroupUpdateRemoveTag - | GroupUpdateChangePolicy - | GroupUpdateRemoveGroup - | GroupUpdateExpose - | GroupUpdateInitialGroup; - -export type GroupAction = Omit; - -export const groupBunts = { - group: (): Group => ({ members: new Set(), tags: { role: {} }, hidden: false, policy: groupBunts.policy() }), - policy: (): GroupPolicy => ({ open: { banned: new Set(), banRanks: new Set() } }) -}; diff --git a/pkg/interface/src/types/group-view.ts b/pkg/interface/src/types/group-view.ts deleted file mode 100644 index 635a2954a..000000000 --- a/pkg/interface/src/types/group-view.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const joinError = ['no-perms', 'strange'] as const; -export type JoinError = typeof joinError[number]; -export const joinResult = ['done', ...joinError] as const; -export type JoinResult = typeof joinResult[number]; - -export const joinProgress = ['start', 'added', ...joinResult] as const; -export type JoinProgress = typeof joinProgress[number]; - -export interface JoinRequests { - [rid: string]: JoinProgress; -} diff --git a/pkg/interface/src/types/hark-update.ts b/pkg/interface/src/types/hark-update.ts deleted file mode 100644 index 156f96b66..000000000 --- a/pkg/interface/src/types/hark-update.ts +++ /dev/null @@ -1,68 +0,0 @@ -import _ from "lodash"; -import { Post } from "./graph-update"; -import { GroupUpdate } from "./group-update"; -import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap"; - -export type GraphNotifDescription = "link" | "comment" | "note" | "mention"; - -export interface UnreadStats { - unreads: Set | number; - notifications: number; - last: number; -} - -export interface GraphNotifIndex { - graph: string; - group: string; - description: GraphNotifDescription; - module: string; - index: string; -} - -export interface GroupNotifIndex { - group: string; - description: string; -} - -export type NotifIndex = - | { graph: GraphNotifIndex } - | { group: GroupNotifIndex }; - -export type GraphNotificationContents = Post[]; - -export type GroupNotificationContents = GroupUpdate[]; - -export type NotificationContents = - | { graph: GraphNotificationContents } - | { group: GroupNotificationContents }; -export interface Notification { - read: boolean; - time: number; - contents: NotificationContents; -} - -export interface IndexedNotification { - index: NotifIndex; - notification: Notification; -} - -export type Timebox = IndexedNotification[]; - -export type Notifications = BigIntOrderedMap; - -export interface NotificationGraphConfig { - watchOnSelf: boolean; - mentions: boolean; - watching: WatchedIndex[] -} - -export interface Unreads { - graph: Record>; - group: Record; -} - -interface WatchedIndex { - graph: string; - index: string; -} -export type GroupNotificationsConfig = string[]; diff --git a/pkg/interface/src/types/index.ts b/pkg/interface/src/types/index.ts index 5c1d81b0b..c4abcba64 100644 --- a/pkg/interface/src/types/index.ts +++ b/pkg/interface/src/types/index.ts @@ -1,16 +1,8 @@ export * from './cage'; export * from './connection'; -export * from './contact-update'; export * from './global'; -export * from './group-update'; -export * from './group-view'; -export * from './graph-update'; -export * from './hark-update'; -export * from './invite-update'; export * from './launch-update'; export * from './local-update'; -export * from './metadata-update'; -export * from './noun'; export * from './s3-update'; export * from './workspace'; export * from './util'; diff --git a/pkg/interface/src/types/invite-update.ts b/pkg/interface/src/types/invite-update.ts deleted file mode 100644 index b897687a4..000000000 --- a/pkg/interface/src/types/invite-update.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Serial, PatpNoSig, Path } from './noun'; -import {Resource} from './group-update'; - -export type InviteUpdate = - InviteUpdateInitial -| InviteUpdateCreate -| InviteUpdateDelete -| InviteUpdateInvite -| InviteUpdateAccepted -| InviteUpdateDecline; - - -interface InviteUpdateInitial { - initial: Invites; -} - -interface InviteUpdateCreate { - create: { - path: Path; - }; -} - -interface InviteUpdateDelete { - delete: { - path: Path; - }; -} - -interface InviteUpdateInvite { - invite: { - path: Path; - uid: Serial; - invite: Invite; - }; -} - -interface InviteUpdateAccepted { - accepted: { - path: Path; - uid: Serial; - }; -} - -interface InviteUpdateDecline { - decline: { - path: Path; - uid: Serial; - }; -} - -// actual datastructures - - -export type Invites = { - [p in Path]: AppInvites; -}; - -export type AppInvites = { - [s in Serial]: Invite; -}; - -export interface Invite { - app: string; - recipient: PatpNoSig; - resource: Resource; - ship: PatpNoSig; - text: string; -} diff --git a/pkg/interface/src/types/launch-update.ts b/pkg/interface/src/types/launch-update.ts index 099987845..879c3620b 100644 --- a/pkg/interface/src/types/launch-update.ts +++ b/pkg/interface/src/types/launch-update.ts @@ -1,11 +1,9 @@ - export type LaunchUpdate = LaunchUpdateInitial | LaunchUpdateFirstTime | LaunchUpdateOrder | LaunchUpdateIsShown; - interface LaunchUpdateInitial { initial: LaunchState; } diff --git a/pkg/interface/src/types/metadata-update.ts b/pkg/interface/src/types/metadata-update.ts deleted file mode 100644 index 640bd5a9b..000000000 --- a/pkg/interface/src/types/metadata-update.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { AppName, Path, Patp } from './noun'; - - -export type MetadataUpdate = - MetadataUpdateInitial -| MetadataUpdateAdd -| MetadataUpdateUpdate -| MetadataUpdateRemove; - -interface MetadataUpdateInitial { - associations: ResourceAssociations; -} - -type ResourceAssociations = { - [p in Path]: Association; -} - -type MetadataUpdateAdd = { - add: Association; -} - -type MetadataUpdateUpdate = { - update: Association; -} - -type MetadataUpdateRemove = { - remove: Resource & { - group: Path; - } -} - -export interface MetadataUpdatePreview { - group: string; - channels: Associations; - "channel-count": number; - members: number; - metadata: Metadata; -} - -export type Associations = Record; - -export type AppAssociations = { - [p in Path]: Association; -} - -interface Resource { - resource: Path; - 'app-name': AppName; -} - -export type Association = Resource & { - group: Path; - metadata: Metadata; -}; - -export interface Metadata { - color: string; - creator: Patp; - 'date-created': string; - description: string; - title: string; - module: string; - picture: string; - preview: boolean; - vip: PermVariation; -} - -export type PermVariation = '' | 'reader-comments' | 'member-metadata'; diff --git a/pkg/interface/src/types/noun.ts b/pkg/interface/src/types/noun.ts deleted file mode 100644 index f566f0240..000000000 --- a/pkg/interface/src/types/noun.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Martian embassy - */ - -// an urbit style path rendered as string -export type Path = string; - -// patp including leading sig -export type Patp = string; - -// patp excluding leading sig -export type PatpNoSig = string; - -// @uvH encoded string -export type Serial = string; - -// jug from hoon -export type Jug = Map>; - -// name of app -export type AppName = 'contacts' | 'groups' | 'graph'; - -export function getTagFromFrond(frond: O): keyof O { - const tags = Object.keys(frond) as Array; - const tag = tags[0]; - if(!tag) { - throw new Error("bad frond"); - } - return tag; -} - -export type ShipRank = 'czar' | 'king' | 'duke' | 'earl' | 'pawn'; - - -export type SetElement = S extends Set<(infer T)> ? T : never; -export type MapKey = M extends Map<(infer K), any> ? K : never; -export type MapValue = M extends Map ? V : never; - -/** - * Turns sets into arrays and maps into objects so we can send them over the wire - */ -export type Enc = - S extends Set ? - Enc>[] : - S extends Map ? - { [s: string]: Enc> } : - S extends object ? - { [K in keyof S]: Enc } : - S; diff --git a/pkg/interface/src/types/s3-update.ts b/pkg/interface/src/types/s3-update.ts index 3dfa51622..88763b7d6 100644 --- a/pkg/interface/src/types/s3-update.ts +++ b/pkg/interface/src/types/s3-update.ts @@ -1,6 +1,4 @@ - - - export interface S3Credentials { +export interface S3Credentials { endpoint: string; accessKeyId: string; secretAccessKey: string; @@ -51,7 +49,6 @@ interface S3UpdateSecretAccessKey { setSecretAccessKey: string; } - export type S3Update = S3UpdateCredentials | S3UpdateConfiguration diff --git a/pkg/interface/src/types/settings.ts b/pkg/interface/src/types/settings.ts deleted file mode 100644 index f0f50df49..000000000 --- a/pkg/interface/src/types/settings.ts +++ /dev/null @@ -1,55 +0,0 @@ -export type Key = string; -export type Value = string | boolean | number; -export type Bucket = Map; -export type Settings = Map; - -interface PutBucket { - "put-bucket": { - "bucket-key": Key; - "bucket": Bucket; - }; -} - -interface DelBucket { - "del-bucket": { - "bucket-key": Key; - }; -} - -interface PutEntry { - "put-entry": { - "bucket-key": Key; - "entry-key": Key; - "value": Value; - }; -} - -interface DelEntry { - "del-entry": { - "bucket-key": Key; - "entry-key": Key; - }; -} - -interface AllData { - "all": Settings; -} - -interface BucketData { - "bucket": Bucket; -} - -interface EntryData { - "entry": Value; -} - -export type SettingsUpdate = - | PutBucket - | DelBucket - | PutEntry - | DelEntry; - -export type SettingsData = - | AllData - | BucketData - | EntryData; diff --git a/pkg/interface/src/types/util.ts b/pkg/interface/src/types/util.ts index 2f3d390e5..f247978ca 100644 --- a/pkg/interface/src/types/util.ts +++ b/pkg/interface/src/types/util.ts @@ -1,4 +1,4 @@ -import { Icon } from "@tlon/indigo-react"; +import { Icon } from '@tlon/indigo-react'; export type PropFunc any> = Parameters[0]; export type Primitive = string | number | undefined | symbol | null | boolean; diff --git a/pkg/interface/src/types/workspace.ts b/pkg/interface/src/types/workspace.ts index 69da82ed3..e8238e725 100644 --- a/pkg/interface/src/types/workspace.ts +++ b/pkg/interface/src/types/workspace.ts @@ -1,5 +1,3 @@ - - interface GroupWorkspace { type: 'group'; group: string; diff --git a/pkg/interface/src/views/apps/chat/ChatResource.tsx b/pkg/interface/src/views/apps/chat/ChatResource.tsx index 5e8a35e95..03a887a88 100644 --- a/pkg/interface/src/views/apps/chat/ChatResource.tsx +++ b/pkg/interface/src/views/apps/chat/ChatResource.tsx @@ -3,7 +3,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { Col } from '@tlon/indigo-react'; import _ from 'lodash'; -import { Association } from '~/types/metadata-update'; +import { Association } from '@urbit/api/metadata'; import { StoreState } from '~/logic/store/type'; import { useFileDrag } from '~/logic/lib/useDrag'; import ChatWindow from './components/ChatWindow'; @@ -86,12 +86,16 @@ export function ChatResource(props: ChatResourceProps) { useEffect(() => { (async () => { - if (!res) { return; } - if (!group) { return; } + if (!res) { + return; +} + if (!group) { + return; +} if (group.hidden) { const members = _.compact(await Promise.all( Array.from(group.members) - .map(s => { + .map((s) => { const ship = `~${s}`; if(s === window.ship) { return Promise.resolve(null); @@ -101,7 +105,7 @@ export function ChatResource(props: ChatResourceProps) { 'personal', ship, true - ).then(isAllowed => { + ).then((isAllowed) => { return isAllowed ? null : ship; }); }) @@ -113,7 +117,6 @@ export function ChatResource(props: ChatResourceProps) { } else { setShowBanner(false); } - } else { const groupShared = await props.api.contacts.fetchIsAllowed( `~${window.ship}`, @@ -124,14 +127,13 @@ export function ChatResource(props: ChatResourceProps) { setShowBanner(!groupShared); } })(); - }, [groupPath]); if(!graph) { return ; } - var modifiedContacts = { ...contacts }; + const modifiedContacts = { ...contacts }; delete modifiedContacts[`~${window.ship}`]; return ( @@ -145,7 +147,7 @@ export function ChatResource(props: ChatResourceProps) { setShowBanner={setShowBanner} group={group} groupPath={groupPath} - /> + /> {dragging && } { inCodeMode: false }, async () => { const output = await props.api.graph.eval(text); - const contents: Content[] = [{ code: { output, expression: text }}]; + const contents: Content[] = [{ code: { output, expression: text } }]; const post = createPost(contents); props.api.graph.addPost(ship, name, post); }); return; } - const post = createPost(tokenizeMessage((text))) + const post = createPost(tokenizeMessage((text))); props.deleteMessage(); @@ -110,7 +110,7 @@ class ChatInput extends Component { if (!this.props.canUpload) { return; } - Array.from(files).forEach(file => { + Array.from(files).forEach((file) => { this.props.uploadDefault(file) .then(this.uploadSuccess) .catch(this.uploadError); @@ -178,7 +178,7 @@ class ChatInput extends Component { width="16" height="16" onClick={() => this.props.promptUpload().then(this.uploadSuccess)} - /> + /> : null } @@ -200,4 +200,4 @@ class ChatInput extends Component { } } -export default withLocalState(withS3(ChatInput, {accept: 'image/*'}), ['hideAvatars']); +export default withLocalState(withS3(ChatInput, { accept: 'image/*' }), ['hideAvatars']); diff --git a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx index 6252383dd..7f7cca0a3 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx @@ -17,7 +17,7 @@ import { useShowNickname, useHovering } from '~/logic/lib/util'; -import { Group, Association, Contacts, Post, Groups, Associations } from '~/types'; +import { Group, Association, Contacts, Post, Groups, Associations } from '@urbit/api'; import TextContent from './content/text'; import CodeContent from './content/code'; import RemoteContent from '~/views/components/RemoteContent'; @@ -66,12 +66,12 @@ interface ChatMessageProps { contacts: Contacts; className?: string; isPending: boolean; - style?: any; + style?: unknown; scrollWindow: HTMLDivElement; isLastMessage?: boolean; unreadMarkerRef: React.RefObject; - history: any; - api: any; + history: unknown; + api: GlobalApi; highlighted?: boolean; } @@ -149,7 +149,7 @@ export default class ChatMessage extends Component { highlighted, fontSize, associations, - groups, + groups }; const unreadContainerStyle = { @@ -230,7 +230,7 @@ export const MessageWithSigil = (props) => { fontSize } = props; - const dark = useLocalState((state) => state.dark); + const dark = useLocalState(state => state.dark); const datestamp = moment .unix(msg['time-sent'] / 1000) @@ -255,7 +255,7 @@ export const MessageWithSigil = (props) => { const [showOverlay, setShowOverlay] = useState(false); const toggleOverlay = () => { - setShowOverlay((value) => !value); + setShowOverlay(value => !value); }; const showCopyNotice = () => { diff --git a/pkg/interface/src/views/apps/chat/components/ChatWindow.tsx b/pkg/interface/src/views/apps/chat/components/ChatWindow.tsx index 8db2935a5..54d10b9d1 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatWindow.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatWindow.tsx @@ -1,22 +1,17 @@ -import React, { Component } from "react"; -import { RouteComponentProps } from "react-router-dom"; -import _ from "lodash"; +import React, { Component } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import _ from 'lodash'; import bigInt, { BigInteger } from 'big-integer'; import { Col } from '@tlon/indigo-react'; +import { Patp, Contacts, Association, Associations, Group, Groups, Graph } from '@urbit/api'; -import GlobalApi from "~/logic/api/global"; -import { Patp, Path } from "~/types/noun"; -import { Contacts } from "~/types/contact-update"; -import { Association, Associations } from "~/types/metadata-update"; -import { Group, Groups } from "~/types/group-update"; -import { Envelope, IMessage } from "~/types/chat-update"; -import { Graph } from "~/types"; +import GlobalApi from '~/logic/api/global'; -import VirtualScroller from "~/views/components/VirtualScroller"; +import VirtualScroller from '~/views/components/VirtualScroller'; import ChatMessage, { MessagePlaceholder } from './ChatMessage'; -import { UnreadNotice } from "./unread-notice"; +import { UnreadNotice } from './unread-notice'; const INITIAL_LOAD = 20; const DEFAULT_BACKLOG_SIZE = 100; @@ -66,8 +61,6 @@ export default class ChatWindow extends Component scrollTop) @@ -246,7 +242,6 @@ export default class ChatWindow extends Component {this.virtualList = list}} + ref={(list) => { + this.virtualList = list; +}} origin="bottom" style={{ height: '100%' }} onStartReached={() => { @@ -274,7 +271,8 @@ export default class ChatWindow extends Component { const msg = graph.get(index)?.post; - if (!msg) return null; + if (!msg) +return null; if (!this.state.initialized) { return ; } @@ -285,7 +283,6 @@ export default class ChatWindow extends Component alphabeticalOrder(a.metadata.title, b.metadata.title); - const getGraphUnreads = (associations: Associations, unreads: Unreads) => (path: string) => f.flow( f.pickBy((a: Association) => a.group === path), @@ -34,12 +33,11 @@ const getGraphNotifications = (associations: Associations, unreads: Unreads) => f.reduce(f.add, 0) )(associations.graph); - export default function Groups(props: GroupsProps & Parameters[0]) { const { associations, unreads, inbox, ...boxProps } = props; const groups = Object.values(associations?.groups || {}) - .filter((e) => e?.group in props.groups) + .filter(e => e?.group in props.groups) .sort(sortGroupsAlph); const graphUnreads = getGraphUnreads(associations || {}, unreads); const graphNotifications = getGraphNotifications(associations || {}, unreads); @@ -48,7 +46,7 @@ export default function Groups(props: GroupsProps & Parameters[0]) { <> {groups.map((group, index) => { const path = group?.group; - const unreadCount = graphUnreads(path) + const unreadCount = graphUnreads(path); const notCount = graphNotifications(path); return ( diff --git a/pkg/interface/src/views/apps/launch/components/ModalButton.tsx b/pkg/interface/src/views/apps/launch/components/ModalButton.tsx index eea2d3b6e..15b223e17 100644 --- a/pkg/interface/src/views/apps/launch/components/ModalButton.tsx +++ b/pkg/interface/src/views/apps/launch/components/ModalButton.tsx @@ -1,6 +1,6 @@ -import React from "react" -import { Box, Button, Icon, Text } from "@tlon/indigo-react" -import {useModal} from "~/logic/lib/useModal"; +import React from 'react'; +import { Box, Button, Icon, Text } from '@tlon/indigo-react'; +import { useModal } from '~/logic/lib/useModal'; const ModalButton = (props) => { const { @@ -13,7 +13,6 @@ const ModalButton = (props) => { } = props; const { modal, showModal } = useModal({ modal: props.children }); - return ( <> {modal} @@ -33,6 +32,6 @@ const ModalButton = (props) => { ); -} +}; export default ModalButton; diff --git a/pkg/interface/src/views/apps/links/LinkResource.tsx b/pkg/interface/src/views/apps/links/LinkResource.tsx index d85a2d770..0f61a7bf8 100644 --- a/pkg/interface/src/views/apps/links/LinkResource.tsx +++ b/pkg/interface/src/views/apps/links/LinkResource.tsx @@ -1,19 +1,18 @@ -import React, { useEffect, useCallback } from "react"; -import { Box, Row, Col, Center, LoadingSpinner, Text } from "@tlon/indigo-react"; -import { Switch, Route, Link } from "react-router-dom"; +import React, { useEffect } from 'react'; +import { Box, Col, Center, LoadingSpinner, Text } from '@tlon/indigo-react'; +import { Switch, Route, Link } from 'react-router-dom'; import bigInt from 'big-integer'; -import GlobalApi from "~/logic/api/global"; -import { StoreState } from "~/logic/store/type"; -import { uxToHex } from '~/logic/lib/util'; -import { RouteComponentProps } from "react-router-dom"; +import GlobalApi from '~/logic/api/global'; +import { StoreState } from '~/logic/store/type'; +import { RouteComponentProps } from 'react-router-dom'; -import { LinkItem } from "./components/LinkItem"; -import { LinkPreview } from "./components/link-preview"; -import { LinkWindow } from "./LinkWindow"; -import { Comments } from "~/views/components/Comments"; +import { LinkItem } from './components/LinkItem'; +import { LinkWindow } from './LinkWindow'; +import { Comments } from '~/views/components/Comments'; -import "./css/custom.css"; +import './css/custom.css'; +import { Association } from '@urbit/api/metadata'; const emptyMeasure = () => {}; @@ -38,11 +37,11 @@ export function LinkResource(props: LinkResourceProps) { history } = props; - const rid = association.resource; + const rid = association.resource; const relativePath = (p: string) => `${baseUrl}/resource/link${rid}${p}`; - const [, , ship, name] = rid.split("/"); + const [, , ship, name] = rid.split('/'); const resourcePath = `${ship.slice(1)}/${name}`; const resource = associations.graph[rid] ? associations.graph[rid] @@ -58,7 +57,7 @@ export function LinkResource(props: LinkResourceProps) { const resourceUrl = `${baseUrl}/resource/link${rid}`; if (!graph) { - return

; + return
; } return ( @@ -66,7 +65,7 @@ export function LinkResource(props: LinkResourceProps) { { return ( { const index = bigInt(props.match.params.index); const editCommentId = props.match.params.commentId || null; @@ -95,7 +94,7 @@ export function LinkResource(props: LinkResourceProps) { return
Malformed URL
; } - const node = !!graph ? graph.get(index) : null; + const node = graph ? graph.get(index) : null; if (!node) { return Not found; @@ -106,7 +105,7 @@ export function LinkResource(props: LinkResourceProps) { return ( - {"<- Back"} + {'<- Back'} { const list = virtualList?.current; - if(!list) return; + if(!list) +return; list.calculateVisibleItems(); }, [graph.size]); const first = graph.peekLargest()?.[0]; const [,,ship, name] = association.resource.split('/'); - const canWrite = isWriter(props.group, association.resource) + const canWrite = isWriter(props.group, association.resource); const style = useMemo(() => ({ - height: "100%", - width: "100%", + height: '100%', + width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' @@ -76,7 +77,7 @@ export function LinkWindow(props: LinkWindowProps) { return ( (virtualList.current = l ?? undefined)} + ref={l => (virtualList.current = l ?? undefined)} origin="top" style={style} onStartReached={() => {}} @@ -86,11 +87,12 @@ export function LinkWindow(props: LinkWindowProps) { renderer={({ index, measure, scrollWindow }) => { const node = graph.get(index); const post = node?.post; - if (!node || !post) return null; + if (!node || !post) +return null; const linkProps = { ...props, node, - measure, + measure }; if(canWrite && index.eq(first ?? bigInt.zero)) { return ( @@ -100,7 +102,7 @@ export function LinkWindow(props: LinkWindowProps) { - ) + ); } return ; }} diff --git a/pkg/interface/src/views/apps/links/components/LinkItem.tsx b/pkg/interface/src/views/apps/links/components/LinkItem.tsx index 8149fc580..4e02ae8b9 100644 --- a/pkg/interface/src/views/apps/links/components/LinkItem.tsx +++ b/pkg/interface/src/views/apps/links/components/LinkItem.tsx @@ -1,12 +1,12 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; +import React, { useState, useEffect, useRef, useCallback, ReactElement } from 'react'; import { Link } from 'react-router-dom'; + import { Row, Col, Anchor, Box, Text, Icon, Action } from '@tlon/indigo-react'; +import { GraphNode, Group, Rolodex, Unreads } from '@urbit/api'; import { writeText } from '~/logic/lib/util'; import Author from '~/views/components/Author'; - import { roleForShip } from '~/logic/lib/group'; -import { Contacts, GraphNode, Group, Rolodex, Unreads } from '~/types'; import GlobalApi from '~/logic/api/global'; import { Dropdown } from '~/views/components/Dropdown'; import RemoteContent from '~/views/components/RemoteContent'; @@ -22,7 +22,7 @@ interface LinkItemProps { measure: (el: any) => void; } -export const LinkItem = (props: LinkItemProps) => { +export const LinkItem = (props: LinkItemProps): ReactElement => { const { node, resource, @@ -46,7 +46,7 @@ export const LinkItem = (props: LinkItemProps) => { // FF will only update on next tick setTimeout(() => { console.log(remoteRef.current); - if(document.activeElement instanceof HTMLIFrameElement + if(document.activeElement instanceof HTMLIFrameElement && remoteRef?.current?.containerRef?.contains(document.activeElement)) { markRead(); } @@ -55,8 +55,7 @@ export const LinkItem = (props: LinkItemProps) => { window.addEventListener('blur', onBlur); return () => { window.removeEventListener('blur', onBlur); - } - + }; }, [markRead]); const URLparser = new RegExp( @@ -94,11 +93,9 @@ export const LinkItem = (props: LinkItemProps) => { const commColor = (props.unreads.graph?.[appPath]?.[`/${index}`]?.unreads ?? 0) > 0 ? 'blue' : 'gray'; const isUnread = props.unreads.graph?.[appPath]?.['/']?.unreads?.has(node.post.index); - - const onMeasure = useCallback(() => { ref.current && measure(ref.current); - }, [ref.current, measure]) + }, [ref.current, measure]); useEffect(() => { onMeasure(); @@ -120,7 +117,9 @@ export const LinkItem = (props: LinkItemProps) => { onClick={markRead} > { remoteRef.current = r}} + ref={(r) => { + remoteRef.current = r; +}} url={contents[1].url} text={contents[0].text} unfold={true} @@ -143,7 +142,8 @@ export const LinkItem = (props: LinkItemProps) => { alignSelf: 'center', style: { textOverflow: 'ellipsis', whiteSpace: 'pre', width: '100%' }, p: 2 - }} /> + }} + /> @@ -189,7 +189,7 @@ export const LinkItem = (props: LinkItemProps) => { } } - > + > diff --git a/pkg/interface/src/views/apps/links/components/LinkSubmit.tsx b/pkg/interface/src/views/apps/links/components/LinkSubmit.tsx index 2eaba4555..c9679062a 100644 --- a/pkg/interface/src/views/apps/links/components/LinkSubmit.tsx +++ b/pkg/interface/src/views/apps/links/components/LinkSubmit.tsx @@ -1,22 +1,22 @@ -import { BaseInput, Box, Button, LoadingSpinner, Text } from "@tlon/indigo-react"; -import React, { useCallback, useState } from "react"; -import GlobalApi from "~/logic/api/global"; -import { useFileDrag } from "~/logic/lib/useDrag"; -import useS3 from "~/logic/lib/useS3"; -import { S3State } from "~/types"; -import SubmitDragger from "~/views/components/SubmitDragger"; -import { createPost } from "~/logic/api/graph"; -import { hasProvider } from "oembed-parser"; +import { BaseInput, Box, Button, LoadingSpinner, Text } from '@tlon/indigo-react'; +import React, { useCallback, useState } from 'react'; +import GlobalApi from '~/logic/api/global'; +import { useFileDrag } from '~/logic/lib/useDrag'; +import useS3 from '~/logic/lib/useS3'; +import { S3State } from '@urbit/api'; +import SubmitDragger from '~/views/components/SubmitDragger'; +import { createPost } from '~/logic/api/graph'; +import { hasProvider } from 'oembed-parser'; interface LinkSubmitProps { api: GlobalApi; s3: S3State; name: string; ship: string; -}; +} const LinkSubmit = (props: LinkSubmitProps) => { - let { canUpload, uploadDefault, uploading, promptUpload } = useS3(props.s3); + const { canUpload, uploadDefault, uploading, promptUpload } = useS3(props.s3); const [submitFocused, setSubmitFocused] = useState(false); const [urlFocused, setUrlFocused] = useState(false); @@ -100,7 +100,7 @@ const LinkSubmit = (props: LinkSubmitProps) => { const onLinkChange = (linkValue: string) => { setLinkValueHook(linkValue); - const link = validateLink(linkValue) + const link = validateLink(linkValue); setLinkValid(link); }; @@ -133,7 +133,7 @@ const LinkSubmit = (props: LinkSubmitProps) => { px={2} pt={2} style={{ pointerEvents: 'none' }} - >{canUpload + >{canUpload ? <> Drop or{' '} { zIndex={9} alignItems="center" justifyContent="center" - > + > } {dragging && } @@ -223,4 +223,4 @@ const LinkSubmit = (props: LinkSubmitProps) => { ); }; -export default LinkSubmit; \ No newline at end of file +export default LinkSubmit; diff --git a/pkg/interface/src/views/apps/notifications/graph.tsx b/pkg/interface/src/views/apps/notifications/graph.tsx index 7517b0b50..f289f366c 100644 --- a/pkg/interface/src/views/apps/notifications/graph.tsx +++ b/pkg/interface/src/views/apps/notifications/graph.tsx @@ -1,31 +1,29 @@ -import React, { ReactNode, useCallback } from "react"; -import moment from "moment"; -import { Row, Box, Col, Text, Anchor, Icon, Action } from "@tlon/indigo-react"; -import { Link, useHistory } from "react-router-dom"; -import _ from "lodash"; +import React, { ReactElement, useCallback } from 'react'; +import moment from 'moment'; +import _ from 'lodash'; +import { useHistory } from 'react-router-dom'; +import styled from 'styled-components'; + +import { Row, Box, Col, Text, Anchor, Icon, Action } from '@tlon/indigo-react'; import { - Post, GraphNotifIndex, GraphNotificationContents, Associations, - Content, Rolodex, - Groups, -} from "~/types"; -import { Header } from "./header"; -import { cite, deSig, pluralize } from "~/logic/lib/util"; -import { Sigil } from "~/logic/lib/sigil"; -import RichText from "~/views/components/RichText"; -import GlobalApi from "~/logic/api/global"; -import ReactMarkdown from "react-markdown"; -import { getSnippet } from "~/logic/lib/publish"; -import styled from "styled-components"; -import {MentionText} from "~/views/components/MentionText"; -import ChatMessage, {MessageWithoutSigil} from "../chat/components/ChatMessage"; + Groups +} from '@urbit/api'; -function getGraphModuleIcon(module: string) { - if (module === "link") { - return "Collection"; +import { Header } from './header'; +import { cite, deSig, pluralize } from '~/logic/lib/util'; +import { Sigil } from '~/logic/lib/sigil'; +import GlobalApi from '~/logic/api/global'; +import { getSnippet } from '~/logic/lib/publish'; +import { MentionText } from '~/views/components/MentionText'; +import { MessageWithoutSigil } from '../chat/components/ChatMessage'; + +function getGraphModuleIcon(module: string): string { + if (module === 'link') { + return 'Collection'; } return _.capitalize(module); } @@ -34,32 +32,32 @@ const FilterBox = styled(Box)` background: linear-gradient( to bottom, transparent, - ${(p) => p.theme.colors.white} + ${p => p.theme.colors.white} ); `; -function describeNotification(description: string, plural: boolean) { +function describeNotification(description: string, plural: boolean): string { switch (description) { - case "link": - return `added ${pluralize("new link", plural)} to`; - case "comment": - return `left ${pluralize("comment", plural)} on`; - case "edit-comment": - return `updated ${pluralize("comment", plural)} on`; - case "note": - return `posted ${pluralize("note", plural)} to`; - case "edit-note": - return `updated ${pluralize("note", plural)} in`; - case "mention": - return "mentioned you on"; - case "message": - return `sent ${pluralize("message", plural)} to`; + case 'link': + return `added ${pluralize('new link', plural)} to`; + case 'comment': + return `left ${pluralize('comment', plural)} on`; + case 'edit-comment': + return `updated ${pluralize('comment', plural)} on`; + case 'note': + return `posted ${pluralize('note', plural)} to`; + case 'edit-note': + return `updated ${pluralize('note', plural)} in`; + case 'mention': + return 'mentioned you on'; + case 'message': + return `sent ${pluralize('message', plural)} to`; default: return description; } } -const GraphUrl = ({ url, title }) => ( +const GraphUrl = ({ url, title }): ReactElement => ( @@ -68,10 +66,10 @@ const GraphUrl = ({ url, title }) => ( ); -const GraphNodeContent = ({ group, post, contacts, mod, description, index, remoteContentPolicy }) => { +const GraphNodeContent = ({ group, post, contacts, mod, index }): ReactElement => { const { contents } = post; - const idx = index.slice(1).split("/"); - if (mod === "link") { + const idx = index.slice(1).split('/'); + if (mod === 'link') { if (idx.length === 1) { const [{ text }, { url }] = contents; return ; @@ -80,20 +78,20 @@ const GraphNodeContent = ({ group, post, contacts, mod, description, index, remo content={contents} contacts={contacts} group={group} - /> + />; } return null; } - if (mod === "publish") { - if (idx[1] === "2") { + if (mod === 'publish') { + if (idx[1] === '2') { return - } else if (idx[1] === "1") { + />; + } else if (idx[1] === '1') { const [{ text: header }, { text: body }] = contents; const snippet = getSnippet(body); return ( @@ -123,42 +121,41 @@ const GraphNodeContent = ({ group, post, contacts, mod, description, index, remo flexShrink={0} flexGrow={1} flexWrap="wrap" - > + > {}} group={group} contacts={contacts} groups={{}} - associations={{ graph: {}, groups: {}}} + associations={{ graph: {}, groups: {} }} msg={post} fontSize='0' pt='2' /> ); - } return null; }; -function getNodeUrl(mod: string, hidden: boolean, groupPath: string, graph: string, index: string) { +function getNodeUrl(mod: string, hidden: boolean, groupPath: string, graph: string, index: string): string { if (hidden && mod === 'chat') { groupPath = '/messages'; } else if (hidden) { groupPath = '/home'; } const graphUrl = `/~landscape${groupPath}/resource/${mod}${graph}`; - const idx = index.slice(1).split("/"); - if (mod === "publish") { + const idx = index.slice(1).split('/'); + if (mod === 'publish') { const [noteId] = idx; return `${graphUrl}/note/${noteId}`; - } else if (mod === "link") { + } else if (mod === 'link') { const [linkId] = idx; return `${graphUrl}/${linkId}`; } else if (mod === 'chat') { return graphUrl; } - return ""; + return ''; } const GraphNode = ({ post, @@ -174,9 +171,7 @@ const GraphNode = ({ read, onRead, showContact = false, - remoteContentPolicy -}) => { - const { contents } = post; +}): ReactElement => { author = deSig(author); const history = useHistory(); @@ -185,7 +180,7 @@ const GraphNode = ({ ship={`~${author}`} size={16} icon - color={`#000000`} + color={'#000000'} classes="mix-blend-diff" padding={2} /> @@ -212,12 +207,12 @@ const GraphNode = ({ alignItems="center" p="1" backgroundColor="white" - > + > {cite(author)} - {moment(time).format("HH:mm")} + {moment(time).format('HH:mm')} } @@ -249,7 +244,7 @@ export function GraphNotification(props: { }) { const { contents, index, read, time, api, timebox, groups } = props; - const authors = _.map(contents, "author"); + const authors = _.map(contents, 'author'); const { graph, group } = index; const icon = getGraphModuleIcon(index.module); const desc = describeNotification(index.description, contents.length !== 1); @@ -259,7 +254,7 @@ export function GraphNotification(props: { return; } - return api.hark["read"](timebox, { graph: index }); + return api.hark['read'](timebox, { graph: index }); }, [api, timebox, index, read]); return ( @@ -284,7 +279,7 @@ return ( author={content.author} contacts={props.contacts} mod={index.module} - time={content?.["time-sent"]} + time={content?.['time-sent']} description={index.description} index={content.index} graph={graph} diff --git a/pkg/interface/src/views/apps/notifications/group.tsx b/pkg/interface/src/views/apps/notifications/group.tsx index 2b5a66f8d..782069508 100644 --- a/pkg/interface/src/views/apps/notifications/group.tsx +++ b/pkg/interface/src/views/apps/notifications/group.tsx @@ -1,44 +1,34 @@ -import React, { ReactNode, useCallback } from "react"; -import moment from "moment"; -import { Row, Box, Col, Text, Anchor, Icon, Action } from "@tlon/indigo-react"; -import _ from "lodash"; -import { NotificationProps } from "./types"; +import React, { ReactElement, useCallback } from 'react'; +import _ from 'lodash'; + +import { Col } from '@tlon/indigo-react'; import { - Post, - GraphNotifIndex, - GraphNotificationContents, Associations, - Content, - IndexedNotification, GroupNotificationContents, GroupNotifIndex, GroupUpdate, - Rolodex, -} from "~/types"; -import { Header } from "./header"; -import { cite, deSig } from "~/logic/lib/util"; -import { Sigil } from "~/logic/lib/sigil"; -import RichText from "~/views/components/RichText"; -import GlobalApi from "~/logic/api/global"; -import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction"; + Rolodex +} from '@urbit/api'; +import { Header } from './header'; +import GlobalApi from '~/logic/api/global'; function describeNotification(description: string, plural: boolean) { switch (description) { - case "add-members": - return "joined"; - case "remove-members": - return "left"; + case 'add-members': + return 'joined'; + case 'remove-members': + return 'left'; default: return description; } } -function getGroupUpdateParticipants(update: GroupUpdate) { - if ("addMembers" in update) { +function getGroupUpdateParticipants(update: GroupUpdate): string[] { + if ('addMembers' in update) { return update.addMembers.ships; } - if ("removeMembers" in update) { + if ('removeMembers' in update) { return update.removeMembers.ships; } return []; @@ -56,7 +46,7 @@ interface GroupNotificationProps { api: GlobalApi; } -export function GroupNotification(props: GroupNotificationProps) { +export function GroupNotification(props: GroupNotificationProps): ReactElement { const { contents, index, read, time, api, timebox, associations } = props; const authors = _.flatten(_.map(contents, getGroupUpdateParticipants)); @@ -68,7 +58,7 @@ export function GroupNotification(props: GroupNotificationProps) { if (props.archived) { return; } - const func = read ? "unread" : "read"; + const func = read ? 'unread' : 'read'; return api.hark[func](timebox, { group: index }); }, [api, timebox, index, read]); diff --git a/pkg/interface/src/views/apps/notifications/header.tsx b/pkg/interface/src/views/apps/notifications/header.tsx index 7d7644b1a..528460f13 100644 --- a/pkg/interface/src/views/apps/notifications/header.tsx +++ b/pkg/interface/src/views/apps/notifications/header.tsx @@ -1,17 +1,19 @@ -import React from "react"; -import { Text as NormalText, Row, Icon, Rule, Box } from "@tlon/indigo-react"; -import f from "lodash/fp"; -import _ from "lodash"; -import moment from "moment"; -import { PropFunc } from "~/types/util"; -import { getContactDetails, useShowNickname } from "~/logic/lib/util"; -import { Associations, Contact, Contacts, Rolodex } from "~/types"; +import React, { ReactElement } from 'react'; +import f from 'lodash/fp'; +import _ from 'lodash'; +import moment from 'moment'; + +import { Text as NormalText, Row, Icon, Rule } from '@tlon/indigo-react'; +import { Associations, Contact, Contacts, Rolodex } from '@urbit/api'; + +import { PropFunc } from '~/types/util'; +import { useShowNickname } from '~/logic/lib/util'; const Text = (props: PropFunc) => ( ); -function Author(props: { patp: string; contacts: Contacts; last?: boolean }) { +function Author(props: { patp: string; contacts: Contacts; last?: boolean }): ReactElement { const contact: Contact | undefined = props.contacts?.[props.patp]; const showNickname = useShowNickname(contact); @@ -20,7 +22,7 @@ function Author(props: { patp: string; contacts: Contacts; last?: boolean }) { return ( {name} - {!props.last && ", "} + {!props.last && ', '} ); } @@ -36,7 +38,7 @@ export function Header(props: { time: number; read: boolean; associations: Associations; -} & PropFunc ) { +} & PropFunc ): ReactElement { const { description, channel, group, moduleIcon, read } = props; const contacts = props.contacts[group] || {}; @@ -50,17 +52,17 @@ export function Header(props: { const last = lent - 1 === parseInt(idx, 10); return ; }), - (auths) => ( + auths => ( {auths} {authors.length > 3 && - ` and ${authors.length - 3} other${authors.length === 4 ? "" : "s"}`} + ` and ${authors.length - 3} other${authors.length === 4 ? '' : 's'}`} ) )(authors); - const time = moment(props.time).format("HH:mm"); + const time = moment(props.time).format('HH:mm'); const groupTitle = props.associations.groups?.[props.group]?.metadata?.title; @@ -84,8 +86,8 @@ export function Header(props: { {authorDesc} {description} - {!!moduleIcon && } - {!!channel && {channelTitle}} + {Boolean(moduleIcon) && } + {Boolean(channel) && {channelTitle}} {groupTitle && <> diff --git a/pkg/interface/src/views/apps/notifications/inbox.tsx b/pkg/interface/src/views/apps/notifications/inbox.tsx index a5d5cf382..2f05b736e 100644 --- a/pkg/interface/src/views/apps/notifications/inbox.tsx +++ b/pkg/interface/src/views/apps/notifications/inbox.tsx @@ -1,22 +1,28 @@ -import React, { useEffect, useCallback, useRef, useState } from "react"; -import f from "lodash/fp"; -import _ from "lodash"; -import { Icon, Col, Center, Row, Box, Text, Anchor, Rule, LoadingSpinner } from "@tlon/indigo-react"; -import moment from "moment"; -import { Notifications, Rolodex, Timebox, IndexedNotification, Groups, joinProgress, JoinRequests, GroupNotificationsConfig, NotificationGraphConfig } from "~/types"; -import { MOMENT_CALENDAR_DATE, daToUnix, resourceAsPath } from "~/logic/lib/util"; -import { BigInteger } from "big-integer"; -import GlobalApi from "~/logic/api/global"; -import { Notification } from "./notification"; -import { Associations } from "~/types"; -import { InviteItem } from '~/views/components/Invite'; -import { useWaitForProps } from "~/logic/lib/useWaitForProps"; -import { useHistory } from "react-router-dom"; -import {useModal} from "~/logic/lib/useModal"; -import {JoinGroup} from "~/views/landscape/components/JoinGroup"; -import {JoiningStatus} from "./joining"; -import {Invites} from "./invites"; -import {useLazyScroll} from "~/logic/lib/useLazyScroll"; +import React, { useEffect, useCallback, useRef } from 'react'; +import f from 'lodash/fp'; +import _ from 'lodash'; +import moment from 'moment'; +import { BigInteger } from 'big-integer'; + +import { Col, Center, Box, Text, LoadingSpinner } from '@tlon/indigo-react'; +import { + Associations, + Notifications, + Rolodex, + Timebox, + IndexedNotification, + Groups, + JoinRequests, + GroupNotificationsConfig, + NotificationGraphConfig, + Invites as InviteType +} from '@urbit/api'; + +import { MOMENT_CALENDAR_DATE, daToUnix } from '~/logic/lib/util'; +import GlobalApi from '~/logic/api/global'; +import { Notification } from './notification'; +import { Invites } from './invites'; +import { useLazyScroll } from '~/logic/lib/useLazyScroll'; type DatedTimebox = [BigInteger, Timebox]; @@ -25,12 +31,12 @@ function filterNotification(associations: Associations, groups: string[]) { return () => true; } return (n: IndexedNotification) => { - if ("graph" in n.index) { + if ('graph' in n.index) { const { group } = n.index.graph; - return groups.findIndex((g) => group === g) !== -1; - } else if ("group" in n.index) { + return groups.findIndex(g => group === g) !== -1; + } else if ('group' in n.index) { const { group } = n.index.group; - return groups.findIndex((g) => group === g) !== -1; + return groups.findIndex(g => group === g) !== -1; } return true; }; @@ -46,7 +52,7 @@ export default function Inbox(props: { associations: Associations; contacts: Rolodex; filter: string[]; - invites: any; + invites: InviteType; pendingJoin: JoinRequests; notificationsGroupConfig: GroupNotificationsConfig; notificationsGraphConfig: NotificationGraphConfig; @@ -70,30 +76,30 @@ export default function Inbox(props: { const calendar = { ...MOMENT_CALENDAR_DATE, sameDay: function (now) { if (this.subtract(6, 'hours').isBefore(now)) { - return "[Earlier Today]"; + return '[Earlier Today]'; } else { return MOMENT_CALENDAR_DATE.sameDay; } } }; - let notificationsByDay = f.flow( + const notificationsByDay = f.flow( f.map(([date, nots]) => [ date, - nots.filter(filterNotification(associations, props.filter)), + nots.filter(filterNotification(associations, props.filter)) ]), f.groupBy(([d]) => { const date = moment(daToUnix(d)); if (moment().subtract(6, 'hours').isBefore(date)) { return 'latest'; } else { - return date.format("YYYYMMDD"); + return date.format('YYYYMMDD'); } - }), + }) )(notifications); const notificationsByDayMap = new Map( - Object.keys(notificationsByDay).map(timebox => { + Object.keys(notificationsByDay).map((timebox) => { return [timebox, notificationsByDay[timebox]]; }) ); @@ -105,13 +111,12 @@ export default function Inbox(props: { }, [api]); const { isDone, isLoading } = useLazyScroll( - scrollRef, + scrollRef, 0.2, _.flatten(notifications).length, loadMore ); - return ( @@ -123,7 +128,7 @@ export default function Inbox(props: { label={day === 'latest' ? 'Today' : moment(day).calendar(null, calendar)} timeboxes={timeboxes} contacts={props.contacts} - archive={!!props.showArchive} + archive={Boolean(props.showArchive)} associations={props.associations} api={api} groups={props.groups} @@ -142,7 +147,7 @@ export default function Inbox(props: { )} - + ); } @@ -167,9 +172,8 @@ function DaySection({ associations, api, groupConfig, - graphConfig, + graphConfig }) { - const lent = timeboxes.map(([,nots]) => nots.length).reduce(f.add, 0); if (lent === 0 || timeboxes.length === 0) { return null; diff --git a/pkg/interface/src/views/apps/notifications/invites.tsx b/pkg/interface/src/views/apps/notifications/invites.tsx index a856af68d..829a383bc 100644 --- a/pkg/interface/src/views/apps/notifications/invites.tsx +++ b/pkg/interface/src/views/apps/notifications/invites.tsx @@ -1,15 +1,12 @@ -import React, { useCallback, useState } from "react"; +import React, { ReactElement } from 'react'; import _ from 'lodash'; -import { Box, Row, Col } from "@tlon/indigo-react"; -import GlobalApi from "~/logic/api/global"; -import { Invites as IInvites, Associations, Invite, JoinRequests, Groups, Contacts, AppInvites, JoinProgress } from "~/types"; -import { resourceAsPath, alphabeticalOrder } from "~/logic/lib/util"; -import { useHistory } from "react-router-dom"; -import { useWaitForProps } from "~/logic/lib/useWaitForProps"; -import InviteItem from "~/views/components/Invite"; -import {JoiningStatus} from "./joining"; -import {useModal} from "~/logic/lib/useModal"; -import {JoinGroup} from "~/views/landscape/components/JoinGroup"; + +import { Col } from '@tlon/indigo-react'; +import { Invites as IInvites, Associations, Invite, JoinRequests, Groups, Contacts, AppInvites, JoinProgress } from '@urbit/api'; + +import GlobalApi from '~/logic/api/global'; +import { resourceAsPath, alphabeticalOrder } from '~/logic/lib/util'; +import InviteItem from '~/views/components/Invite'; interface InvitesProps { api: GlobalApi; @@ -26,50 +23,18 @@ interface InviteRef { invite: Invite; } -export function Invites(props: InvitesProps) { +export function Invites(props: InvitesProps): ReactElement { const { api, invites, pendingJoin } = props; - const [selected, setSelected] = useState<[string, string, Invite] | undefined>() - - const acceptInvite = ( - app: string, - uid: string, - invite: Invite - ) => async () => { - setSelected([app, uid, invite]); - showModal(); - }; - - const declineInvite = useCallback( - (app: string, uid: string) => () => api.invite.decline(app, uid), - [api] - ); - - const { modal, showModal } = useModal({ modal: () => { - const [app, uid, invite] = selected!; - const autojoin = `~${invite.resource.ship}/${invite.resource.name}`; - return ( - - )}}); const inviteArr: InviteRef[] = _.reduce(invites, (acc: InviteRef[], val: AppInvites, app: string) => { const appInvites = _.reduce(val, (invs: InviteRef[], invite: Invite, uid: string) => { return [...invs, { invite, uid, app }]; - }, []); return [...acc, ...appInvites]; }, []); - const invitesAndStatus: { [rid: string]: JoinProgress | InviteRef } = - {..._.keyBy(inviteArr, ({ invite }) => resourceAsPath(invite.resource)), ...props.pendingJoin }; - - + const invitesAndStatus: { [rid: string]: JoinProgress | InviteRef } = + { ..._.keyBy(inviteArr, ({ invite }) => resourceAsPath(invite.resource)), ...props.pendingJoin }; return ( { + .map((resource) => { const inviteOrStatus = invitesAndStatus[resource]; if(typeof inviteOrStatus === 'string') { return ( @@ -93,10 +58,10 @@ export function Invites(props: InvitesProps) { groups={props.groups} associations={props.associations} resource={resource} - pendingJoin={pendingJoin} - api={api} /> - ) - + pendingJoin={pendingJoin} + api={api} + /> + ); } else { const { app, uid, invite } = inviteOrStatus; console.log(inviteOrStatus); @@ -107,13 +72,13 @@ export function Invites(props: InvitesProps) { invite={invite} app={app} uid={uid} - pendingJoin={pendingJoin} + pendingJoin={pendingJoin} resource={resource} contacts={props.contacts} groups={props.groups} associations={props.associations} /> - ) + ); } })} diff --git a/pkg/interface/src/views/apps/notifications/joining.tsx b/pkg/interface/src/views/apps/notifications/joining.tsx index 7b2295fb2..576371205 100644 --- a/pkg/interface/src/views/apps/notifications/joining.tsx +++ b/pkg/interface/src/views/apps/notifications/joining.tsx @@ -1,35 +1,32 @@ -import React, { useState, useEffect } from "react"; -import { Col, Row, Text, SegmentedProgressBar, Box } from "@tlon/indigo-react"; -import GlobalApi from "~/logic/api/global"; +import React from 'react'; +import { Row, Text, SegmentedProgressBar, Box } from '@tlon/indigo-react'; import { JoinProgress, joinProgress, - MetadataUpdatePreview, - joinError, -} from "~/types"; -import { clamp } from "~/logic/lib/util"; + joinError +} from '@urbit/api'; interface JoiningStatusProps { status: JoinProgress; } const description: string[] = [ - "Attempting to contact host", - "Retrieving data", - "Finished join", - "Unable to join, you do not have the correct permissions", - "Internal error, please file an issue", + 'Attempting to contact host', + 'Retrieving data', + 'Finished join', + 'Unable to join, you do not have the correct permissions', + 'Internal error, please file an issue' ]; export function JoiningStatus(props: JoiningStatusProps) { const { status } = props; const current = joinProgress.indexOf(status); - const desc = description?.[current] || ""; + const desc = description?.[current] || ''; const isError = joinError.indexOf(status as any) !== -1; return ( - + {desc} diff --git a/pkg/interface/src/views/apps/notifications/metadata.tsx b/pkg/interface/src/views/apps/notifications/metadata.tsx index b5643f359..20d5ac0dc 100644 --- a/pkg/interface/src/views/apps/notifications/metadata.tsx +++ b/pkg/interface/src/views/apps/notifications/metadata.tsx @@ -1,8 +1,8 @@ -import React from "react"; -import { Box } from "@tlon/indigo-react"; +import React from 'react'; +import { Box } from '@tlon/indigo-react'; -import { MetadataBody, NotificationProps } from "./types"; -import { Header } from "./header"; +import { MetadataBody, NotificationProps } from './types'; +import { Header } from './header'; function getInvolvedUsers(body: MetadataBody) { return []; @@ -10,22 +10,22 @@ function getInvolvedUsers(body: MetadataBody) { function getDescription(body: MetadataBody) { const b = body.metadata; - if ("new" in b) { - return "created"; - } else if ("changedTitle" in b) { - return "changed the title to"; - } else if ("changedDescription" in b) { - return "changed the description to"; - } else if ("changedColor" in b) { - return "changed the color to"; - } else if ("deleted" in b) { - return "deleted"; + if ('new' in b) { + return 'created'; + } else if ('changedTitle' in b) { + return 'changed the title to'; + } else if ('changedDescription' in b) { + return 'changed the description to'; + } else if ('changedColor' in b) { + return 'changed the color to'; + } else if ('deleted' in b) { + return 'deleted'; } else { - throw new Error("bad metadata frond"); + throw new Error('bad metadata frond'); } } -export function MetadataNotification(props: NotificationProps<"metadata">) { +export function MetadataNotification(props: NotificationProps<'metadata'>) { const { unread } = props; const description = getDescription(unread.unreads[0].body); diff --git a/pkg/interface/src/views/apps/notifications/notification.tsx b/pkg/interface/src/views/apps/notifications/notification.tsx index cc55d3814..8c550257d 100644 --- a/pkg/interface/src/views/apps/notifications/notification.tsx +++ b/pkg/interface/src/views/apps/notifications/notification.tsx @@ -1,6 +1,6 @@ -import React, { ReactNode, useCallback, useMemo, useState } from "react"; -import { Row, Box } from "@tlon/indigo-react"; -import _ from "lodash"; +import React, { ReactNode, useCallback, useMemo, useState } from 'react'; +import { Row, Box } from '@tlon/indigo-react'; +import _ from 'lodash'; import { GraphNotificationContents, IndexedNotification, @@ -9,15 +9,15 @@ import { GroupNotificationsConfig, Groups, Associations, - Contacts, -} from "~/types"; -import GlobalApi from "~/logic/api/global"; -import { getParentIndex } from "~/logic/lib/notification"; -import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction"; -import { GroupNotification } from "./group"; -import { GraphNotification } from "./graph"; -import { BigInteger } from "big-integer"; -import { useHovering } from "~/logic/lib/util"; + Contacts +} from '@urbit/api'; +import GlobalApi from '~/logic/api/global'; +import { getParentIndex } from '~/logic/lib/notification'; +import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction'; +import { GroupNotification } from './group'; +import { GraphNotification } from './graph'; +import { BigInteger } from 'big-integer'; +import { useHovering } from '~/logic/lib/util'; interface NotificationProps { notification: IndexedNotification; @@ -37,7 +37,7 @@ function getMuted( graphs: NotificationGraphConfig ) { const { index, notification } = idxNotif; - if ("graph" in idxNotif.index) { + if ('graph' in idxNotif.index) { const { graph } = idxNotif.index.graph; if(!('graph' in notification.contents)) { throw new Error(); @@ -46,11 +46,11 @@ function getMuted( return _.findIndex( graphs?.watching || [], - (g) => g.graph === graph && g.index === parent + g => g.graph === graph && g.index === parent ) === -1; } - if ("group" in index) { - return _.findIndex(groups || [], (g) => g === index.group.group) === -1; + if ('group' in index) { + return _.findIndex(groups || [], g => g === index.group.group) === -1; } return false; } @@ -77,13 +77,13 @@ function NotificationWrapper(props: { ); const onChangeMute = useCallback(async () => { - const func = isMuted ? "unmute" : "mute"; + const func = isMuted ? 'unmute' : 'mute'; return api.hark[func](notif); }, [notif, api, isMuted]); const { hovering, bind } = useHovering(); - const changeMuteDesc = isMuted ? "Unmute" : "Mute"; + const changeMuteDesc = isMuted ? 'Unmute' : 'Mute'; return ( ); - if ("graph" in notification.index) { + if ('graph' in notification.index) { const index = notification.index.graph; const c: GraphNotificationContents = (contents as any).graph; @@ -147,7 +147,7 @@ export function Notification(props: NotificationProps) { ); } - if ("group" in notification.index) { + if ('group' in notification.index) { const index = notification.index.group; const c: GroupNotificationContents = (contents as any).group; return ( diff --git a/pkg/interface/src/views/apps/notifications/notifications.tsx b/pkg/interface/src/views/apps/notifications/notifications.tsx index 87e5764c2..3f9bd7555 100644 --- a/pkg/interface/src/views/apps/notifications/notifications.tsx +++ b/pkg/interface/src/views/apps/notifications/notifications.tsx @@ -1,25 +1,25 @@ -import React, { useCallback, useState, useRef } from "react"; +import React, { useCallback, useState, useRef, ReactElement } from 'react'; import _ from 'lodash'; -import { Box, Col, Text, Row } from "@tlon/indigo-react"; -import { Link, Switch, Route } from "react-router-dom"; -import Helmet from "react-helmet"; +import { Link, Switch, Route } from 'react-router-dom'; +import Helmet from 'react-helmet'; -import { Body } from "~/views/components/Body"; -import { PropFunc } from "~/types/util"; -import Inbox from "./inbox"; -import NotificationPreferences from "./preferences"; -import { Dropdown } from "~/views/components/Dropdown"; -import { Formik } from "formik"; -import { FormikOnBlur } from "~/views/components/FormikOnBlur"; -import GroupSearch from "~/views/components/GroupSearch"; -import {useTutorialModal} from "~/views/components/useTutorialModal"; +import { Box, Col, Text, Row } from '@tlon/indigo-react'; -const baseUrl = "/~notifications"; +import { Body } from '~/views/components/Body'; +import { PropFunc } from '~/types/util'; +import Inbox from './inbox'; +import NotificationPreferences from './preferences'; +import { Dropdown } from '~/views/components/Dropdown'; +import { FormikOnBlur } from '~/views/components/FormikOnBlur'; +import GroupSearch from '~/views/components/GroupSearch'; +import { useTutorialModal } from '~/views/components/useTutorialModal'; + +const baseUrl = '/~notifications'; const HeaderLink = React.forwardRef(( props: PropFunc & { view?: string; current: string }, ref -) => { +): ReactElement => { const { current, view, ...textProps } = props; const to = view ? `${baseUrl}/${view}` : baseUrl; const active = view ? current === view : !current; @@ -35,7 +35,7 @@ interface NotificationFilter { groups: string[]; } -export default function NotificationsScreen(props: any) { +export default function NotificationsScreen(props: any): ReactElement { const relativePath = (p: string) => baseUrl + p; const [filter, setFilter] = useState({ groups: [] }); @@ -43,20 +43,20 @@ export default function NotificationsScreen(props: any) { setFilter({ groups }); }; const onReadAll = useCallback(() => { - props.api.hark.readAll() + props.api.hark.readAll(); }, []); const groupFilterDesc = filter.groups.length === 0 - ? "All" + ? 'All' : filter.groups - .map((g) => props.associations?.groups?.[g]?.metadata?.title) - .join(", "); + .map(g => props.associations?.groups?.[g]?.metadata?.title) + .join(', '); const anchorRef = useRef(null); useTutorialModal('notifications', true, anchorRef.current); return ( { const { view } = routeProps.match.params; return ( @@ -89,7 +89,8 @@ export default function NotificationsScreen(props: any) { + justifyContent="space-between" + > - {view === "preferences" && ( + {view === 'preferences' && ( ) => { console.log(values); try { - let promises: Promise[] = []; + const promises: Promise[] = []; if (values.mentions !== graphConfig.mentions) { promises.push(api.hark.setMentions(values.mentions)); } @@ -46,7 +45,7 @@ export default function NotificationPreferences( promises.push(api.hark.setWatchOnSelf(values.watchOnSelf)); } if (values.dnd !== dnd && !_.isUndefined(values.dnd)) { - promises.push(api.hark.setDoNotDisturb(values.dnd)) + promises.push(api.hark.setDoNotDisturb(values.dnd)); } await Promise.all(promises); diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index d43cfbaad..e22f01b29 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -1,30 +1,25 @@ -import React from "react"; -import * as Yup from "yup"; +import React, { ReactElement } from 'react'; +import * as Yup from 'yup'; import _ from 'lodash'; +import { Formik } from 'formik'; +import { useHistory } from 'react-router-dom'; import { ManagedForm as Form, ManagedTextInputField as Input, ManagedCheckboxField as Checkbox, - Center, Col, - Box, Text, Row, - Button, -} from "@tlon/indigo-react"; -import { Formik, FormikHelpers } from "formik"; -import { useHistory } from "react-router-dom"; - -import { uxToHex } from "~/logic/lib/util"; -import { Sigil } from "~/logic/lib/sigil"; -import { AsyncButton } from "~/views/components/AsyncButton"; -import { ColorInput } from "~/views/components/ColorInput"; -import { ImageInput } from "~/views/components/ImageInput"; -import { MarkdownField } from "~/views/apps/publish/components/MarkdownField"; -import { resourceFromPath } from "~/logic/lib/group"; -import GroupSearch from "~/views/components/GroupSearch"; +} from '@tlon/indigo-react'; +import { uxToHex } from '~/logic/lib/util'; +import { AsyncButton } from '~/views/components/AsyncButton'; +import { ColorInput } from '~/views/components/ColorInput'; +import { ImageInput } from '~/views/components/ImageInput'; +import { MarkdownField } from '~/views/apps/publish/components/MarkdownField'; +import { resourceFromPath } from '~/logic/lib/group'; +import GroupSearch from '~/views/components/GroupSearch'; const formSchema = Yup.object({ nickname: Yup.string(), @@ -45,8 +40,7 @@ const emptyContact = { isPublic: false }; - -export function EditProfile(props: any) { +export function EditProfile(props: any): ReactElement { const { contact, ship, api, isPublic } = props; const history = useHistory(); if (contact) { @@ -58,10 +52,10 @@ export function EditProfile(props: any) { try { await Object.keys(values).reduce((acc, key) => { console.log(key); - const newValue = key !== "color" ? values[key] : uxToHex(values[key]); + const newValue = key !== 'color' ? values[key] : uxToHex(values[key]); if (newValue !== contact[key]) { - if (key === "isPublic") { + if (key === 'isPublic') { return acc.then(() => api.contacts.setPublic(newValue) ); @@ -70,23 +64,22 @@ export function EditProfile(props: any) { console.log(toRemove); const toAdd: string[] = _.difference(newValue, contact?.groups || []); console.log(toAdd); - let promises: Promise[] = []; + const promises: Promise[] = []; promises.concat( toRemove.map(e => - api.contacts.edit(ship, {'remove-group': resourceFromPath(e) }) + api.contacts.edit(ship, { 'remove-group': resourceFromPath(e) }) ) ); promises.concat( toAdd.map(e => - api.contacts.edit(ship, {'add-group': resourceFromPath(e) }) + api.contacts.edit(ship, { 'add-group': resourceFromPath(e) }) ) ); return acc.then(() => Promise.all(promises)); - } else if ( - key !== "last-updated" && - key !== "isPublic" + key !== 'last-updated' && + key !== 'isPublic' ) { return acc.then(() => api.contacts.edit(ship, { [key]: newValue }) @@ -95,7 +88,7 @@ export function EditProfile(props: any) { } return acc; }, Promise.resolve()); - //actions.setStatus({ success: null }); + // actions.setStatus({ success: null }); history.push(`/~profile/${ship}`); } catch (e) { console.error(e); diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 77b3bc0d1..eccf1ce72 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -1,25 +1,23 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Sigil } from "~/logic/lib/sigil"; -import { ViewProfile } from './ViewProfile'; -import { EditProfile } from './EditProfile'; -import { SetStatusBarModal } from '~/views/components/SetStatusBarModal'; +import React, { ReactElement, useEffect, useRef, useState } from 'react'; +import { useHistory } from 'react-router-dom'; -import { uxToHex } from "~/logic/lib/util"; import { Center, Box, Row, BaseImage, - StatelessTextInput as Input, - Button, Text -} from "@tlon/indigo-react"; -import useLocalState from "~/logic/state/local"; -import { useHistory } from "react-router-dom"; -import {useTutorialModal} from "~/views/components/useTutorialModal"; +} from '@tlon/indigo-react'; +import { Sigil } from '~/logic/lib/sigil'; +import { ViewProfile } from './ViewProfile'; +import { EditProfile } from './EditProfile'; +import { SetStatusBarModal } from '~/views/components/SetStatusBarModal'; +import { uxToHex } from '~/logic/lib/util'; +import useLocalState from '~/logic/state/local'; +import { useTutorialModal } from '~/views/components/useTutorialModal'; -export function Profile(props: any) { +export function Profile(props: any): ReactElement { const { hideAvatars } = useLocalState(({ hideAvatars }) => ({ hideAvatars })); @@ -31,17 +29,13 @@ export function Profile(props: any) { const { contact, nackedContacts, hasLoaded, isPublic, isEdit, ship } = props; const nacked = nackedContacts.has(ship); - const [statusModal, showStatusModal] = useState(false); - - useEffect(() => { if(hasLoaded && !contact && !nacked) { props.api.contacts.retrieve(ship); } - }, [hasLoaded, contact]) + }, [hasLoaded, contact]); - - const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : "#000000"; + const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : '#000000'; const cover = (contact?.cover) ? : ; @@ -58,19 +52,24 @@ export function Profile(props: any) {
+ width="100%" + > + width="100%" + > {ship === `~${window.ship}` ? ( { history.push(`/~profile/${ship}/edit`) }}> + onClick={() => { + history.push(`/~profile/${ship}/edit`); +}} + > Edit Profile ) : null} - {contact?.status ?? ""} + overflow="hidden" display="inline-block" +verticalAlign="middle" + >{contact?.status ?? ''} {cover} @@ -108,7 +110,8 @@ export function Profile(props: any) { api={props.api} groups={props.groups} associations={props.associations} - isPublic={isPublic}/> + isPublic={isPublic} + /> ) : ( { - setStatus(!!contact ? contact.status : ''); + setStatus(contact ? contact.status : ''); }, [contact]); const editStatus = () => { - api.contacts.edit(ship, {status: _status}); + api.contacts.edit(ship, { status: _status }); if (callback) { callback(); @@ -53,7 +52,8 @@ export function SetStatus(props: any) { color="white" ml={2} width="25%" - onClick={editStatus}> + onClick={editStatus} + > Set Status diff --git a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx index cd639b135..939d8aab7 100644 --- a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx @@ -1,28 +1,21 @@ -import React, {useEffect, useState} from "react"; +import React from 'react'; import _ from 'lodash'; -import { Sigil } from "~/logic/lib/sigil"; +import { useHistory } from 'react-router-dom'; import { Center, Box, Text, Row, - Button, Col, - LoadingSpinner -} from "@tlon/indigo-react"; -import { AsyncButton } from "~/views/components/AsyncButton"; -import RichText from "~/views/components/RichText"; -import { useHistory } from "react-router-dom"; -import {GroupSummary} from "~/views/landscape/components/GroupSummary"; -import {MetadataUpdatePreview} from "~/types"; -import {GroupLink} from "~/views/components/GroupLink"; -import {lengthOrder} from "~/logic/lib/util"; -import useLocalState from "~/logic/state/local"; +} from '@tlon/indigo-react'; +import RichText from '~/views/components/RichText'; +import { GroupLink } from '~/views/components/GroupLink'; +import { lengthOrder } from '~/logic/lib/util'; +import useLocalState from '~/logic/state/local'; -export function ViewProfile(props: any) { - const history = useHistory(); +export function ViewProfile(props: any): ReactElement { const { hideNicknames } = useLocalState(({ hideNicknames }) => ({ hideNicknames })); @@ -33,17 +26,19 @@ export function ViewProfile(props: any) { + width="100%" + >
- {((!hideNicknames && contact?.nickname) ? contact.nickname : "")} + {((!hideNicknames && contact?.nickname) ? contact.nickname : '')}
+ width="100%" + >
{ship}
@@ -52,10 +47,11 @@ export function ViewProfile(props: any) { pb={2} alignItems="center" justifyContent="center" - width="100%"> + width="100%" + >
- {(contact?.bio ? contact.bio : "")} + {(contact?.bio ? contact.bio : '')}
@@ -82,7 +78,8 @@ export function ViewProfile(props: any) { borderRadius={1} bg="white" border={1} - borderColor="washedGray"> + borderColor="washedGray" + >
{ship} remains private diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index 977a0975e..ea99e7ece 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -1,34 +1,24 @@ -import React from "react"; -import { Route, Link } from "react-router-dom"; +import React from 'react'; +import { Route, Link } from 'react-router-dom'; import Helmet from 'react-helmet'; -import { Box, Text, Row, Col, Icon, BaseImage } from "@tlon/indigo-react"; +import { Box } from '@tlon/indigo-react'; -import { uxToHex } from "~/logic/lib/util"; - -import { Profile } from "./components/Profile"; -import useLocalState from "~/logic/state/local"; +import { Profile } from './components/Profile'; export default function ProfileScreen(props: any) { - const { dark } = props; - const hideAvatars = useLocalState(state => state.hideAvatars); return ( <> { props.notificationsCount ? `(${String(props.notificationsCount) }) `: '' }Landscape - Profile { + path={'/~profile/:ship/:edit?'} + render={({ match }) => { const ship = match.params.ship; const isEdit = match.url.includes('edit'); const isPublic = props.isContactPublic; const contact = props.contacts?.[ship]; - const sigilColor = contact?.color - ? `#${uxToHex(contact.color)}` - : dark - ? "#FFFFFF" - : "#000000"; return ( diff --git a/pkg/interface/src/views/apps/publish/PublishResource.tsx b/pkg/interface/src/views/apps/publish/PublishResource.tsx index cd9f077c1..38aa2a07d 100644 --- a/pkg/interface/src/views/apps/publish/PublishResource.tsx +++ b/pkg/interface/src/views/apps/publish/PublishResource.tsx @@ -1,11 +1,11 @@ -import React from "react"; -import { Box } from "@tlon/indigo-react"; +import React from 'react'; +import { Box } from '@tlon/indigo-react'; -import GlobalApi from "~/logic/api/global"; -import { StoreState } from "~/logic/store/type"; -import { Association } from "~/types"; -import { RouteComponentProps } from "react-router-dom"; -import { NotebookRoutes } from "./components/NotebookRoutes"; +import GlobalApi from '~/logic/api/global'; +import { StoreState } from '~/logic/store/type'; +import { Association } from '@urbit/api'; +import { RouteComponentProps } from 'react-router-dom'; +import { NotebookRoutes } from './components/NotebookRoutes'; type PublishResourceProps = StoreState & { association: Association; @@ -16,7 +16,7 @@ type PublishResourceProps = StoreState & { export function PublishResource(props: PublishResourceProps) { const { association, api, baseUrl, notebooks } = props; const rid = association.resource; - const [, , ship, book] = rid.split("/"); + const [, , ship, book] = rid.split('/'); const notebookContacts = props.contacts[association.group]; return ( diff --git a/pkg/interface/src/views/apps/publish/components/EditPost.tsx b/pkg/interface/src/views/apps/publish/components/EditPost.tsx index 0f6ed4dcc..cb7ab51cf 100644 --- a/pkg/interface/src/views/apps/publish/components/EditPost.tsx +++ b/pkg/interface/src/views/apps/publish/components/EditPost.tsx @@ -1,12 +1,16 @@ -import React from "react"; +import React, { ReactElement } from 'react'; import _ from 'lodash'; -import { PostFormSchema, PostForm } from "./NoteForm"; -import { FormikHelpers } from "formik"; -import GlobalApi from "~/logic/api/global"; -import { RouteComponentProps, useLocation } from "react-router-dom"; -import { GraphNode, TextContent, Association, S3State } from "~/types"; -import { getLatestRevision, editPost } from "~/logic/lib/publish"; -import {useWaitForProps} from "~/logic/lib/useWaitForProps"; +import { FormikHelpers } from 'formik'; +import { RouteComponentProps, useLocation } from 'react-router-dom'; + +import { GraphNode } from '@urbit/api'; + +import { PostFormSchema, PostForm } from './NoteForm'; +import GlobalApi from '~/logic/api/global'; +import { getLatestRevision, editPost } from '~/logic/lib/publish'; +import { useWaitForProps } from '~/logic/lib/useWaitForProps'; +import { S3State } from '~/types'; + interface EditPostProps { ship: string; noteId: number; @@ -16,7 +20,7 @@ interface EditPostProps { s3: S3State; } -export function EditPost(props: EditPostProps & RouteComponentProps) { +export function EditPost(props: EditPostProps & RouteComponentProps): ReactElement { const { note, book, noteId, api, ship, history, s3 } = props; const [revNum, title, body] = getLatestRevision(note); const location = useLocation(); @@ -24,19 +28,19 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { const waiter = useWaitForProps(props); const initial: PostFormSchema = { title, - body, + body }; const onSubmit = async ( values: PostFormSchema, actions: FormikHelpers - ) => { + ): Promise => { const { title, body } = values; try { const newRev = revNum + 1; const nodes = editPost(newRev, noteId, title, body); await api.graph.addNodes(ship, book, nodes); - await waiter(p => { + await waiter((p) => { const [rev] = getLatestRevision(p.note); return rev === newRev; }); @@ -44,7 +48,7 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { history.push(noteUrl); } catch (e) { console.error(e); - actions.setStatus({ error: "Failed to edit notebook" }); + actions.setStatus({ error: 'Failed to edit notebook' }); } }; diff --git a/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx b/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx index 32f372f5d..4f917084f 100644 --- a/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx +++ b/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx @@ -1,26 +1,26 @@ -import React, { createRef, useCallback, useRef } from "react"; -import { IUnControlledCodeMirror, UnControlled as CodeEditor } from "react-codemirror2"; +import React, { createRef, useCallback, useRef } from 'react'; +import { IUnControlledCodeMirror, UnControlled as CodeEditor } from 'react-codemirror2'; import { useFormikContext } from 'formik'; import { Prompt } from 'react-router-dom'; import { Editor } from 'codemirror'; -import { MOBILE_BROWSER_REGEX, usePreventWindowUnload } from "~/logic/lib/util"; -import { PropFunc } from "~/types/util"; -import CodeMirror from "codemirror"; +import { MOBILE_BROWSER_REGEX, usePreventWindowUnload } from '~/logic/lib/util'; +import { PropFunc } from '~/types/util'; +import CodeMirror from 'codemirror'; -import "codemirror/mode/markdown/markdown"; -import "codemirror/addon/display/placeholder"; -import "codemirror/addon/edit/continuelist"; +import 'codemirror/mode/markdown/markdown'; +import 'codemirror/addon/display/placeholder'; +import 'codemirror/addon/edit/continuelist'; -import "codemirror/lib/codemirror.css"; -import { Box } from "@tlon/indigo-react"; -import { useFileDrag } from "~/logic/lib/useDrag"; -import SubmitDragger from "~/views/components/SubmitDragger"; -import useS3 from "~/logic/lib/useS3"; -import { S3State } from "~/types"; +import 'codemirror/lib/codemirror.css'; +import { Box } from '@tlon/indigo-react'; +import { useFileDrag } from '~/logic/lib/useDrag'; +import SubmitDragger from '~/views/components/SubmitDragger'; +import useS3 from '~/logic/lib/useS3'; +import { S3State } from '@urbit/api'; const MARKDOWN_CONFIG = { - name: "markdown", + name: 'markdown' }; interface MarkdownEditorProps { @@ -49,12 +49,12 @@ export function MarkdownEditor( const options = { mode: MARKDOWN_CONFIG, - theme: "tlon", + theme: 'tlon', lineNumbers: false, lineWrapping: true, - scrollbarStyle: "native", + scrollbarStyle: 'native', // cursorHeight: 0.85, - placeholder: placeholder || "", + placeholder: placeholder || '', extraKeys: { 'Enter': 'newlineAndIndentContinueMarkdownList' } }; @@ -84,7 +84,7 @@ export function MarkdownEditor( const codeMirror: Editor = editor.current.editor; const doc = codeMirror.getDoc(); - Array.from(files).forEach(async file => { + Array.from(files).forEach(async (file) => { const placeholder = `![Uploading ${file.name}](...)`; doc.setValue(doc.getValue() + placeholder); const url = await uploadDefault(file); diff --git a/pkg/interface/src/views/apps/publish/components/MarkdownField.tsx b/pkg/interface/src/views/apps/publish/components/MarkdownField.tsx index 750a64978..e96aec733 100644 --- a/pkg/interface/src/views/apps/publish/components/MarkdownField.tsx +++ b/pkg/interface/src/views/apps/publish/components/MarkdownField.tsx @@ -1,8 +1,8 @@ -import React, { useCallback } from "react"; -import _ from "lodash"; -import { Box, ErrorLabel } from "@tlon/indigo-react"; -import { useField } from "formik"; -import { MarkdownEditor } from "./MarkdownEditor"; +import React, { useCallback } from 'react'; +import _ from 'lodash'; +import { Box, ErrorLabel } from '@tlon/indigo-react'; +import { useField } from 'formik'; +import { MarkdownEditor } from './MarkdownEditor'; export const MarkdownField = ({ id, @@ -13,13 +13,13 @@ export const MarkdownField = ({ const handleBlur = useCallback( (e: any) => { - _.set(e, "target.id", id); + _.set(e, 'target.id', id); onBlur && onBlur(e); }, [onBlur, id] ); - const hasError = !!(error && touched); + const hasError = Boolean(error && touched); return ( - + {error} diff --git a/pkg/interface/src/views/apps/publish/components/MetadataForm.tsx b/pkg/interface/src/views/apps/publish/components/MetadataForm.tsx index c586321a1..7a3bb39ae 100644 --- a/pkg/interface/src/views/apps/publish/components/MetadataForm.tsx +++ b/pkg/interface/src/views/apps/publish/components/MetadataForm.tsx @@ -12,10 +12,10 @@ import { import { Formik, Form, useFormikContext, FormikHelpers } from "formik"; import GlobalApi from "~/logic/api/global"; import { Notebook } from "~/types/publish-update"; -import { Contacts } from "~/types/contact-update"; +import { Contacts } from "@urbit/api/contacts"; import { FormError } from "~/views/components/FormError"; import { RouteComponentProps, useHistory } from "react-router-dom"; -import {Association} from "~/types"; +import {Association} from "@urbit/api"; import { uxToHex } from "~/logic/lib/util"; interface MetadataFormProps { diff --git a/pkg/interface/src/views/apps/publish/components/Note.tsx b/pkg/interface/src/views/apps/publish/components/Note.tsx index d336650fa..2bb8e77e0 100644 --- a/pkg/interface/src/views/apps/publish/components/Note.tsx +++ b/pkg/interface/src/views/apps/publish/components/Note.tsx @@ -1,16 +1,16 @@ -import React, { useState, useEffect } from "react"; -import { Box, Text, Col } from "@tlon/indigo-react"; -import ReactMarkdown from "react-markdown"; +import React, { useState, useEffect } from 'react'; +import { Box, Text, Col } from '@tlon/indigo-react'; +import ReactMarkdown from 'react-markdown'; import bigInt from 'big-integer'; -import { Link, RouteComponentProps } from "react-router-dom"; -import { Spinner } from "~/views/components/Spinner"; -import { Comments } from "~/views/components/Comments"; -import { NoteNavigation } from "./NoteNavigation"; -import GlobalApi from "~/logic/api/global"; +import { Link, RouteComponentProps } from 'react-router-dom'; +import { Spinner } from '~/views/components/Spinner'; +import { Comments } from '~/views/components/Comments'; +import { NoteNavigation } from './NoteNavigation'; +import GlobalApi from '~/logic/api/global'; import { getLatestRevision, getComments } from '~/logic/lib/publish'; -import Author from "~/views/components/Author"; -import { Contacts, GraphNode, Graph, Association, Unreads, Group } from "~/types"; +import Author from '~/views/components/Author'; +import { Contacts, GraphNode, Graph, Association, Unreads, Group } from '@urbit/api'; interface NoteProps { ship: string; @@ -34,12 +34,11 @@ export function Note(props: NoteProps & RouteComponentProps) { const deletePost = async () => { setDeleting(true); - const indices = [note.post.index] + const indices = [note.post.index]; await api.graph.removeNodes(ship, book, indices); props.history.push(rootUrl); }; - const comments = getComments(note); const [revNum, title, body, post] = getLatestRevision(note); const index = note.post.index.split('/'); @@ -49,8 +48,6 @@ export function Note(props: NoteProps & RouteComponentProps) { api.hark.markEachAsRead(props.association, '/',`/${index[1]}/1/1`, 'note', 'publish'); }, [props.association, props.note]); - - let adminLinks: JSX.Element | null = null; if (window.ship === note?.post?.author) { adminLinks = ( @@ -67,7 +64,7 @@ export function Note(props: NoteProps & RouteComponentProps) { color="red" ml={2} onClick={deletePost} - style={{ cursor: "pointer" }} + style={{ cursor: 'pointer' }} > Delete @@ -96,21 +93,21 @@ export function Note(props: NoteProps & RouteComponentProps) { ref={windowRef} > - {"<- Notebook Index"} + {'<- Notebook Index'} - {title || ""} + {title || ''} {adminLinks} - - + + { history.goBack(); }} - type="button">Cancel} + type="button" + >Cancel} diff --git a/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx b/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx index b3633df3f..0a0d345fb 100644 --- a/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx +++ b/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx @@ -1,29 +1,31 @@ -import React, { Component } from "react"; -import moment from "moment"; -import { Box } from "@tlon/indigo-react"; -import { Link } from "react-router-dom"; -import { Graph, GraphNode } from "~/types"; -import { getLatestRevision } from "~/logic/lib/publish"; -import { BigInteger } from "big-integer"; +import React, { ReactElement } from 'react'; +import moment from 'moment'; +import { Link } from 'react-router-dom'; +import { BigInteger } from 'big-integer'; + +import { Box } from '@tlon/indigo-react'; +import { Graph } from '@urbit/api'; + +import { getLatestRevision } from '~/logic/lib/publish'; function NavigationItem(props: { url: string; title: string; date: number; prev?: boolean; -}) { +}): ReactElement { const date = moment(props.date).fromNow(); return ( - {props.prev ? "Previous" : "Next"} + {props.prev ? 'Previous' : 'Next'} {props.title} {date} @@ -53,7 +55,7 @@ interface NoteNavigationProps { baseUrl: string; } -export function NoteNavigation(props: NoteNavigationProps) { +export function NoteNavigation(props: NoteNavigationProps): ReactElement { let nextComponent = ; let prevComponent = ; const { noteId, notebook } = props; @@ -72,13 +74,13 @@ export function NoteNavigation(props: NoteNavigationProps) { if (next && nextId) { const nextUrl = makeNoteUrl(nextId); const [, title, , post] = getLatestRevision(next); - const date = post["time-sent"]; + const date = post['time-sent']; nextComponent = ; } if (prev && prevId) { const prevUrl = makeNoteUrl(prevId); const [, title, , post] = getLatestRevision(prev); - const date = post["time-sent"]; + const date = post['time-sent']; prevComponent = ( ); diff --git a/pkg/interface/src/views/apps/publish/components/NotePreview.tsx b/pkg/interface/src/views/apps/publish/components/NotePreview.tsx index 1820188c4..becefc7e3 100644 --- a/pkg/interface/src/views/apps/publish/components/NotePreview.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotePreview.tsx @@ -4,14 +4,14 @@ import styled from 'styled-components'; import { Col, Row, Box, Text, Icon, Image } from '@tlon/indigo-react'; import Author from '~/views/components/Author'; -import { GraphNode } from '~/types/graph-update'; -import { Contacts, Group } from '~/types'; +import { GraphNode } from '@urbit/api/graph'; +import { Contacts, Group } from '@urbit/api'; import { getComments, getLatestRevision, - getSnippet, -} from "~/logic/lib/publish"; -import {Unreads} from "~/types"; + getSnippet +} from '~/logic/lib/publish'; +import { Unreads } from '@urbit/api'; import GlobalApi from '~/logic/api/global'; import ReactMarkdown from 'react-markdown'; diff --git a/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx b/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx index 916347038..9ac272b83 100644 --- a/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx @@ -1,12 +1,12 @@ -import React from "react"; -import { Route, Switch } from "react-router-dom"; +import React from 'react'; +import { Route, Switch } from 'react-router-dom'; -import GlobalApi from "~/logic/api/global"; -import { RouteComponentProps } from "react-router-dom"; -import Note from "./Note"; -import { EditPost } from "./EditPost"; +import GlobalApi from '~/logic/api/global'; +import { RouteComponentProps } from 'react-router-dom'; +import Note from './Note'; +import { EditPost } from './EditPost'; -import { GraphNode, Graph, Contacts, Association, S3State, Group } from "~/types"; +import { GraphNode, Graph, Contacts, Association, S3State, Group } from '@urbit/api'; interface NoteRoutesProps { ship: string; @@ -32,17 +32,18 @@ export function NoteRoutes(props: NoteRoutesProps & RouteComponentProps) { } + path={relativePath('/edit')} + render={routeProps => } /> + path={relativePath('/:commentId?')} + render={routeProps => + rootUrl={rootUrl} + /> } /> diff --git a/pkg/interface/src/views/apps/publish/components/Notebook.tsx b/pkg/interface/src/views/apps/publish/components/Notebook.tsx index 206349723..f689297c7 100644 --- a/pkg/interface/src/views/apps/publish/components/Notebook.tsx +++ b/pkg/interface/src/views/apps/publish/components/Notebook.tsx @@ -1,10 +1,12 @@ -import React from "react"; -import { RouteComponentProps, Link } from "react-router-dom"; -import { NotebookPosts } from "./NotebookPosts"; -import { Col, Box, Text, Button, Row } from "@tlon/indigo-react"; -import GlobalApi from "~/logic/api/global"; -import { Contacts, Rolodex, Groups, Associations, Graph, Association, Unreads } from "~/types"; -import { useShowNickname } from "~/logic/lib/util"; +import React, { ReactElement } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; + +import { Col, Box, Text, Row } from '@tlon/indigo-react'; +import { Contacts, Rolodex, Groups, Associations, Graph, Association, Unreads } from '@urbit/api'; + +import { NotebookPosts } from './NotebookPosts'; +import GlobalApi from '~/logic/api/global'; +import { useShowNickname } from '~/logic/lib/util'; interface NotebookProps { api: GlobalApi; @@ -21,7 +23,7 @@ interface NotebookProps { unreads: Unreads; } -export function Notebook(props: NotebookProps & RouteComponentProps) { +export function Notebook(props: NotebookProps & RouteComponentProps): ReactElement { const { ship, book, @@ -36,7 +38,6 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { return null; // Waiting on groups to populate } - const relativePath = (p: string) => props.baseUrl + p; const contact = notebookContacts?.[ship]; diff --git a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx index 639cf5330..8cd94f68b 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx @@ -1,7 +1,7 @@ -import React, { Component } from "react"; -import { Col } from "@tlon/indigo-react"; -import { NotePreview } from "./NotePreview"; -import { Contacts, Graph, Unreads, Group } from "~/types"; +import React, { Component } from 'react'; +import { Col } from '@tlon/indigo-react'; +import { NotePreview } from './NotePreview'; +import { Contacts, Graph, Unreads, Group } from '@urbit/api'; interface NotebookPostsProps { contacts: Contacts; diff --git a/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx b/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx index a3d1f5474..a4c8e1e7e 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx @@ -1,6 +1,6 @@ -import React, { useEffect } from "react"; -import { RouteComponentProps, Route, Switch } from "react-router-dom"; -import GlobalApi from "~/logic/api/global"; +import React, { useEffect } from 'react'; +import { RouteComponentProps, Route, Switch } from 'react-router-dom'; +import GlobalApi from '~/logic/api/global'; import { Association, Associations, @@ -10,16 +10,14 @@ import { Rolodex, Unreads, S3State -} from "~/types"; -import { Center, LoadingSpinner } from "@tlon/indigo-react"; +} from '@urbit/api'; +import { Center, LoadingSpinner } from '@tlon/indigo-react'; import bigInt from 'big-integer'; -import Notebook from "./Notebook"; -import NewPost from "./new-post"; +import Notebook from './Notebook'; +import NewPost from './new-post'; import { NoteRoutes } from './NoteRoutes'; - - interface NotebookRoutesProps { api: GlobalApi; ship: string; @@ -49,7 +47,6 @@ export function NotebookRoutes( const group = groups?.[props.association?.group]; - const relativePath = (path: string) => `${baseUrl}${path}`; return ( @@ -66,12 +63,13 @@ export function NotebookRoutes( contacts={notebookContacts} association={props.association} rootUrl={rootUrl} - baseUrl={baseUrl} />; + baseUrl={baseUrl} + />; }} /> ( + path={relativePath('/new')} + render={routeProps => ( { const { noteId } = routeProps.match.params; const noteIdNum = bigInt(noteId); diff --git a/pkg/interface/src/views/apps/publish/components/Writers.js b/pkg/interface/src/views/apps/publish/components/Writers.js index b6be6c159..e8ff547af 100644 --- a/pkg/interface/src/views/apps/publish/components/Writers.js +++ b/pkg/interface/src/views/apps/publish/components/Writers.js @@ -1,16 +1,14 @@ import React, { Component } from 'react'; import { Box, Text } from '@tlon/indigo-react'; import { ShipSearch } from '~/views/components/ShipSearch'; -import { Formik, Form, FormikHelpers } from 'formik'; +import { Formik, Form } from 'formik'; import { resourceFromPath } from '~/logic/lib/group'; import { AsyncButton } from '~/views/components/AsyncButton'; -import { cite } from '~/logic/lib/util'; export class Writers extends Component { render() { const { association, groups, contacts, api } = this.props; - const [,,,name] = association?.resource.split('/'); const resource = resourceFromPath(association?.group); const onSubmit = async (values, actions) => { diff --git a/pkg/interface/src/views/apps/publish/components/new-post.tsx b/pkg/interface/src/views/apps/publish/components/new-post.tsx index 2b0dd366a..3dea28bfb 100644 --- a/pkg/interface/src/views/apps/publish/components/new-post.tsx +++ b/pkg/interface/src/views/apps/publish/components/new-post.tsx @@ -1,13 +1,13 @@ -import React from "react"; -import { FormikHelpers } from "formik"; -import GlobalApi from "~/logic/api/global"; -import { useWaitForProps } from "~/logic/lib/useWaitForProps"; -import { RouteComponentProps } from "react-router-dom"; -import { PostForm, PostFormSchema } from "./NoteForm"; -import {createPost} from "~/logic/api/graph"; -import {Graph} from "~/types/graph-update"; -import {Association, S3State} from "~/types"; -import {newPost} from "~/logic/lib/publish"; +import React from 'react'; +import { FormikHelpers } from 'formik'; +import GlobalApi from '~/logic/api/global'; +import { useWaitForProps } from '~/logic/lib/useWaitForProps'; +import { RouteComponentProps } from 'react-router-dom'; +import { PostForm, PostFormSchema } from './NoteForm'; +import { createPost } from '~/logic/api/graph'; +import { Graph } from '@urbit/api/graph'; +import { Association, S3State } from '@urbit/api'; +import { newPost } from '~/logic/lib/publish'; interface NewPostProps { api: GlobalApi; @@ -30,21 +30,21 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) { ) => { const { title, body } = values; try { - const [noteId, nodes] = newPost(title, body) - await api.graph.addNodes(ship, book, nodes) - await waiter(p => + const [noteId, nodes] = newPost(title, body); + await api.graph.addNodes(ship, book, nodes); + await waiter(p => p.graph.has(noteId) && !p.graph.get(noteId)?.post?.pending ); history.push(`${props.baseUrl}/note/${noteId}`); } catch (e) { console.error(e); - actions.setStatus({ error: "Posting note failed" }); + actions.setStatus({ error: 'Posting note failed' }); } }; const initialValues: PostFormSchema = { - title: "", - body: "", + title: '', + body: '' }; return ( diff --git a/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx b/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx index 53aed1b3e..deae6d478 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx @@ -1,32 +1,30 @@ -import React from "react"; +import React, { ReactElement } from 'react'; + import { - Box, Row, Label, Col, ManagedRadioButtonField as Radio, - ManagedTextInputField as Input, -} from "@tlon/indigo-react"; +} from '@tlon/indigo-react'; -import GlobalApi from "~/logic/api/global"; -import { S3State } from "~/types"; -import { ImageInput } from "~/views/components/ImageInput"; -import {ColorInput} from "~/views/components/ColorInput"; +import GlobalApi from '~/logic/api/global'; +import { ImageInput } from '~/views/components/ImageInput'; +import { ColorInput } from '~/views/components/ColorInput'; +import { S3State } from '~/types/s3-update'; -export type BgType = "none" | "url" | "color"; +export type BgType = 'none' | 'url' | 'color'; export function BackgroundPicker({ bgType, bgUrl, api, - s3, + s3 }: { bgType: BgType; bgUrl?: string; api: GlobalApi; s3: S3State; -}) { - +}): ReactElement { const rowSpace = { my: 0, alignItems: 'center' }; const radioProps = { my: 4, mr: 4, name: 'bgType' }; return ( @@ -34,7 +32,7 @@ export function BackgroundPicker({ - {bgType === "url" && ( + {bgType === 'url' && ( )} - {bgType === "color" && ( - + {bgType === 'color' && ( + )} diff --git a/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx b/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx index 5ffdc270a..931f230b0 100644 --- a/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx +++ b/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx @@ -1,30 +1,29 @@ -import React, { useCallback } from "react"; +import React, { ReactElement, useCallback } from 'react'; +import { Formik } from 'formik'; import { ManagedTextInputField as Input, ManagedForm as Form, Box, Button, - Col, Text, Menu, MenuButton, MenuList, - MenuItem, -} from "@tlon/indigo-react"; -import { Formik } from "formik"; + MenuItem +} from '@tlon/indigo-react'; -import GlobalApi from "~/logic/api/global"; +import GlobalApi from '~/logic/api/global'; export function BucketList({ buckets, selected, - api, + api }: { buckets: Set; selected: string; api: GlobalApi; -}) { +}): ReactElement { const _buckets = Array.from(buckets); const onSubmit = useCallback( @@ -53,14 +52,14 @@ export function BucketList({ ); return ( - +
- {_buckets.map((bucket) => ( + {_buckets.map(bucket => ( { - setRemoteContentPolicy(state => { + setRemoteContentPolicy((state) => { Object.assign(state.remoteContentPolicy, values); }); actions.setSubmitting(false); }} > - {(props) => ( + {props => ( state.hideAvatars); +export default function SettingsScreen(props: any): ReactElement { return ( <> Landscape - Settings { + path={['/~settings']} + render={() => { return ( + borderRadius={1} + > ({ children, @@ -15,11 +15,11 @@ export function AsyncButton({ useEffect(() => { const s = status || {}; let done = false; - if ("success" in s) { + if ('success' in s) { setSuccess(true); onSuccess(); done = true; - } else if ("error" in s) { + } else if ('error' in s) { setSuccess(false); done = true; } @@ -40,13 +40,13 @@ export function AsyncButton({ > {isSubmitting ? ( ) : success === true ? ( - "Done" + 'Done' ) : success === false ? ( - "Errored" + 'Errored' ) : ( children )} diff --git a/pkg/interface/src/views/components/Author.tsx b/pkg/interface/src/views/components/Author.tsx index 8428121e4..1a3a8b56c 100644 --- a/pkg/interface/src/views/components/Author.tsx +++ b/pkg/interface/src/views/components/Author.tsx @@ -1,13 +1,15 @@ -import React, { ReactNode, useState, useRef } from 'react'; +import React, { ReactElement, ReactNode, useState } from 'react'; import moment from 'moment'; +import { useHistory } from 'react-router-dom'; + import { Row, Box, BaseImage } from '@tlon/indigo-react'; +import { Contacts } from '@urbit/api/contacts'; +import { Group } from '@urbit/api'; + import { uxToHex, cite, useShowNickname } from '~/logic/lib/util'; -import { Contacts } from '~/types/contact-update'; import OverlaySigil from './OverlaySigil'; import { Sigil } from '~/logic/lib/sigil'; -import { Group } from '~/types'; import GlobalApi from '~/logic/api/global'; -import { useHistory } from 'react-router-dom'; interface AuthorProps { contacts: Contacts; @@ -21,7 +23,7 @@ interface AuthorProps { } // eslint-disable-next-line max-lines-per-function -export default function Author(props: AuthorProps) { +export default function Author(props: AuthorProps): ReactElement { const { contacts, ship = '', date, showImage, group } = props; const history = useHistory(); let contact; @@ -36,7 +38,7 @@ export default function Author(props: AuthorProps) { const [showOverlay, setShowOverlay] = useState(false); const toggleOverlay = () => { - setShowOverlay((value) => !value); + setShowOverlay(value => !value); }; const img = diff --git a/pkg/interface/src/views/components/Body.tsx b/pkg/interface/src/views/components/Body.tsx index 7449689fd..4e2e64203 100644 --- a/pkg/interface/src/views/components/Body.tsx +++ b/pkg/interface/src/views/components/Body.tsx @@ -1,6 +1,6 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode } from 'react'; -import { Box } from "@tlon/indigo-react"; +import { Box } from '@tlon/indigo-react'; export function Body( props: { children: ReactNode } & Parameters[0] @@ -14,7 +14,7 @@ export function Body( width="100%" borderRadius={2} border={[0, 1]} - borderColor={["washedGray", "washedGray"]} + borderColor={['washedGray', 'washedGray']} {...boxProps} > {props.children} diff --git a/pkg/interface/src/views/components/ChipInput.tsx b/pkg/interface/src/views/components/ChipInput.tsx index e75934fff..02471d817 100644 --- a/pkg/interface/src/views/components/ChipInput.tsx +++ b/pkg/interface/src/views/components/ChipInput.tsx @@ -2,23 +2,23 @@ import React, { useCallback, useState, ReactNode, - SyntheticEvent, useEffect, useRef, -} from "react"; + ReactElement +} from 'react'; +import { useField } from 'formik'; +import Mousetrap from 'mousetrap'; + import { - Box, Label, Row, Col, StatelessTextInput as Input, ErrorLabel -} from "@tlon/indigo-react"; -import { useField } from "formik"; -import Mousetrap from "mousetrap"; -import * as Yup from "yup"; +} from '@tlon/indigo-react'; -function Chip(props: { children: ReactNode }) { + +function Chip(props: { children: ReactNode }): ReactElement { return ( ( + const [{ onBlur, value }, meta, { setValue }] = useField( id ); - const [newChip, setNextChip] = useState(""); + const [newChip, setNextChip] = useState(''); const onChange = useCallback( (e: any) => { setNextChip(e.target.value); @@ -57,7 +57,7 @@ export function ChipInput(props: ChipInputProps) { const addNewChip = useCallback(() => { setValue([...value, newChip]); - setNextChip(""); + setNextChip(''); }, [setValue, value, newChip, setNextChip]); const removeLastChip = useCallback(() => { @@ -70,18 +70,18 @@ export function ChipInput(props: ChipInputProps) { return () => {}; } const mousetrap = Mousetrap(inputRef.current); - mousetrap.bind("backspace", (e) => { + mousetrap.bind('backspace', (e) => { if (newChip.length === 0) { removeLastChip(); return false; } return true; }); - mousetrap.bind("tab", (e) => { + mousetrap.bind('tab', (e) => { addNewChip(); return false; }); - mousetrap.bind("space", (e) => { + mousetrap.bind('space', (e) => { if (props.breakOnSpace) { addNewChip(); return false; @@ -89,9 +89,9 @@ export function ChipInput(props: ChipInputProps) { return true; }); return () => { - mousetrap.unbind("tab"); - mousetrap.unbind("backspace"); - mousetrap.unbind("space"); + mousetrap.unbind('tab'); + mousetrap.unbind('backspace'); + mousetrap.unbind('space'); }; }, [inputRef.current, addNewChip, newChip]); @@ -128,7 +128,7 @@ export function ChipInput(props: ChipInputProps) { py="1" /> - + {meta.error} diff --git a/pkg/interface/src/views/components/ColorInput.tsx b/pkg/interface/src/views/components/ColorInput.tsx index 0ee279db1..8ef2b9d00 100644 --- a/pkg/interface/src/views/components/ColorInput.tsx +++ b/pkg/interface/src/views/components/ColorInput.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import { useField } from "formik"; +import React, { FormEvent, ReactElement } from 'react'; +import { useField } from 'formik'; + import { Col, Label, Row, Box, ErrorLabel, - StatelessTextInput as Input, -} from "@tlon/indigo-react"; + StatelessTextInput as Input +} from '@tlon/indigo-react'; -import { uxToHex, hexToUx } from "~/logic/lib/util"; +import { hexToUx } from '~/logic/lib/util'; type ColorInputProps = Parameters[0] & { id: string; @@ -17,14 +18,14 @@ type ColorInputProps = Parameters[0] & { disabled?: boolean; }; -export function ColorInput(props: ColorInputProps) { +export function ColorInput(props: ColorInputProps): ReactElement { const { id, label, caption, disabled, ...rest } = props; const [{ value, onBlur }, meta, { setValue }] = useField(id); - const hex = value.replace('#', '').replace("0x","").replace(".", ""); - const padded = hex.padStart(6, "0"); + const hex = value.replace('#', '').replace('0x','').replace('.', ''); + const padded = hex.padStart(6, '0'); - const onChange = (e: any) => { + const onChange = (e: FormEvent) => { let { value: newValue } = e.target as HTMLInputElement; newValue = newValue.replace('#', ''); const valid = newValue.match(/^(\d|[a-f]|[A-F]){0,6}$/); @@ -36,7 +37,6 @@ export function ColorInput(props: ColorInputProps) { setValue(result); }; - return ( @@ -78,7 +78,7 @@ export function ColorInput(props: ColorInputProps) { /> - + {meta.error} diff --git a/pkg/interface/src/views/components/CommentInput.tsx b/pkg/interface/src/views/components/CommentInput.tsx index 99189efb7..9c669bbd5 100644 --- a/pkg/interface/src/views/components/CommentInput.tsx +++ b/pkg/interface/src/views/components/CommentInput.tsx @@ -1,15 +1,15 @@ -import React from "react"; -import * as Yup from "yup"; -import { Formik, FormikHelpers, Form, useFormikContext } from "formik"; -import { AsyncButton } from "./AsyncButton"; -import { ManagedTextAreaField as TextArea } from "@tlon/indigo-react"; +import React from 'react'; +import * as Yup from 'yup'; +import { Formik, FormikHelpers, Form, useFormikContext } from 'formik'; +import { AsyncButton } from './AsyncButton'; +import { ManagedTextAreaField as TextArea } from '@tlon/indigo-react'; interface FormSchema { comment: string; } const formSchema = Yup.object({ - comment: Yup.string().required("Comment can't be empty"), + comment: Yup.string().required('Comment can\'t be empty') }); interface CommentInputProps { @@ -25,7 +25,7 @@ interface CommentInputProps { const SubmitTextArea = (props) => { const { submitForm } = useFormikContext(); const onKeyDown = (e: KeyboardEvent) => { - if ((e.getModifierState("Control") || e.metaKey) && e.key === "Enter") { + if ((e.getModifierState('Control') || e.metaKey) && e.key === 'Enter') { submitForm(); } }; @@ -33,9 +33,9 @@ const SubmitTextArea = (props) => { }; export default function CommentInput(props: CommentInputProps) { - const initialValues: FormSchema = { comment: props.initial || "" }; - const label = props.label || "Add Comment"; - const loading = props.loadingText || "Commenting..."; + const initialValues: FormSchema = { comment: props.initial || '' }; + const label = props.label || 'Add Comment'; + const loading = props.loadingText || 'Commenting...'; return ( {label} diff --git a/pkg/interface/src/views/components/CommentItem.tsx b/pkg/interface/src/views/components/CommentItem.tsx index 4bd76a8eb..a18905c7a 100644 --- a/pkg/interface/src/views/components/CommentItem.tsx +++ b/pkg/interface/src/views/components/CommentItem.tsx @@ -1,13 +1,14 @@ -import React, { useState } from 'react'; -import { Link } from "react-router-dom"; -import { Contacts } from '~/types/contact-update'; -import GlobalApi from '~/logic/api/global'; -import { Box, Row, Text } from '@tlon/indigo-react'; +import React from 'react'; +import { Link } from 'react-router-dom'; import styled from 'styled-components'; + +import { Box, Row, Text } from '@tlon/indigo-react'; +import { Contacts } from '@urbit/api/contacts'; +import { GraphNode } from '@urbit/api/graph'; +import { Group } from '@urbit/api'; + +import GlobalApi from '~/logic/api/global'; import Author from '~/views/components/Author'; -import { GraphNode, TextContent } from '~/types/graph-update'; -import tokenizeMessage from '~/logic/lib/tokenizeMessage'; -import { Group } from '~/types'; import { MentionText } from '~/views/components/MentionText'; import { getLatestCommentRevision } from '~/logic/lib/publish'; @@ -28,9 +29,9 @@ interface CommentItemProps { group: Group; } -export function CommentItem(props: CommentItemProps) { +export function CommentItem(props: CommentItemProps): ReactElement { const { ship, contacts, name, api, comment, group } = props; - const [revNum, post] = getLatestCommentRevision(comment); + const [, post] = getLatestCommentRevision(comment); const disabled = props.pending || window.ship !== post?.author; const onDelete = async () => { @@ -39,7 +40,7 @@ export function CommentItem(props: CommentItemProps) { const commentIndexArray = (comment.post?.index || '/').split('/'); const commentIndex = commentIndexArray[commentIndexArray.length - 1]; - const updateUrl = `${props.baseUrl}/${commentIndex}` + const updateUrl = `${props.baseUrl}/${commentIndex}`; return ( diff --git a/pkg/interface/src/views/components/Comments.tsx b/pkg/interface/src/views/components/Comments.tsx index 51297020b..6c6d13513 100644 --- a/pkg/interface/src/views/components/Comments.tsx +++ b/pkg/interface/src/views/components/Comments.tsx @@ -3,10 +3,10 @@ import bigInt from 'big-integer'; import { Col } from '@tlon/indigo-react'; import { CommentItem } from './CommentItem'; import CommentInput from './CommentInput'; -import { Contacts } from '~/types/contact-update'; +import { Contacts } from '@urbit/api/contacts'; import GlobalApi from '~/logic/api/global'; import { FormikHelpers } from 'formik'; -import { Group, GraphNode, Association } from '~/types'; +import { Group, GraphNode, Association } from '@urbit/api'; import { createPost, createBlankNodeWithChildPost } from '~/logic/api/graph'; import { getLatestCommentRevision } from '~/logic/lib/publish'; import tokenizeMessage from '~/logic/lib/tokenizeMessage'; diff --git a/pkg/interface/src/views/components/Dropdown.tsx b/pkg/interface/src/views/components/Dropdown.tsx index 7e2a1fb2c..352973905 100644 --- a/pkg/interface/src/views/components/Dropdown.tsx +++ b/pkg/interface/src/views/components/Dropdown.tsx @@ -4,14 +4,17 @@ import React, { useRef, useEffect, useCallback, -} from "react"; -import styled from "styled-components"; -import _ from "lodash"; -import { Box, Col } from "@tlon/indigo-react"; -import { useOutsideClick } from "~/logic/lib/useOutsideClick"; -import { useLocation } from "react-router-dom"; -import { Portal } from "./Portal"; -import { getRelativePosition, AlignY, AlignX } from "~/logic/lib/relativePosition"; + ReactElement +} from 'react'; +import styled from 'styled-components'; +import _ from 'lodash'; +import { useLocation } from 'react-router-dom'; + +import { Box } from '@tlon/indigo-react'; + +import { useOutsideClick } from '~/logic/lib/useOutsideClick'; +import { Portal } from './Portal'; +import { getRelativePosition, AlignY, AlignX } from '~/logic/lib/relativePosition'; interface DropdownProps { children: ReactNode; @@ -33,7 +36,7 @@ const DropdownOptions = styled(Box)` transition-timing-function: ease; `; -export function Dropdown(props: DropdownProps) { +export function Dropdown(props: DropdownProps): ReactElement { const { children, options } = props; const dropdownRef = useRef(null); const anchorRef = useRef(null); @@ -75,14 +78,14 @@ export function Dropdown(props: DropdownProps) { }); return ( - + {children} {open && ( @@ -95,6 +98,6 @@ export function Dropdown(props: DropdownProps) { } Dropdown.defaultProps = { - alignX: "left", - alignY: "bottom", + alignX: 'left', + alignY: 'bottom' }; diff --git a/pkg/interface/src/views/components/DropdownSearch.tsx b/pkg/interface/src/views/components/DropdownSearch.tsx index 5860af19e..962423eb3 100644 --- a/pkg/interface/src/views/components/DropdownSearch.tsx +++ b/pkg/interface/src/views/components/DropdownSearch.tsx @@ -5,17 +5,18 @@ import React, { useCallback, useEffect, ChangeEvent, -} from "react"; -import _ from "lodash"; -import Mousetrap from "mousetrap"; + ReactElement +} from 'react'; +import _ from 'lodash'; +import Mousetrap from 'mousetrap'; + import { Box, - Label, - ErrorLabel, - StatelessTextInput as Input, -} from "@tlon/indigo-react"; -import { useDropdown } from "~/logic/lib/useDropdown"; -import { PropFunc } from "~/types/util"; + StatelessTextInput as Input +} from '@tlon/indigo-react'; + +import { useDropdown } from '~/logic/lib/useDropdown'; +import { PropFunc } from '~/types/util'; interface DropdownSearchExtraProps { // check if entry is exact match @@ -36,14 +37,14 @@ interface DropdownSearchExtraProps { disabled?: boolean; placeholder?: string; onChange?: (e: ChangeEvent) => void; - onBlur?: (e: any) => void; + onBlur?: (e: FocusEvent) => void; onFocus?: (e: FocusEvent) => void; } type DropdownSearchProps = PropFunc & DropdownSearchExtraProps; -export function DropdownSearch(props: DropdownSearchProps) { +export function DropdownSearch(props: DropdownSearchProps): ReactElement { const textarea = useRef(); const { candidates, @@ -54,13 +55,13 @@ export function DropdownSearch(props: DropdownSearchProps) { renderCandidate, disabled, placeholder, - onFocus = () => {}, - onChange = () => {}, - onBlur = () => {}, + onFocus = (): void => {}, + onChange = (): void => {}, + onBlur = (): void => {}, ...rest } = props; - const [query, setQuery] = useState(""); + const [query, setQuery] = useState(''); const exact = useCallback( (s: string) => { return isExact ? isExact(s) : undefined; @@ -77,7 +78,7 @@ export function DropdownSearch(props: DropdownSearchProps) { const handleSelect = useCallback( (c: C) => { - setQuery(""); + setQuery(''); onSelect(c); }, [setQuery, onSelect] @@ -96,14 +97,14 @@ export function DropdownSearch(props: DropdownSearchProps) { } const mousetrap = Mousetrap(textarea.current); - mousetrap.bind(["down", "tab"], next); - mousetrap.bind(["up", "shift+tab"], back); - mousetrap.bind("enter", onEnter); + mousetrap.bind(['down', 'tab'], next); + mousetrap.bind(['up', 'shift+tab'], back); + mousetrap.bind('enter', onEnter); return () => { - mousetrap.unbind(["down", "tab"]); - mousetrap.unbind(["up", "shift+tab"]); - mousetrap.unbind("enter"); + mousetrap.unbind(['down', 'tab']); + mousetrap.unbind(['up', 'shift+tab']); + mousetrap.unbind('enter'); }; }, [textarea.current, next, back, onEnter]); diff --git a/pkg/interface/src/views/components/ErrorBoundary.tsx b/pkg/interface/src/views/components/ErrorBoundary.tsx index 5d36f7ab9..c166dfe6b 100644 --- a/pkg/interface/src/views/components/ErrorBoundary.tsx +++ b/pkg/interface/src/views/components/ErrorBoundary.tsx @@ -13,7 +13,7 @@ class ErrorBoundary extends Component< history.listen((location, action) => { if (this.state.error) { this.setState({ - error: undefined, + error: undefined }); } }); @@ -26,7 +26,7 @@ class ErrorBoundary extends Component< render() { if (this.state.error) { - return () + return (); } return this.props.children; } diff --git a/pkg/interface/src/views/components/FormError.tsx b/pkg/interface/src/views/components/FormError.tsx index 6aa5cca1c..ed10100a8 100644 --- a/pkg/interface/src/views/components/FormError.tsx +++ b/pkg/interface/src/views/components/FormError.tsx @@ -1,16 +1,16 @@ -import React from "react"; -import { useFormikContext } from "formik"; -import { ErrorLabel } from "@tlon/indigo-react"; -import {PropFunc} from "~/types/util"; +import React from 'react'; +import { useFormikContext } from 'formik'; +import { ErrorLabel } from '@tlon/indigo-react'; +import { PropFunc } from '~/types/util'; export function FormError(props: { message?: string } & PropFunc) { const { status } = useFormikContext(); const { message, ...rest } = props; - let s = status || {}; + const s = status || {}; const contents = message || s?.error; return ( - {contents} + {contents} ); } diff --git a/pkg/interface/src/views/components/FormSubmit.tsx b/pkg/interface/src/views/components/FormSubmit.tsx index ae481de85..6851c8618 100644 --- a/pkg/interface/src/views/components/FormSubmit.tsx +++ b/pkg/interface/src/views/components/FormSubmit.tsx @@ -1,13 +1,13 @@ -import React, { useCallback, ReactNode } from "react"; -import { useFormikContext } from "formik"; -import { Row, Button } from "@tlon/indigo-react"; -import { AsyncButton } from "./AsyncButton"; +import React, { useCallback, ReactNode, ReactElement } from 'react'; +import { useFormikContext } from 'formik'; +import { Row, Button } from '@tlon/indigo-react'; +import { AsyncButton } from './AsyncButton'; interface FormSubmitProps { children?: ReactNode; } -export function FormSubmit(props: FormSubmitProps) { +export function FormSubmit(props: FormSubmitProps): ReactElement { const { children } = props; const { initialValues, values, dirty, resetForm, isSubmitting } = useFormikContext(); @@ -19,7 +19,6 @@ export function FormSubmit(props: FormSubmitProps) { resetForm({ errors: {}, touched: {}, values: initialValues, status: {} }); }, [resetForm, initialValues]); - return ( void; detailed?: boolean; } & PropFunc -) { +): ReactElement { const { resource, api, associations, groups, measure, ...rest } = props; const name = resource.slice(6); const [preview, setPreview] = useState(null); @@ -31,7 +33,7 @@ export function GroupLink( ) : ( @@ -41,7 +43,7 @@ export function GroupLink( api={api} autojoin={name} /> - ), + ) }); useEffect(() => { diff --git a/pkg/interface/src/views/components/GroupSearch.tsx b/pkg/interface/src/views/components/GroupSearch.tsx index 4cc75b781..d9bc2f6e8 100644 --- a/pkg/interface/src/views/components/GroupSearch.tsx +++ b/pkg/interface/src/views/components/GroupSearch.tsx @@ -1,4 +1,8 @@ -import React, { useMemo, useState } from 'react'; +import React, { ReactElement, useMemo, useState } from 'react'; +import { useFormikContext, FieldArray } from 'formik'; +import _ from 'lodash'; +import styled from 'styled-components'; + import { Box, Text, @@ -8,15 +12,12 @@ import { Icon, ErrorLabel } from '@tlon/indigo-react'; -import _ from 'lodash'; -import { useField, useFormikContext, FieldArray } from 'formik'; -import styled from 'styled-components'; +import { Groups } from '@urbit/api'; +import { Associations, Association } from '@urbit/api/metadata'; + import { roleForShip } from '~/logic/lib/group'; - import { DropdownSearch } from './DropdownSearch'; -import { Groups } from '~/types'; -import { Associations, Association } from '~/types/metadata-update'; interface GroupSearchProps { disabled?: boolean; @@ -36,7 +37,7 @@ const CandidateBox = styled(Box)<{ selected: boolean }>` } `; -const Candidate = ({ title, selected, onClick }) => ( +const Candidate = ({ title, selected, onClick }): ReactElement => ( void -) { +): ReactElement { const { title } = a.metadata; const onClick = () => { @@ -68,7 +69,7 @@ type FormValues = { [id in I]: string[]; }; -export function GroupSearch>(props: GroupSearchProps) { +export function GroupSearch>(props: GroupSearchProps): ReactElement { const { id, caption, label } = props; const { values, @@ -76,7 +77,7 @@ export function GroupSearch>(props: Gr errors, initialValues, setFieldValue, - setFieldTouched, + setFieldTouched } = useFormikContext(); const [inputIdx, setInputIdx] = useState(initialValues[id].length); const name = `${id}[${inputIdx}]`; @@ -177,7 +178,8 @@ export function GroupSearch>(props: Gr ); - }} /> + }} + /> ); } diff --git a/pkg/interface/src/views/components/HoverBox.tsx b/pkg/interface/src/views/components/HoverBox.tsx index d678c1e4d..8159a68e3 100644 --- a/pkg/interface/src/views/components/HoverBox.tsx +++ b/pkg/interface/src/views/components/HoverBox.tsx @@ -1,19 +1,19 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import styled from "styled-components"; -import { Box } from "@tlon/indigo-react"; -import { PropFunc } from "~/types/util"; +import React from 'react'; +import { Link } from 'react-router-dom'; +import styled from 'styled-components'; +import { Box } from '@tlon/indigo-react'; +import { PropFunc } from '~/types/util'; interface HoverBoxProps { selected: boolean; bg: string; bgActive: string; } export const HoverBox = styled(Box)` - background-color: ${(p) => + background-color: ${p => p.selected ? p.theme.colors[p.bgActive] : p.theme.colors[p.bg]}; pointer: cursor; &:hover { - background-color: ${(p) => p.theme.colors[p.bgActive]}; + background-color: ${p => p.theme.colors[p.bgActive]}; } `; diff --git a/pkg/interface/src/views/components/IconRadio.tsx b/pkg/interface/src/views/components/IconRadio.tsx index 912329e8f..e58e76a80 100644 --- a/pkg/interface/src/views/components/IconRadio.tsx +++ b/pkg/interface/src/views/components/IconRadio.tsx @@ -1,15 +1,15 @@ -import React, { useCallback, useMemo } from "react"; -import styled from "styled-components"; +import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; +import { useField } from 'formik'; + import { Icon, Box, Row, BaseLabel, - Indicator, Col, - Label, -} from "@tlon/indigo-react"; -import { useField } from "formik"; + Label +} from '@tlon/indigo-react'; type IconRadioProps = Parameters[0] & { id: string; @@ -40,45 +40,50 @@ type IconIndicatorProps = Parameters & { const indicator = { state: { on: { - //"*": { fill: "white" }, - backgroundColor: "blue", - borderColor: "blue", + // "*": { fill: "white" }, + backgroundColor: 'blue', + borderColor: 'blue' }, off: { - //"*": { fill: "transparent" }, - backgroundColor: "white", - borderColor: "lightGray", + // "*": { fill: "transparent" }, + backgroundColor: 'white', + borderColor: 'lightGray' }, onError: { - //"*": { fill: "white" }, - backgroundColor: "red", - borderColor: "red", + // "*": { fill: "white" }, + backgroundColor: 'red', + borderColor: 'red' }, offError: { // "*": { fill: "transparent" }, - backgroundColor: "washedRed", - borderColor: "red", + backgroundColor: 'washedRed', + borderColor: 'red' }, offDisabled: { - //"*": { fill: "transparent" }, - backgroundColor: "washedGray", - borderColor: "lightGray", + // "*": { fill: "transparent" }, + backgroundColor: 'washedGray', + borderColor: 'lightGray' }, onDisabled: { - //"*": { fill: "lightGray" }, - backgroundColor: "washedGray", - borderColor: "lightGray", - }, - }, + // "*": { fill: "lightGray" }, + backgroundColor: 'washedGray', + borderColor: 'lightGray' + } + } }; const IconIndicator = ({ disabled, selected, hasError, children, ...rest }) => { const style = useMemo(() => { - if (selected && disabled) return indicator.state.onDisabled; - if (selected && hasError) return indicator.state.onError; - if (selected) return indicator.state.on; - if (disabled) return indicator.state.offDisabled; - if (hasError) return indicator.state.offError; + if (selected && disabled) +return indicator.state.onDisabled; + if (selected && hasError) +return indicator.state.onError; + if (selected) +return indicator.state.on; + if (disabled) +return indicator.state.offDisabled; + if (hasError) +return indicator.state.offError; return indicator.state.off; }, [selected, disabled, hasError]); @@ -95,7 +100,7 @@ export function IconRadio(props: IconRadioProps) { name, id, value: id, - type: "radio", + type: 'radio' }); const onChange = useCallback( @@ -122,12 +127,12 @@ export function IconRadio(props: IconRadioProps) { > - + {caption ? ( diff --git a/pkg/interface/src/views/components/Invite/Group.tsx b/pkg/interface/src/views/components/Invite/Group.tsx index ea5c37b98..9345d1c78 100644 --- a/pkg/interface/src/views/components/Invite/Group.tsx +++ b/pkg/interface/src/views/components/Invite/Group.tsx @@ -1,11 +1,11 @@ -import React, { ReactNode } from "react"; -import { Text, Box, Button, Icon, Row, Rule, Col } from "@tlon/indigo-react"; +import React, { ReactElement, ReactNode } from 'react'; +import { Text, Box, Icon, Row } from '@tlon/indigo-react'; -import { cite } from "~/logic/lib/util"; -import { MetadataUpdatePreview, JoinProgress, Invite } from "~/types"; -import { GroupSummary } from "~/views/landscape/components/GroupSummary"; -import { InviteSkeleton } from "./InviteSkeleton"; -import { JoinSkeleton } from "./JoinSkeleton"; +import { cite } from '~/logic/lib/util'; +import { MetadataUpdatePreview, JoinProgress, Invite } from '@urbit/api'; +import { GroupSummary } from '~/views/landscape/components/GroupSummary'; +import { InviteSkeleton } from './InviteSkeleton'; +import { JoinSkeleton } from './JoinSkeleton'; interface GroupInviteProps { preview: MetadataUpdatePreview; @@ -15,12 +15,12 @@ interface GroupInviteProps { onDecline: () => Promise; } -export function GroupInvite(props: GroupInviteProps) { +export function GroupInvite(props: GroupInviteProps): ReactElement { const { preview, invite, status, onAccept, onDecline } = props; const { metadata, members } = props.preview; let inner: ReactNode = null; - let Outer: (p: { children: ReactNode }) => JSX.Element = (p) => ( + let Outer: (p: { children: ReactNode }) => JSX.Element = p => ( <>{p.children} ); @@ -68,7 +68,7 @@ export function GroupInvite(props: GroupInviteProps) { gray metadata={metadata} memberCount={members} - channelCount={preview?.["channel-count"]} + channelCount={preview?.['channel-count']} /> diff --git a/pkg/interface/src/views/components/Invite/InviteSkeleton.tsx b/pkg/interface/src/views/components/Invite/InviteSkeleton.tsx index c1b4d3899..bcf773f68 100644 --- a/pkg/interface/src/views/components/Invite/InviteSkeleton.tsx +++ b/pkg/interface/src/views/components/Invite/InviteSkeleton.tsx @@ -1,8 +1,8 @@ -import React, { ReactNode } from "react"; -import { Text, Box, Button, Icon, Row, Rule, Col } from "@tlon/indigo-react"; +import React, { ReactElement, ReactNode } from 'react'; +import { Row, Rule, Col } from '@tlon/indigo-react'; -import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction"; -import { PropFunc } from "~/types"; +import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction'; +import { PropFunc } from '~/types'; export interface InviteSkeletonProps { onAccept: () => Promise; @@ -14,7 +14,7 @@ export interface InviteSkeletonProps { export function InviteSkeleton( props: InviteSkeletonProps & PropFunc -) { +): ReactElement { const { children, acceptDesc, diff --git a/pkg/interface/src/views/components/Invite/JoinSkeleton.tsx b/pkg/interface/src/views/components/Invite/JoinSkeleton.tsx index c3e39bd63..c3e6874f3 100644 --- a/pkg/interface/src/views/components/Invite/JoinSkeleton.tsx +++ b/pkg/interface/src/views/components/Invite/JoinSkeleton.tsx @@ -1,14 +1,15 @@ -import React, { ReactNode } from "react"; -import { Col, Row, SegmentedProgressBar, Text, Rule } from "@tlon/indigo-react"; -import { JoiningStatus } from "~/views/apps/notifications/joining"; -import { JoinProgress, PropFunc } from "~/types"; +import React, { ReactElement, ReactNode } from 'react'; +import { Col, Rule } from '@tlon/indigo-react'; +import { JoiningStatus } from '~/views/apps/notifications/joining'; +import { JoinProgress } from '@urbit/api'; +import { PropFunc } from '~/types/util'; type JoinSkeletonProps = { children: ReactNode; status: JoinProgress; } & PropFunc; -export function JoinSkeleton(props: JoinSkeletonProps) { +export function JoinSkeleton(props: JoinSkeletonProps): ReactElement { const { children, status, ...rest } = props; return ( <> diff --git a/pkg/interface/src/views/components/Invite/index.tsx b/pkg/interface/src/views/components/Invite/index.tsx index fc6e9b192..d198e430e 100644 --- a/pkg/interface/src/views/components/Invite/index.tsx +++ b/pkg/interface/src/views/components/Invite/index.tsx @@ -1,25 +1,23 @@ -import React, { Component, useState, useEffect, useCallback, useMemo } from "react"; -import { Invite } from "~/types/invite-update"; -import { Text, Box, Button, Icon, Row, Rule, Col } from "@tlon/indigo-react"; -import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction"; -import { cite } from "~/logic/lib/util"; +import React, { useState, useEffect, useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; + import { MetadataUpdatePreview, Contacts, JoinRequests, - JoinProgress, Groups, - Associations, -} from "~/types"; -import GlobalApi from "~/logic/api/global"; -import { GroupSummary } from "~/views/landscape/components/GroupSummary"; -import { JoiningStatus } from "~/views/apps/notifications/joining"; -import { resourceFromPath } from "~/logic/lib/group"; -import { GroupInvite } from "./Group"; -import { InviteSkeleton } from "./InviteSkeleton"; -import { JoinSkeleton } from "./JoinSkeleton"; -import { useWaitForProps } from "~/logic/lib/useWaitForProps"; -import { useHistory } from "react-router-dom"; + Associations +} from '@urbit/api'; +import { Invite } from '@urbit/api/invite'; +import { Text, Icon, Row } from '@tlon/indigo-react'; + +import { cite } from '~/logic/lib/util'; +import GlobalApi from '~/logic/api/global'; +import { resourceFromPath } from '~/logic/lib/group'; +import { GroupInvite } from './Group'; +import { InviteSkeleton } from './InviteSkeleton'; +import { JoinSkeleton } from './JoinSkeleton'; +import { useWaitForProps } from '~/logic/lib/useWaitForProps'; interface InviteItemProps { invite?: Invite; @@ -61,7 +59,7 @@ export function InviteItem(props: InviteItemProps) { if (props.groups?.[resource]?.hidden) { const { metadata } = associations.graph[resource]; - if (name.startsWith("dm--")) { + if (name.startsWith('dm--')) { history.push(`/~landscape/messages/resource/${metadata.module}${resource}`); } else { history.push(`/~landscape/home/resource/${metadata.module}${resource}`); @@ -78,10 +76,10 @@ export function InviteItem(props: InviteItemProps) { await api.invite.decline(app, uid); }, [app, uid]); - const handlers = { onAccept: inviteAccept, onDecline: inviteDecline } + const handlers = { onAccept: inviteAccept, onDecline: inviteDecline }; useEffect(() => { - if (!app || app === "groups") { + if (!app || app === 'groups') { (async () => { setPreview(await api.metadata.preview(resource)); })(); @@ -102,7 +100,7 @@ export function InviteItem(props: InviteItemProps) { {...handlers} /> ); - } else if (invite && name.startsWith("dm--")) { + } else if (invite && name.startsWith('dm--')) { return ( ); - } else if (status && name.startsWith("dm--")) { + } else if (status && name.startsWith('dm--')) { return ( You are joining a DM with - {cite("~hastuc-dibtux")} + {cite('~hastuc-dibtux')} @@ -151,7 +149,7 @@ export function InviteItem(props: InviteItemProps) { ); } else if (status) { - const [, , ship, name] = resource.split("/"); + const [, , ship, name] = resource.split('/'); return ( diff --git a/pkg/interface/src/views/components/Loading.tsx b/pkg/interface/src/views/components/Loading.tsx index a11f5e09c..78822673e 100644 --- a/pkg/interface/src/views/components/Loading.tsx +++ b/pkg/interface/src/views/components/Loading.tsx @@ -1,7 +1,7 @@ -import React from "react"; -import { Text, Center, LoadingSpinner } from "@tlon/indigo-react"; +import React from 'react'; +import { Text, Center, LoadingSpinner } from '@tlon/indigo-react'; -import { Body } from "./Body"; +import { Body } from './Body'; interface LoadingProps { text?: string; @@ -11,7 +11,7 @@ export function Loading({ text }: LoadingProps) {
- {!!text && {text}} + {Boolean(text) && {text}}
); diff --git a/pkg/interface/src/views/components/MentionText.tsx b/pkg/interface/src/views/components/MentionText.tsx index 02bdf0c22..96187d94c 100644 --- a/pkg/interface/src/views/components/MentionText.tsx +++ b/pkg/interface/src/views/components/MentionText.tsx @@ -1,7 +1,7 @@ import React, { useState, useCallback } from 'react'; import _ from 'lodash'; import { Text, Box } from '@tlon/indigo-react'; -import { Contact, Contacts, Content, Group } from '~/types'; +import { Contact, Contacts, Content, Group } from '@urbit/api'; import RichText from '~/views/components/RichText'; import { cite, useShowNickname, uxToHex } from '~/logic/lib/util'; import OverlaySigil from '~/views/components/OverlaySigil'; diff --git a/pkg/interface/src/views/components/ModalOverlay.tsx b/pkg/interface/src/views/components/ModalOverlay.tsx index fee8c578e..9838fe1ad 100644 --- a/pkg/interface/src/views/components/ModalOverlay.tsx +++ b/pkg/interface/src/views/components/ModalOverlay.tsx @@ -1,9 +1,9 @@ -import React, { useCallback, UIEvent, MouseEvent, useRef } from "react"; -import { Box } from "@tlon/indigo-react"; -import { PropFunc } from "~/types/util"; +import React, { useCallback, UIEvent, MouseEvent, useRef } from 'react'; +import { Box } from '@tlon/indigo-react'; +import { PropFunc } from '~/types/util'; interface ModalOverlayProps { - spacing: PropFunc["m"]; + spacing: PropFunc['m']; dismiss: () => void; } type Props = ModalOverlayProps & PropFunc; @@ -22,7 +22,7 @@ export const ModalOverlay = (props: Props) => { const onKeyDown = useCallback( (e: any) => { - if (e.key === "Escape") { + if (e.key === 'Escape') { props.dismiss(); e.stopPropagation(); } @@ -49,4 +49,4 @@ export const ModalOverlay = (props: Props) => { ); -} +}; diff --git a/pkg/interface/src/views/components/OverlaySigil.tsx b/pkg/interface/src/views/components/OverlaySigil.tsx index f2d031feb..066e1de7d 100644 --- a/pkg/interface/src/views/components/OverlaySigil.tsx +++ b/pkg/interface/src/views/components/OverlaySigil.tsx @@ -1,5 +1,5 @@ -import React, { useState, useRef, useEffect, PureComponent } from 'react'; -import { Contact, Group } from '~/types'; +import React, { useState, useRef, useEffect } from 'react'; +import { Contact, Group } from '@urbit/api'; import ProfileOverlay, { OVERLAY_HEIGHT } from './ProfileOverlay'; import { Box, ColProps } from '@tlon/indigo-react'; diff --git a/pkg/interface/src/views/components/Portal.tsx b/pkg/interface/src/views/components/Portal.tsx index 7bc5d51b6..297168e90 100644 --- a/pkg/interface/src/views/components/Portal.tsx +++ b/pkg/interface/src/views/components/Portal.tsx @@ -1,10 +1,10 @@ -import { useEffect, ReactNode, useMemo } from "react"; -import { createPortal } from "react-dom"; +import { useEffect, ReactNode, useMemo } from 'react'; +import { createPortal } from 'react-dom'; export function Portal(props: { children: ReactNode }) { - const root = document.getElementById("portal-root"); + const root = document.getElementById('portal-root'); - const el = useMemo(() => document.createElement("div"), []); + const el = useMemo(() => document.createElement('div'), []); useEffect(() => { root?.appendChild(el); diff --git a/pkg/interface/src/views/components/ProfileOverlay.tsx b/pkg/interface/src/views/components/ProfileOverlay.tsx index 367d81186..df1fec01c 100644 --- a/pkg/interface/src/views/components/ProfileOverlay.tsx +++ b/pkg/interface/src/views/components/ProfileOverlay.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; -import { Contact, Group } from '~/types'; +import { Contact, Group } from '@urbit/api'; import { cite, useShowNickname } from '~/logic/lib/util'; import { Sigil } from '~/logic/lib/sigil'; @@ -33,7 +33,7 @@ type ProfileOverlayProps = ColProps & { api: any; }; -class ProfileOverlay extends PureComponent { +class ProfileOverlay extends PureComponent> { public popoverRef: React.Ref; constructor(props) { @@ -123,13 +123,14 @@ class ProfileOverlay extends PureComponent { > {(!isOwn) && ( - history.push(`/~landscape/dm/${ship}`)}/> + history.push(`/~landscape/dm/${ship}`)} /> )} history.push(`/~profile/~${ship}`)}> + onClick={() => history.push(`/~profile/~${ship}`)} + > {img} diff --git a/pkg/interface/src/views/components/RemoteContent.tsx b/pkg/interface/src/views/components/RemoteContent.tsx index c6f16c658..b490a3d2d 100644 --- a/pkg/interface/src/views/components/RemoteContent.tsx +++ b/pkg/interface/src/views/components/RemoteContent.tsx @@ -66,7 +66,8 @@ class RemoteContent extends PureComponent { this.setState({ embed: result }); }).catch((error) => { - if (error.name === 'AbortError') return; + if (error.name === 'AbortError') +return; this.setState({ embed: 'error' }); }); } @@ -81,7 +82,7 @@ class RemoteContent extends PureComponent + > {contents} ); } @@ -171,7 +172,7 @@ class RemoteContent extends PureComponent + > {this.state.unfold ? 'collapse' : 'expand'} : null} {this.state.embed && this.state.embed.html && this.state.unfold ? -
{ this.containerRef = el; }} - dangerouslySetInnerHTML={{__html: this.state.embed.html}}>
+
{ + this.containerRef = el; +}} + dangerouslySetInnerHTML={{ __html: this.state.embed.html }} + >
: null}
diff --git a/pkg/interface/src/views/components/ShipSearch.tsx b/pkg/interface/src/views/components/ShipSearch.tsx index e4e6c4566..5a0433495 100644 --- a/pkg/interface/src/views/components/ShipSearch.tsx +++ b/pkg/interface/src/views/components/ShipSearch.tsx @@ -2,33 +2,28 @@ import React, { useMemo, useCallback, ChangeEvent, - useState, - SyntheticEvent, - useEffect, useRef, -} from "react"; + ReactElement +} from 'react'; +import _ from 'lodash'; +import ob from 'urbit-ob'; +import * as Yup from 'yup'; +import { FieldArray, useFormikContext } from 'formik'; + import { - Box, Label, Icon, Text, Row, Col, - ErrorLabel, -} from "@tlon/indigo-react"; -import _ from "lodash"; -import ob from "urbit-ob"; -import * as Yup from "yup"; -import { useField, FieldArray, useFormikContext } from "formik"; -import styled from "styled-components"; + ErrorLabel +} from '@tlon/indigo-react'; +import { Rolodex, Groups } from '@urbit/api'; -import { DropdownSearch } from "./DropdownSearch"; -import { Associations, Association } from "~/types/metadata-update"; -import { cite, deSig } from "~/logic/lib/util"; -import { Rolodex, Groups } from "~/types"; -import { HoverBox } from "./HoverBox"; -const INVALID_SHIP_ERR = "Invalid ship"; +import { DropdownSearch } from './DropdownSearch'; +import { cite, deSig } from '~/logic/lib/util'; +import { HoverBox } from './HoverBox'; interface InviteSearchProps { autoFocus?: boolean; @@ -42,7 +37,7 @@ interface InviteSearchProps { maxLength?: number; } -const getNicknameForShips = (groups: Groups, contacts: Rolodex) => { +const getNicknameForShips = (groups: Groups, contacts: Rolodex): readonly [string[], Map] => { const peerSet = new Set(); const nicknames = new Map(); _.forEach(groups, (group, path) => { @@ -71,7 +66,7 @@ const getNicknameForShips = (groups: Groups, contacts: Rolodex) => { return [Array.from(peerSet), nicknames] as const; }; -const Candidate = ({ title, detail, selected, onClick }) => ( +const Candidate = ({ title, detail, selected, onClick }): ReactElement => ( = { }; const shipItemSchema = Yup.string().test( - "is-patp", - "${value} is not a valid @p", + 'is-patp', + '${value} is not a valid @p', x => ob.isValidPatp(`~${x}`) ); export const shipSearchSchema = Yup.array(shipItemSchema).compact(); export const shipSearchSchemaInGroup = (members: string[]) => - Yup.array(shipItemSchema.oneOf(members, "${value} not a member of this group")).compact(); + Yup.array(shipItemSchema.oneOf(members, '${value} not a member of this group')).compact(); export function ShipSearch>( props: InviteSearchProps -) { +): ReactElement { const { id, label, caption } = props; const { values, touched, errors, initialValues, - setFieldValue, + setFieldValue } = useFormikContext(); const inputIdx = useRef(initialValues[id].length); @@ -133,7 +128,7 @@ export function ShipSearch>( const renderCandidate = useCallback( (s: string, selected: boolean, onSelect: (s: string) => void) => { - const detail = _.uniq(nicknames.get(s)).join(", "); + const detail = _.uniq(nicknames.get(s)).join(', '); const onClick = () => { onSelect(s); }; @@ -152,7 +147,7 @@ export function ShipSearch>( const onChange = (e: ChangeEvent) => { const newValue = - e.target.value?.length > 0 ? `~${deSig(e.target.value)}` : ""; + e.target.value?.length > 0 ? `~${deSig(e.target.value)}` : ''; setFieldValue(name(), newValue); }; @@ -165,7 +160,7 @@ export function ShipSearch>( const onAdd = (ship: string) => { setFieldValue(name(), ship); inputIdx.current += 1; - arrayHelpers.push(""); + arrayHelpers.push(''); }; const onRemove = (idx: number) => { @@ -196,7 +191,7 @@ export function ShipSearch>( props.maxLength ? selected.length >= props.maxLength : false } search={(s: string, t: string) => - (t || "").toLowerCase().startsWith(s.toLowerCase()) + (t || '').toLowerCase().startsWith(s.toLowerCase()) } getKey={(s: string) => s} onChange={onChange} @@ -227,7 +222,7 @@ export function ShipSearch>( ))}
0}> - {error.join(", ")} + {error.join(', ')} ); diff --git a/pkg/interface/src/views/components/StatelessAsyncAction.tsx b/pkg/interface/src/views/components/StatelessAsyncAction.tsx index be46d4de1..68ab25f63 100644 --- a/pkg/interface/src/views/components/StatelessAsyncAction.tsx +++ b/pkg/interface/src/views/components/StatelessAsyncAction.tsx @@ -1,7 +1,7 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode } from 'react'; import { useStatelessAsyncClickable } from '~/logic/lib/useStatelessAsyncClickable'; -import { LoadingSpinner, Action } from "@tlon/indigo-react"; +import { LoadingSpinner, Action } from '@tlon/indigo-react'; interface AsyncActionProps { children: ReactNode; @@ -19,7 +19,7 @@ export function StatelessAsyncAction({ }: AsyncActionProps & Parameters[0]) { const { onClick: handleClick, - buttonState: state, + buttonState: state } = useStatelessAsyncClickable(onClick, name); return ( @@ -27,16 +27,17 @@ export function StatelessAsyncAction({ height="18px" hideDisabled={!disabled} disabled={disabled || state === 'loading'} - onClick={handleClick} {...rest}> - {state === "error" ? ( - "Error" - ) : state === "loading" ? ( + onClick={handleClick} {...rest} + > + {state === 'error' ? ( + 'Error' + ) : state === 'loading' ? ( - ) : state === "success" ? ( - "Done" + ) : state === 'success' ? ( + 'Done' ) : ( children )} diff --git a/pkg/interface/src/views/components/StatelessAsyncButton.tsx b/pkg/interface/src/views/components/StatelessAsyncButton.tsx index faf933ed3..68c27d226 100644 --- a/pkg/interface/src/views/components/StatelessAsyncButton.tsx +++ b/pkg/interface/src/views/components/StatelessAsyncButton.tsx @@ -1,9 +1,8 @@ -import React, { ReactNode, useState, useEffect, useCallback } from "react"; +import React, { ReactElement, ReactNode } from 'react'; -import { Button, LoadingSpinner } from "@tlon/indigo-react"; -import { useFormikContext } from "formik"; +import { Button, LoadingSpinner } from '@tlon/indigo-react'; -import { useStatelessAsyncClickable } from "~/logic/lib/useStatelessAsyncClickable"; +import { useStatelessAsyncClickable } from '~/logic/lib/useStatelessAsyncClickable'; interface AsyncButtonProps { children: ReactNode; @@ -14,33 +13,33 @@ interface AsyncButtonProps { export function StatelessAsyncButton({ children, onClick, - name = "", + name = '', disabled = false, ...rest -}: AsyncButtonProps & Parameters[0]) { +}: AsyncButtonProps & Parameters[0]): ReactElement { const { onClick: handleClick, - buttonState: state, + buttonState: state } = useStatelessAsyncClickable(onClick, name); return ( - + { isAdmin && ( diff --git a/pkg/interface/src/views/landscape/components/GroupSettings/Personal.tsx b/pkg/interface/src/views/landscape/components/GroupSettings/Personal.tsx index 5d262d6b2..8f14a4ba6 100644 --- a/pkg/interface/src/views/landscape/components/GroupSettings/Personal.tsx +++ b/pkg/interface/src/views/landscape/components/GroupSettings/Personal.tsx @@ -1,41 +1,22 @@ -import React, { useCallback } from "react"; +import React from 'react'; -import { AsyncButton } from "~/views/components/AsyncButton"; -import * as Yup from "yup"; import { - Box, - ManagedTextInputField as Input, - ManagedToggleSwitchField as Toggle, Col, Label, - Button, - LoadingSpinner, BaseLabel, - Anchor, BaseAnchor -} from "@tlon/indigo-react"; -import { Group, GroupPolicy } from "~/types/group-update"; -import { Enc } from "~/types/noun"; -import { Association } from "~/types/metadata-update"; -import GlobalApi from "~/logic/api/global"; -import { resourceFromPath, roleForShip } from "~/logic/lib/group"; -import { StatelessAsyncButton } from "~/views/components/StatelessAsyncButton"; -import { ColorInput } from "~/views/components/ColorInput"; -import { useHistory } from "react-router-dom"; - -import { uxToHex } from "~/logic/lib/util"; -import { FormikOnBlur } from "~/views/components/FormikOnBlur"; -import {GroupNotificationsConfig} from "~/types"; -import {StatelessAsyncToggle} from "~/views/components/StatelessAsyncToggle"; - +} from '@tlon/indigo-react'; +import { GroupNotificationsConfig } from '@urbit/api'; +import { Association } from '@urbit/api/metadata'; +import GlobalApi from '~/logic/api/global'; +import { StatelessAsyncToggle } from '~/views/components/StatelessAsyncToggle'; export function GroupPersonalSettings(props: { api: GlobalApi; association: Association; notificationsGroupConfig: GroupNotificationsConfig; }) { - const groupPath = props.association.group; const watching = props.notificationsGroupConfig.findIndex(g => g === groupPath) !== -1; @@ -48,7 +29,7 @@ export function GroupPersonalSettings(props: { return ( Group Notifications - ) { +export function GroupSummary(props: GroupSummaryProps & PropFunc): ReactElement { const { channelCount, memberCount, metadata, resource, children, ...rest } = props; const anchorRef = useRef(null); useTutorialModal( - "group-desc", + 'group-desc', resource === `/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`, anchorRef.current ); @@ -39,7 +39,8 @@ export function GroupSummary(props: GroupSummaryProps & PropFunc) { fontSize="1" textOverflow="ellipsis" whiteSpace="nowrap" - overflow="hidden">{metadata.title} + overflow="hidden" + >{metadata.title} {memberCount} participants @@ -57,7 +58,8 @@ export function GroupSummary(props: GroupSummaryProps & PropFunc) { width="100%" fontSize="1" textOverflow="ellipsis" - overflow="hidden"> + overflow="hidden" + > {metadata.description} } diff --git a/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx b/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx index 769963f98..91926e00f 100644 --- a/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx +++ b/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx @@ -9,11 +9,11 @@ import { import { uxToHex } from '~/logic/lib/util'; import { Link } from 'react-router-dom'; -import { Associations } from '~/types/metadata-update'; +import { Associations } from '@urbit/api/metadata'; import { Dropdown } from '~/views/components/Dropdown'; -import { Workspace } from '~/types'; import { getTitleFromWorkspace } from '~/logic/lib/workspace'; -import {MetadataIcon} from './MetadataIcon'; +import { MetadataIcon } from './MetadataIcon'; +import { Workspace } from '~/types/workspace'; const GroupSwitcherItem = ({ to, children, bottom = false, ...rest }) => ( @@ -93,7 +93,8 @@ export function GroupSwitcher(props: { top="0px" pl='3' borderBottom='1px solid' - borderColor='washedGray'> + borderColor='washedGray' + > ( - "recent-groups", + 'recent-groups', [] ); useEffect(() => { - if (workspace.type !== "group") { + if (workspace.type !== 'group') { return; } - setRecentGroups((gs) => _.uniq([workspace.group, ...gs])); + setRecentGroups(gs => _.uniq([workspace.group, ...gs])); }, [workspace]); if (!(associations && (groupPath ? groupPath in groups : true))) { @@ -75,7 +74,7 @@ export function GroupsPane(props: GroupsPaneProps) { {...routeProps} baseUrl={baseUrl} - />)} + />)} - ) + ); return ( { const { app, host, name } = routeProps.match.params as Record< string, @@ -100,7 +99,7 @@ export function GroupsPane(props: GroupsPaneProps) { const appName = app as AppName; const resource = `/ship/${host}/${name}`; - const association = associations.graph[resource] + const association = associations.graph[resource]; const resourceUrl = `${baseUrl}/resource/${app}${resource}`; if (!association) { @@ -128,7 +127,7 @@ export function GroupsPane(props: GroupsPaneProps) { }} /> { const { app, host, name } = routeProps.match.params; const appPath = `/ship/${host}/${name}`; @@ -168,7 +167,7 @@ export function GroupsPane(props: GroupsPaneProps) { }} /> { const newUrl = `${baseUrl}/new`; return ( @@ -189,10 +188,10 @@ export function GroupsPane(props: GroupsPaneProps) { }} /> { const hasDescription = groupAssociation?.metadata?.description; - const channelCount = Object.keys(props?.associations?.graph ?? {}).filter(e => { + const channelCount = Object.keys(props?.associations?.graph ?? {}).filter((e) => { return props?.associations?.graph?.[e]?.['group'] === groupPath; }).length; let summary: ReactNode; @@ -203,13 +202,11 @@ export function GroupsPane(props: GroupsPaneProps) { channelCount={channelCount} metadata={groupAssociation.metadata} resource={groupAssociation.group} - /> + />; } else { summary = ( Create or select a channel to get started ); - - } const title = groupAssociation?.metadata?.title ?? 'Landscape'; return ( @@ -221,7 +218,7 @@ export function GroupsPane(props: GroupsPaneProps) { {summary} diff --git a/pkg/interface/src/views/landscape/components/InvitePopover.tsx b/pkg/interface/src/views/landscape/components/InvitePopover.tsx index 72dc13d93..de02b7588 100644 --- a/pkg/interface/src/views/landscape/components/InvitePopover.tsx +++ b/pkg/interface/src/views/landscape/components/InvitePopover.tsx @@ -1,26 +1,26 @@ -import React, { useCallback, useRef, useMemo } from "react"; +import React, { useCallback, useRef } from 'react'; import _ from 'lodash'; -import { Switch, Route, useHistory } from "react-router-dom"; -import { Formik, Form } from "formik"; +import { Switch, Route, useHistory } from 'react-router-dom'; +import { Formik, Form } from 'formik'; import * as Yup from 'yup'; import { ManagedTextInputField as Input, Box, Text, Col, - Button, Row -} from "@tlon/indigo-react"; +} from '@tlon/indigo-react'; -import { ShipSearch } from "~/views/components/ShipSearch"; -import { Association } from "~/types/metadata-update"; -import { AsyncButton } from "~/views/components/AsyncButton"; -import { useOutsideClick } from "~/logic/lib/useOutsideClick"; -import { FormError } from "~/views/components/FormError"; -import { resourceFromPath } from "~/logic/lib/group"; -import GlobalApi from "~/logic/api/global"; -import { Groups, Rolodex, Workspace } from "~/types"; -import { deSig } from "~/logic/lib/util"; +import { ShipSearch } from '~/views/components/ShipSearch'; +import { Association } from '@urbit/api/metadata'; +import { AsyncButton } from '~/views/components/AsyncButton'; +import { useOutsideClick } from '~/logic/lib/useOutsideClick'; +import { FormError } from '~/views/components/FormError'; +import { resourceFromPath } from '~/logic/lib/group'; +import GlobalApi from '~/logic/api/global'; +import { Groups, Rolodex } from '@urbit/api'; +import { deSig } from '~/logic/lib/util'; +import { Workspace } from '~/types/workspace'; interface InvitePopoverProps { baseUrl: string; @@ -38,15 +38,15 @@ interface FormSchema { } const formSchema = Yup.object({ - emails: Yup.array(Yup.string().email("Invalid email")), - ships: Yup.array(Yup.string()).min(1, "Must invite at least one ship") + emails: Yup.array(Yup.string().email('Invalid email')), + ships: Yup.array(Yup.string()).min(1, 'Must invite at least one ship') }); export function InvitePopover(props: InvitePopoverProps) { const { baseUrl, api, association } = props; const relativePath = (p: string) => baseUrl + p; - const { title } = association?.metadata || ""; + const { title } = association?.metadata || ''; const innerRef = useRef(null); const history = useHistory(); @@ -75,10 +75,9 @@ export function InvitePopover(props: InvitePopoverProps) { const initialValues: FormSchema = { ships: [], emails: [], description: '' }; - return ( - + { + .required('Must provide group to join') + .test('is-valid', 'Invalid group', (group: string | null | undefined) => { if (!group) { return false; } - const [patp, name] = group.split("/"); + const [patp, name] = group.split('/'); return urbitOb.isValidPatp(patp) && name.length > 0; - }), + }) }); interface FormSchema { @@ -60,17 +58,16 @@ function Autojoin(props: { autojoin: string | null }) { return null; } -export function JoinGroup(props: JoinGroupProps) { +export function JoinGroup(props: JoinGroupProps): ReactElement { const { api, autojoin, associations, groups } = props; const history = useHistory(); const initialValues: FormSchema = { - group: autojoin || "", + group: autojoin || '' }; const [preview, setPreview] = useState< MetadataUpdatePreview | string | null >(null); - const waiter = useWaitForProps(props, _.isString(preview) ? 1 : 5000); const onConfirm = useCallback(async (group: string) => { @@ -78,9 +75,9 @@ export function JoinGroup(props: JoinGroupProps) { await api.groups.join(ship, name); try { await waiter((p: JoinGroupProps) => { - return group in p.groups && + return group in p.groups && (group in (p.associations?.graph ?? {}) - || group in (p.associations?.groups ?? {})) + || group in (p.associations?.groups ?? {})); }); if(props.groups?.[group]?.hidden) { @@ -98,7 +95,7 @@ export function JoinGroup(props: JoinGroupProps) { const onSubmit = useCallback( async (values: FormSchema, actions: FormikHelpers) => { - const [ship, name] = values.group.split("/"); + const [ship, name] = values.group.split('/'); const path = `/ship/${ship}/${name}`; // skip if it's unmanaged try { @@ -107,13 +104,13 @@ export function JoinGroup(props: JoinGroupProps) { setPreview(prev); } catch (e) { if (!(e instanceof Error)) { - actions.setStatus({ error: "Unknown error" }); - } else if (e.message === "no-permissions") { + actions.setStatus({ error: 'Unknown error' }); + } else if (e.message === 'no-permissions') { actions.setStatus({ error: - "Unable to join group, you do not have the correct permissions", + 'Unable to join group, you do not have the correct permissions' }); - } else if (e.message === "offline") { + } else if (e.message === 'offline') { setPreview(path); } } @@ -131,8 +128,8 @@ export function JoinGroup(props: JoinGroupProps) { {_.isString(preview) ? ( The host appears to be offline. Join anyway? - onConfirm(preview)} > @@ -173,7 +170,7 @@ export function JoinGroup(props: JoinGroupProps) { )} - onConfirm(preview.group)} @@ -188,7 +185,7 @@ export function JoinGroup(props: JoinGroupProps) { initialValues={initialValues} onSubmit={onSubmit} > - + & { metadata: Metadata; diff --git a/pkg/interface/src/views/landscape/components/NewChannel.tsx b/pkg/interface/src/views/landscape/components/NewChannel.tsx index deff84835..456029910 100644 --- a/pkg/interface/src/views/landscape/components/NewChannel.tsx +++ b/pkg/interface/src/views/landscape/components/NewChannel.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; import { Box, ManagedTextInputField as Input, @@ -14,13 +14,14 @@ import { FormError } from '~/views/components/FormError'; import { RouteComponentProps } from 'react-router-dom'; import { stringToSymbol, parentPath, deSig } from '~/logic/lib/util'; import { resourceFromPath } from '~/logic/lib/group'; -import { Associations } from '~/types/metadata-update'; +import { Associations } from '@urbit/api/metadata'; import { useWaitForProps } from '~/logic/lib/useWaitForProps'; -import { Groups } from '~/types/group-update'; +import { Groups } from '@urbit/api/groups'; import { ShipSearch, shipSearchSchemaInGroup, shipSearchSchema } from '~/views/components/ShipSearch'; -import { Rolodex, Workspace } from '~/types'; +import { Rolodex } from '@urbit/api'; import { IconRadio } from '~/views/components/IconRadio'; import { ChannelWriteFieldSchema, ChannelWritePerms } from './ChannelWritePerms'; +import { Workspace } from '~/types/workspace'; type FormSchema = { name: string; @@ -47,7 +48,7 @@ interface NewChannelProps { workspace: Workspace; } -export function NewChannel(props: NewChannelProps & RouteComponentProps) { +export function NewChannel(props: NewChannelProps & RouteComponentProps): ReactElement { const { history, api, group, workspace, groups } = props; const waiter = useWaitForProps(props, 5000); @@ -59,7 +60,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { : ''); try { let { description, moduleType, ships, writers } = values; - ships = ships.filter(e => e !== ""); + ships = ships.filter(e => e !== ''); if (workspace?.type === 'messages' && ships.length === 1) { return history.push(`/~landscape/dm/${deSig(ships[0])}`); } @@ -138,7 +139,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { maxWidth="348px" gapY="4" > - + Channel Type = isPrivate ? { invite: { - pending: [], - }, + pending: [] + } } : { open: { banRanks: [], - banned: [], - }, + banned: [] + } }; await api.groups.create(name, policy, title, description); const path = `/ship/~${window.ship}/${name}`; diff --git a/pkg/interface/src/views/landscape/components/Participants.tsx b/pkg/interface/src/views/landscape/components/Participants.tsx index dabea1845..d39a8bace 100644 --- a/pkg/interface/src/views/landscape/components/Participants.tsx +++ b/pkg/interface/src/views/landscape/components/Participants.tsx @@ -2,7 +2,6 @@ import React, { useState, useMemo, useCallback, - SyntheticEvent, ChangeEvent } from 'react'; import { @@ -11,26 +10,25 @@ import { Row, Text, Icon, - Center, - Button, Action, StatelessTextInput as Input } from '@tlon/indigo-react'; import _ from 'lodash'; import f from 'lodash/fp'; import VisibilitySensor from 'react-visibility-sensor'; +import styled from 'styled-components'; +import { Link } from 'react-router-dom'; + +import { Contact, Contacts } from '@urbit/api/contacts'; +import { Group, RoleTags } from '@urbit/api/groups'; +import { Association } from '@urbit/api/metadata'; -import { Contact, Contacts } from '~/types/contact-update'; import { Sigil } from '~/logic/lib/sigil'; import { cite, uxToHex } from '~/logic/lib/util'; -import { Group, RoleTags } from '~/types/group-update'; import { roleForShip, resourceFromPath } from '~/logic/lib/group'; -import { Association } from '~/types/metadata-update'; -import { useHistory, Link } from 'react-router-dom'; import { Dropdown } from '~/views/components/Dropdown'; import GlobalApi from '~/logic/api/global'; import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction'; -import styled from 'styled-components'; import useLocalState from '~/logic/state/local'; const TruncText = styled(Box)` @@ -105,7 +103,7 @@ export function Participants(props: { group: Group; association: Association; api: GlobalApi; -}) { +}): ReactElement { const { api } = props; const tabFilters: Record< ParticipantsTabId, diff --git a/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx b/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx index 4c18d93c0..f457428bd 100644 --- a/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx +++ b/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx @@ -1,20 +1,21 @@ -import React, { useRef, useCallback } from "react"; -import { Route, Switch, RouteComponentProps, Link } from "react-router-dom"; -import { Box, Row, Col, Icon, Text } from "@tlon/indigo-react"; -import { HoverBoxLink } from "~/views/components/HoverBox"; -import { Contacts, Contact } from "~/types/contact-update"; -import { Group } from "~/types/group-update"; -import { Association } from "~/types/metadata-update"; -import GlobalApi from "~/logic/api/global"; -import { GroupNotificationsConfig, S3State, Associations } from "~/types"; +import React, { useRef, useCallback, ReactElement } from 'react'; +import { Route, Switch, RouteComponentProps, Link } from 'react-router-dom'; +import { Box, Col, Text } from '@tlon/indigo-react'; -import { GroupSettings } from "./GroupSettings/GroupSettings"; -import { Participants } from "./Participants"; -import {useHashLink} from "~/logic/lib/useHashLink"; -import {DeleteGroup} from "./DeleteGroup"; -import {resourceFromPath} from "~/logic/lib/group"; -import {ModalOverlay} from "~/views/components/ModalOverlay"; -import { SidebarItem } from "~/views/landscape/components/SidebarItem"; +import { GroupNotificationsConfig, Associations } from '@urbit/api'; +import { Contacts, Contact } from '@urbit/api/contacts'; +import { Group } from '@urbit/api/groups'; +import { Association } from '@urbit/api/metadata'; + +import GlobalApi from '~/logic/api/global'; +import { GroupSettings } from './GroupSettings/GroupSettings'; +import { Participants } from './Participants'; +import { useHashLink } from '~/logic/lib/useHashLink'; +import { DeleteGroup } from './DeleteGroup'; +import { resourceFromPath } from '~/logic/lib/group'; +import { ModalOverlay } from '~/views/components/ModalOverlay'; +import { SidebarItem } from '~/views/landscape/components/SidebarItem'; +import { S3State } from '~/types'; export function PopoverRoutes( props: { @@ -28,7 +29,7 @@ export function PopoverRoutes( notificationsGroupConfig: GroupNotificationsConfig; rootIdentity: Contact; } & RouteComponentProps -) { +): ReactElement { const relativeUrl = (url: string) => `${props.baseUrl}/popover${url}`; const innerRef = useRef(null); @@ -47,7 +48,7 @@ export function PopoverRoutes( return ( { const { view } = routeProps.match.params; return ( @@ -64,13 +65,13 @@ export function PopoverRoutes( > @@ -79,14 +80,14 @@ export function PopoverRoutes( Group {groupSize} { admin && ( @@ -96,12 +97,12 @@ export function PopoverRoutes( @@ -110,16 +111,16 @@ export function PopoverRoutes( - - {"<- Back"} + + {'<- Back'} - {view === "settings" && ( + {view === 'settings' && ( )} - {view === "participants" && ( + {view === 'participants' && ( @@ -48,11 +39,11 @@ export function Resource(props: ResourceProps) { - {app === "chat" ? ( + {app === 'chat' ? ( - ) : app === "publish" ? ( + ) : app === 'publish' ? ( ) : ( @@ -60,7 +51,7 @@ export function Resource(props: ResourceProps) { { return ( diff --git a/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx b/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx index 63bc35f86..43750dd5d 100644 --- a/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx +++ b/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx @@ -1,21 +1,15 @@ -import React, { ReactNode } from "react"; -import { Row, Icon, Box, Col, Text } from "@tlon/indigo-react"; -import styled from "styled-components"; -import { Link } from "react-router-dom"; - -import { ChatResource } from "~/views/apps/chat/ChatResource"; -import { PublishResource } from "~/views/apps/publish/PublishResource"; - -import RichText from "~/views/components/RichText"; - -import { Association } from "~/types/metadata-update"; -import GlobalApi from "~/logic/api/global"; -import { RouteComponentProps, Route, Switch } from "react-router-dom"; -import { ChannelSettings } from "./ChannelSettings"; -import { ChannelMenu } from "./ChannelMenu"; -import { NotificationGraphConfig, Groups } from "~/types"; -import {isWriter} from "~/logic/lib/group"; +import React, { ReactElement, ReactNode } from 'react'; +import { Icon, Box, Col, Text } from '@tlon/indigo-react'; +import styled from 'styled-components'; +import { Link } from 'react-router-dom'; import urbitOb from 'urbit-ob'; + +import { Association } from '@urbit/api/metadata'; +import { Groups, Rolodex } from '@urbit/api'; + +import RichText from '~/views/components/RichText'; +import GlobalApi from '~/logic/api/global'; +import { isWriter } from '~/logic/lib/group'; import { getItemTitle } from '~/logic/lib/util'; const TruncatedBox = styled(Box)` @@ -26,7 +20,7 @@ const TruncatedBox = styled(Box)` type ResourceSkeletonProps = { groups: Groups; - contacts: any; + contacts: Rolodex; association: Association; api: GlobalApi; baseUrl: string; @@ -35,20 +29,20 @@ type ResourceSkeletonProps = { groupTags?: any; }; -export function ResourceSkeleton(props: ResourceSkeletonProps) { - const { association, api, baseUrl, children, atRoot, groups } = props; - const app = association?.metadata?.module || association["app-name"]; +export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement { + const { association, baseUrl, children, groups } = props; + const app = association?.metadata?.module || association['app-name']; const rid = association.resource; const group = groups[association.group]; let workspace = association.group; - if (group?.hidden && app === "chat") { - workspace = "/messages"; + if (group?.hidden && app === 'chat') { + workspace = '/messages'; } else if (group?.hidden) { - workspace = "/home"; + workspace = '/home'; } - let title = (workspace === "/messages") + let title = (workspace === '/messages') ? getItemTitle(association) : association?.metadata?.title; @@ -59,7 +53,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) { title = (props.contacts?.[title]?.nickname) ? props.contacts[title].nickname : title; } - const [, , ship, resource] = rid.split("/"); + const [, , ship, resource] = rid.split('/'); const resourcePath = (p: string) => baseUrl + p; @@ -89,10 +83,10 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) { fontSize='1' mr={3} my="1" - display={["block", "none"]} + display={['block', 'none']} flexShrink={0} > - {"<- Back"} + {'<- Back'} + minWidth={0} + > {title} - {(workspace === "/messages") ? recipient : association?.metadata?.description} + {(workspace === '/messages') ? recipient : association?.metadata?.description} diff --git a/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx b/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx index d56236ad0..fd32f998a 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx @@ -1,8 +1,8 @@ -import { useEffect, useCallback } from "react"; -import { Graphs, UnreadStats } from "~/types"; -import { SidebarItemStatus, SidebarAppConfig } from "./types"; +import { useCallback } from 'react'; +import { Graphs, UnreadStats } from '@urbit/api'; +import { SidebarAppConfig } from './types'; export function useGraphModule( graphKeys: Set, @@ -11,10 +11,10 @@ export function useGraphModule( ): SidebarAppConfig { const getStatus = useCallback( (s: string) => { - const [, , host, name] = s.split("/"); + const [, , host, name] = s.split('/'); const graphKey = `${host.slice(1)}/${name}`; if (!graphKeys.has(graphKey)) { - return "unsubscribed"; + return 'unsubscribed'; } const unreads = graphUnreads?.[s]?.['/']?.unreads; @@ -38,7 +38,6 @@ export function useGraphModule( return 0; } return 1; - }, [getStatus, graphUnreads]); return { getStatus, lastUpdated }; diff --git a/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx b/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx index 7cd0e2dcb..1ec0a3234 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useRef } from 'react'; +import React, { ReactElement, ReactNode, useRef } from 'react'; import styled from 'styled-components'; import { Col @@ -12,14 +12,14 @@ import { Groups, Invites, Rolodex -} from '~/types'; +} from '@urbit/api'; import { SidebarListHeader } from './SidebarListHeader'; import { useLocalStorageState } from '~/logic/lib/useLocalStorageState'; import { getGroupFromWorkspace } from '~/logic/lib/workspace'; import { SidebarAppConfigs } from './types'; import { SidebarList } from './SidebarList'; import { roleForShip } from '~/logic/lib/group'; -import {useTutorialModal} from '~/views/components/useTutorialModal'; +import { useTutorialModal } from '~/views/components/useTutorialModal'; const ScrollbarLessCol = styled(Col)` scrollbar-width: none !important; @@ -46,7 +46,7 @@ interface SidebarProps { workspace: Workspace; } -export function Sidebar(props: SidebarProps) { +export function Sidebar(props: SidebarProps): ReactElement { const { associations, selected, workspace } = props; const groupPath = getGroupFromWorkspace(workspace); const display = props.mobileHide ? ['none', 'flex'] : 'flex'; diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx index b42b345bb..6557edd75 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx @@ -1,26 +1,26 @@ -import React, {useRef} from "react"; -import _ from 'lodash'; - -import { Icon, Row, Box, Text, BaseImage } from "@tlon/indigo-react"; - -import { SidebarAppConfigs, SidebarItemStatus } from "./Sidebar"; -import { HoverBoxLink } from "~/views/components/HoverBox"; -import { Groups, Association } from "~/types"; -import { Sigil } from '~/logic/lib/sigil'; +import React, { ReactElement, useRef } from 'react'; import urbitOb from 'urbit-ob'; -import { getModuleIcon, getItemTitle, uxToHex } from "~/logic/lib/util"; -import {useTutorialModal} from "~/views/components/useTutorialModal"; -import {TUTORIAL_HOST, TUTORIAL_GROUP} from "~/logic/lib/tutorialModal"; + +import { Icon, Row, Box, Text, BaseImage } from '@tlon/indigo-react'; +import { Groups, Association, Rolodex } from '@urbit/api'; + +import { HoverBoxLink } from '~/views/components/HoverBox'; +import { Sigil } from '~/logic/lib/sigil'; +import { getModuleIcon, getItemTitle, uxToHex } from '~/logic/lib/util'; +import { useTutorialModal } from '~/views/components/useTutorialModal'; +import { TUTORIAL_HOST, TUTORIAL_GROUP } from '~/logic/lib/tutorialModal'; +import { SidebarAppConfigs, SidebarItemStatus } from './types'; +import { Workspace } from '~/types/workspace'; function SidebarItemIndicator(props: { status?: SidebarItemStatus }) { switch (props.status) { - case "disconnected": + case 'disconnected': return ; - case "unsubscribed": + case 'unsubscribed': return ; - case "mention": + case 'mention': return ; - case "loading": + case 'loading': return ; default: return null; @@ -30,20 +30,20 @@ function SidebarItemIndicator(props: { status?: SidebarItemStatus }) { export function SidebarItem(props: { hideUnjoined: boolean; association: Association; - contacts: any; + contacts: Rolodex; groups: Groups; path: string; selected: boolean; apps: SidebarAppConfigs; workspace: Workspace; -}) { +}): ReactElement { const { association, path, selected, apps, groups } = props; let title = getItemTitle(association); - const appName = association?.["app-name"]; + const appName = association?.['app-name']; const mod = association?.metadata?.module || appName; - const rid = association?.resource + const rid = association?.resource; const groupPath = association?.group; - const anchorRef = useRef(null) + const anchorRef = useRef(null); useTutorialModal( mod as any, groupPath === `/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`, @@ -54,11 +54,11 @@ export function SidebarItem(props: { if (!app) { return null; } - const DM = (isUnmanaged && props.workspace?.type === "messages"); + const DM = (isUnmanaged && props.workspace?.type === 'messages'); const itemStatus = app.getStatus(path); - const hasUnread = itemStatus === "unread" || itemStatus === "mention"; + const hasUnread = itemStatus === 'unread' || itemStatus === 'mention'; - const isSynced = itemStatus !== "unsubscribed"; + const isSynced = itemStatus !== 'unsubscribed'; let baseUrl = `/~landscape${groupPath}`; @@ -72,7 +72,7 @@ export function SidebarItem(props: { ? `${baseUrl}/resource/${mod}${rid}` : `${baseUrl}/join/${mod}${rid}`; - const color = selected ? "black" : isSynced ? "gray" : "lightGray"; + const color = selected ? 'black' : isSynced ? 'gray' : 'lightGray'; if (props.hideUnjoined && !isSynced) { return null; @@ -82,15 +82,15 @@ export function SidebarItem(props: { if (urbitOb.isValidPatp(title)) { if (props.contacts?.[title] && props.contacts[title].avatar) { - img = ; + img = ; } else { - img = + img = ; } if (props.contacts?.[title] && props.contacts[title].nickname) { title = props.contacts[title].nickname; } } else { - img = + img = ; } return ( @@ -125,9 +125,9 @@ export function SidebarItem(props: { overflow='hidden' width='100%' mono={urbitOb.isValidPatp(title)} - fontWeight={hasUnread ? "bold" : "regular"} - color={selected || isSynced ? "black" : "lightGray"} - style={{ textOverflow: 'ellipsis', whiteSpace: 'pre'}} + fontWeight={hasUnread ? 'bold' : 'regular'} + color={selected || isSynced ? 'black' : 'lightGray'} + style={{ textOverflow: 'ellipsis', whiteSpace: 'pre' }} > {title} diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx index 25af63bff..8264b5d30 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx @@ -1,8 +1,10 @@ -import React, { useMemo } from "react"; -import { alphabeticalOrder } from "~/logic/lib/util"; -import { Associations, AppAssociations, Workspace, Groups } from "~/types"; -import { SidebarAppConfigs, SidebarListConfig, SidebarSort } from "./types"; -import { SidebarItem } from "./SidebarItem"; +import React, { ReactElement } from 'react'; +import { Associations, AppAssociations, Groups, Rolodex } from '@urbit/api'; + +import { alphabeticalOrder } from '~/logic/lib/util'; +import { SidebarAppConfigs, SidebarListConfig, SidebarSort } from './types'; +import { SidebarItem } from './SidebarItem'; +import { Workspace } from '~/types/workspace'; function sidebarSort( associations: AppAssociations, @@ -20,8 +22,8 @@ function sidebarSort( const lastUpdated = (a: string, b: string) => { const aAssoc = associations[a]; const bAssoc = associations[b]; - const aAppName = aAssoc?.["app-name"]; - const bAppName = bAssoc?.["app-name"]; + const aAppName = aAssoc?.['app-name']; + const bAppName = bAssoc?.['app-name']; const aUpdated = apps[aAppName]?.lastUpdated(a) || 0; const bUpdated = apps[bAppName]?.lastUpdated(b) || 0; @@ -37,7 +39,7 @@ function sidebarSort( export function SidebarList(props: { apps: SidebarAppConfigs; - contacts: any; + contacts: Rolodex; config: SidebarListConfig; associations: Associations; groups: Groups; @@ -45,7 +47,7 @@ export function SidebarList(props: { group?: string; selected?: string; workspace: Workspace; -}) { +}): ReactElement { const { selected, group, config, workspace } = props; const associations = { ...props.associations.graph }; @@ -53,11 +55,11 @@ export function SidebarList(props: { .filter((a) => { const assoc = associations[a]; if (workspace?.type === 'messages') { - return (!(assoc.group in props.associations.groups) && assoc.metadata.module === "chat"); + return (!(assoc.group in props.associations.groups) && assoc.metadata.module === 'chat'); } else { return group ? assoc.group === group - : (!(assoc.group in props.associations.groups) && assoc.metadata.module !== "chat"); + : (!(assoc.group in props.associations.groups) && assoc.metadata.module !== 'chat'); } }) .sort(sidebarSort(associations, props.apps)[config.sortBy]); diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx index 66364ac66..d548ff484 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx @@ -1,5 +1,7 @@ -import React, { useCallback } from "react"; -import * as Yup from "yup"; +import React, { ReactElement, useCallback } from 'react'; +import { FormikHelpers } from 'formik'; +import { Link } from 'react-router-dom'; + import { Row, Box, @@ -7,18 +9,18 @@ import { ManagedRadioButtonField as Radio, ManagedCheckboxField as Checkbox, Col, - Text, -} from "@tlon/indigo-react"; -import { FormikOnBlur } from "~/views/components/FormikOnBlur"; -import { Dropdown } from "~/views/components/Dropdown"; -import { FormikHelpers } from "formik"; -import { SidebarListConfig, Workspace } from "./types"; -import { Link, useHistory } from 'react-router-dom'; -import { getGroupFromWorkspace } from "~/logic/lib/workspace"; -import { roleForShip } from "~/logic/lib/group"; -import {Groups, Rolodex, Associations} from "~/types"; -import { NewChannel } from "~/views/landscape/components/NewChannel"; -import GlobalApi from "~/logic/api/global"; + Text +} from '@tlon/indigo-react'; +import { Groups, Rolodex, Associations } from '@urbit/api'; + +import { FormikOnBlur } from '~/views/components/FormikOnBlur'; +import { Dropdown } from '~/views/components/Dropdown'; +import { SidebarListConfig } from './types'; +import { getGroupFromWorkspace } from '~/logic/lib/workspace'; +import { roleForShip } from '~/logic/lib/group'; +import { NewChannel } from '~/views/landscape/components/NewChannel'; +import GlobalApi from '~/logic/api/global'; +import { Workspace } from '~/types/workspace'; export function SidebarListHeader(props: { api: GlobalApi; @@ -30,9 +32,7 @@ export function SidebarListHeader(props: { selected: string; workspace: Workspace; handleSubmit: (c: SidebarListConfig) => void; -}) { - - const history = useHistory(); +}): ReactElement { const onSubmit = useCallback( (values: SidebarListConfig, actions: FormikHelpers) => { props.handleSubmit(values); @@ -46,9 +46,9 @@ export function SidebarListHeader(props: { const memberMetadata = groupPath ? props.associations.contacts?.[groupPath].metadata.vip === 'member-metadata' : false; - const isAdmin = memberMetadata || (role === "admin") || (props.workspace?.type === 'home') || (props.workspace?.type === "messages"); + const isAdmin = memberMetadata || (role === 'admin') || (props.workspace?.type === 'home') || (props.workspace?.type === 'messages'); - const noun = (props.workspace?.type === "messages") ? "Messages" : "Channels"; + const noun = (props.workspace?.type === 'messages') ? 'Messages' : 'Channels'; return ( - {props.workspace?.type === "messages" + {props.workspace?.type === 'messages' ? ( } > - + ) : ( - + : `/~landscape/${props.workspace?.type}/new`} + > + ) } @@ -111,7 +112,7 @@ export function SidebarListHeader(props: { flexShrink='0' width="auto" alignY="top" - alignX={["right", "left"]} + alignX={['right', 'left']} options={ diff --git a/pkg/interface/src/views/landscape/components/Sidebar/types.ts b/pkg/interface/src/views/landscape/components/Sidebar/types.ts index 7cdc970e0..56ecda551 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/types.ts +++ b/pkg/interface/src/views/landscape/components/Sidebar/types.ts @@ -1,11 +1,11 @@ export type SidebarItemStatus = - | "unread" - | "mention" - | "unsubscribed" - | "disconnected" - | "loading"; + | 'unread' + | 'mention' + | 'unsubscribed' + | 'disconnected' + | 'loading'; -export type SidebarSort = "asc" | "lastUpdated"; +export type SidebarSort = 'asc' | 'lastUpdated'; export interface SidebarListConfig { sortBy: SidebarSort; @@ -18,5 +18,5 @@ export interface SidebarAppConfig { } export type SidebarAppConfigs = { - [a in "chat" | "link" | "publish"]: SidebarAppConfig; + [a in 'chat' | 'link' | 'publish']: SidebarAppConfig; }; diff --git a/pkg/interface/src/views/landscape/components/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/SidebarItem.tsx index 798d31e3e..71aea2a02 100644 --- a/pkg/interface/src/views/landscape/components/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/SidebarItem.tsx @@ -1,8 +1,8 @@ -import React from "react"; -import { Row, Icon, Text } from "@tlon/indigo-react"; +import React from 'react'; +import { Row, Icon, Text } from '@tlon/indigo-react'; -import { IconRef, PropFunc } from "~/types/util"; -import { HoverBoxLink } from "~/views/components/HoverBox"; +import { IconRef, PropFunc } from '~/types/util'; +import { HoverBoxLink } from '~/views/components/HoverBox'; interface SidebarItemProps { selected?: boolean; @@ -11,17 +11,17 @@ interface SidebarItemProps { to: string; color?: string; children?: JSX.Element; -} +} export const SidebarItem = ({ icon, text, to, selected = false, - color = "black", + color = 'black', children, ...rest -}: SidebarItemProps & PropFunc) => { +}: SidebarItemProps & PropFunc): ReactElement => { return ( ; graphs: Graphs; linkListening: Set; - links: LinkCollections; - notebooks: Notebooks; invites: Invites; selected?: string; selectedApp?: AppName; @@ -33,10 +28,10 @@ interface SkeletonProps { subscription: GlobalSubscription; includeUnmanaged: boolean; workspace: Workspace; - unreads: any; + unreads: unknown; } -export function Skeleton(props: SkeletonProps) { +export function Skeleton(props: SkeletonProps): ReactElement { const graphConfig = useGraphModule(props.graphKeys, props.graphs, props.unreads.graph); const config = useMemo( () => ({ diff --git a/pkg/interface/src/views/landscape/components/TutorialModal.tsx b/pkg/interface/src/views/landscape/components/TutorialModal.tsx index 350287fc8..aa1c67618 100644 --- a/pkg/interface/src/views/landscape/components/TutorialModal.tsx +++ b/pkg/interface/src/views/landscape/components/TutorialModal.tsx @@ -1,11 +1,11 @@ -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback } from 'react'; import _ from 'lodash'; -import { Box, Col, Row, Button, Text, Icon, Action } from "@tlon/indigo-react"; -import { useHistory } from "react-router-dom"; -import { TutorialProgress, tutorialProgress as progress } from "~/types"; +import { Box, Col, Row, Button, Text, Icon } from '@tlon/indigo-react'; +import { useHistory } from 'react-router-dom'; +import { TutorialProgress, tutorialProgress as progress } from '~/types'; -import { Portal } from "~/views/components/Portal"; -import useLocalState, { selectLocalState } from "~/logic/state/local"; +import { Portal } from '~/views/components/Portal'; +import useLocalState, { selectLocalState } from '~/logic/state/local'; import { progressDetails, MODAL_HEIGHT_PX, @@ -14,21 +14,21 @@ import { MODAL_HEIGHT, TUTORIAL_HOST, TUTORIAL_GROUP, - getTrianglePosition, -} from "~/logic/lib/tutorialModal"; -import { getRelativePosition } from "~/logic/lib/relativePosition"; -import { StatelessAsyncButton } from "~/views/components/StatelessAsyncButton"; -import GlobalApi from "~/logic/api/global"; -import {Triangle} from "~/views/components/Triangle"; -import {ModalOverlay} from "~/views/components/ModalOverlay"; + getTrianglePosition +} from '~/logic/lib/tutorialModal'; +import { getRelativePosition } from '~/logic/lib/relativePosition'; +import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton'; +import GlobalApi from '~/logic/api/global'; +import { Triangle } from '~/views/components/Triangle'; +import { ModalOverlay } from '~/views/components/ModalOverlay'; const localSelector = selectLocalState([ - "tutorialProgress", - "nextTutStep", - "prevTutStep", - "tutorialRef", - "hideTutorial", - "set" + 'tutorialProgress', + 'nextTutStep', + 'prevTutStep', + 'tutorialRef', + 'hideTutorial', + 'set' ]); export function TutorialModal(props: { api: GlobalApi }) { @@ -47,7 +47,7 @@ export function TutorialModal(props: { api: GlobalApi }) { alignX, alignY, offsetX, - offsetY, + offsetY } = progressDetails[tutorialProgress]; const [coords, setCoords] = useState({}); @@ -56,7 +56,7 @@ export function TutorialModal(props: { api: GlobalApi }) { const history = useHistory(); const next = useCallback( () => { - const idx = progress.findIndex((p) => p === tutorialProgress); + const idx = progress.findIndex(p => p === tutorialProgress); const { url } = progressDetails[progress[idx + 1]]; nextTutStep(); history.push(url); @@ -64,7 +64,7 @@ export function TutorialModal(props: { api: GlobalApi }) { [nextTutStep, history, tutorialProgress, setCoords] ); const prev = useCallback(() => { - const idx = progress.findIndex((p) => p === tutorialProgress); + const idx = progress.findIndex(p => p === tutorialProgress); prevTutStep(); history.push(progressDetails[progress[idx - 1]].url); }, [prevTutStep, history, tutorialProgress]); @@ -94,7 +94,6 @@ export function TutorialModal(props: { api: GlobalApi }) { setCoords(withMobile); } else { setCoords({}); - } }, [tutorialRef]); @@ -115,12 +114,12 @@ export function TutorialModal(props: { api: GlobalApi }) { await props.api.groups.leaveGroup(TUTORIAL_HOST, TUTORIAL_GROUP); }, [props.api]); - const progressIdx = progress.findIndex((p) => p === tutorialProgress); + const progressIdx = progress.findIndex(p => p === tutorialProgress); useEffect(() => { if ( - tutorialProgress !== "hidden" && - tutorialProgress !== "done" && + tutorialProgress !== 'hidden' && + tutorialProgress !== 'done' && tutorialRef ) { const interval = setInterval(updatePos, 100); @@ -164,7 +163,7 @@ export function TutorialModal(props: { api: GlobalApi }) { ); } - if (tutorialProgress === "hidden") { + if (tutorialProgress === 'hidden') { return null; } @@ -191,11 +190,9 @@ export function TutorialModal(props: { api: GlobalApi }) { - ) - + ); } - if(Object.keys(coords).length === 0) { return null; } @@ -208,7 +205,7 @@ export function TutorialModal(props: { api: GlobalApi }) { bg="white" zIndex={50} height={MODAL_HEIGHT_PX} - width={["100%", MODAL_WIDTH_PX]} + width={['100%', MODAL_WIDTH_PX]} borderRadius="2" > - - + - + {description} { progressIdx > 1 && ( diff --git a/pkg/interface/src/views/landscape/index.tsx b/pkg/interface/src/views/landscape/index.tsx index b04e3892d..92e7aeade 100644 --- a/pkg/interface/src/views/landscape/index.tsx +++ b/pkg/interface/src/views/landscape/index.tsx @@ -1,14 +1,13 @@ -import React, { Component, useEffect, useCallback } from 'react'; +import React, { Component, useEffect, useCallback, ReactElement } from 'react'; import { Route, Switch, RouteComponentProps } from 'react-router-dom'; import Helmet from 'react-helmet'; import './css/custom.css'; -import { PatpNoSig } from '~/types/noun'; +import { PatpNoSig } from '@urbit/api'; import GlobalApi from '~/logic/api/global'; import { StoreState } from '~/logic/store/type'; import { GroupsPane } from './components/GroupsPane'; -import { Workspace } from '~/types'; import { NewGroup } from './components/NewGroup'; import { JoinGroup } from './components/JoinGroup'; @@ -16,7 +15,8 @@ import { cite } from '~/logic/lib/util'; import { Body } from '../components/Body'; import { Box } from '@tlon/indigo-react'; import { Loading } from '../components/Loading'; - +import { Workspace } from '~/types/workspace'; +import GlobalSubscription from '~/logic/subscription/global'; type LandscapeProps = StoreState & { ship: PatpNoSig; @@ -24,7 +24,7 @@ type LandscapeProps = StoreState & { subscription: GlobalSubscription; } -export function DMRedirect(props: LandscapeProps & RouteComponentProps & { ship: string; }) { +export function DMRedirect(props: LandscapeProps & RouteComponentProps & { ship: string; }): ReactElement { const { ship, api, history, graphKeys } = props; const goToGraph = useCallback((graph: string) => { history.push(`/~landscape/messages/resource/chat/ship/~${graph}`); @@ -47,7 +47,6 @@ export function DMRedirect(props: LandscapeProps & RouteComponentProps & { ship: const aud = ship !== window.ship ? [`~${ship}`] : []; const title = `${cite(window.ship)} <-> ${cite(ship)}`; - api.graph.createUnmanagedGraph( `dm--${ship}`, title, @@ -57,22 +56,20 @@ export function DMRedirect(props: LandscapeProps & RouteComponentProps & { ship: ).then(() => { goToGraph(station); }); - }, []); return ( ); - } -export default class Landscape extends Component { - componentDidMount() { +export default class Landscape extends Component> { + componentDidMount(): void { this.props.subscription.startApp('groups'); this.props.subscription.startApp('graph'); } - render() { + render(): ReactElement { const { props } = this; return ( @@ -82,7 +79,7 @@ export default class Landscape extends Component { { + render={(routeProps) => { const { host, name @@ -93,10 +90,11 @@ export default class Landscape extends Component { return ( - ) - }}/> + ); + }} + /> { + render={() => { const ws: Workspace = { type: 'home' }; return ( @@ -104,7 +102,7 @@ export default class Landscape extends Component { }} /> { + render={() => { const ws: Workspace = { type: 'messages' }; return ( @@ -112,7 +110,7 @@ export default class Landscape extends Component { }} /> { + render={(routeProps) => { return ( @@ -128,13 +126,13 @@ export default class Landscape extends Component { }} /> { + render={(routeProps) => { const { ship } = routeProps.match.params; - return + return ; }} /> { + render={(routeProps) => { const { ship, name } = routeProps.match.params; const autojoin = ship && name ? `${ship}/${name}` : null; return (