mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-09-20 15:08:34 +03:00
interface: Converted all to typescript, fixed auto-fixable problems
This commit is contained in:
parent
f54410c3fd
commit
76bef0ba00
@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: "@urbit"
|
extends: '@urbit'
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import './wdyr';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import './register-sw';
|
import './register-sw';
|
||||||
|
|
||||||
import App from './views/App';
|
import App from './views/App';
|
||||||
|
import './wdyr';
|
||||||
|
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
ReactDOM.render(<App />, document.getElementById('root'));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { Path, Patp } from '@urbit/api';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Patp, Path } from '@urbit/api';
|
|
||||||
import BaseStore from '../store/base';
|
import BaseStore from '../store/base';
|
||||||
|
|
||||||
export default class BaseApi<S extends object = {}> {
|
export default class BaseApi<S extends object = {}> {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import BaseApi from './base';
|
|
||||||
import { StoreState } from '../store/type';
|
|
||||||
import { Patp } from '@urbit/api';
|
import { Patp } from '@urbit/api';
|
||||||
import { ContactEditField } from '@urbit/api/contacts';
|
import { ContactEditField } from '@urbit/api/contacts';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class ContactsApi extends BaseApi<StoreState> {
|
export default class ContactsApi extends BaseApi<StoreState> {
|
||||||
add(ship: Patp, contact: any) {
|
add(ship: Patp, contact: any) {
|
||||||
@ -78,17 +78,17 @@ export default class ContactsApi extends BaseApi<StoreState> {
|
|||||||
return _.compact(
|
return _.compact(
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
ships.map(
|
ships.map(
|
||||||
async s => {
|
async (s) => {
|
||||||
const ship = `~${s}`;
|
const ship = `~${s}`;
|
||||||
if(s === window.ship) {
|
if(s === window.ship) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
const allowed = await this.fetchIsAllowed(
|
const allowed = await this.fetchIsAllowed(
|
||||||
`~${window.ship}`,
|
`~${window.ship}`,
|
||||||
'personal',
|
'personal',
|
||||||
ship,
|
ship,
|
||||||
true
|
true
|
||||||
)
|
);
|
||||||
return allowed ? null : ship;
|
return allowed ? null : ship;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
|
import type { StoreState } from '../store/type';
|
||||||
import BaseApi from './base';
|
import BaseApi from './base';
|
||||||
import type {StoreState} from '../store/type';
|
|
||||||
import type {GcpToken} from '../../types/gcp-state';
|
|
||||||
|
|
||||||
|
|
||||||
export default class GcpApi extends BaseApi<StoreState> {
|
export default class GcpApi extends BaseApi<StoreState> {
|
||||||
// Does not touch the store; use the value manually.
|
// Does not touch the store; use the value manually.
|
||||||
@ -18,4 +16,4 @@ export default class GcpApi extends BaseApi<StoreState> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { Patp } from '@urbit/api';
|
import { Patp } from '@urbit/api';
|
||||||
import BaseApi from './base';
|
|
||||||
import { StoreState } from '../store/type';
|
|
||||||
import GlobalStore from '../store/store';
|
import GlobalStore from '../store/store';
|
||||||
import LocalApi from './local';
|
import { StoreState } from '../store/type';
|
||||||
import InviteApi from './invite';
|
import BaseApi from './base';
|
||||||
import MetadataApi from './metadata';
|
|
||||||
import ContactsApi from './contacts';
|
import ContactsApi from './contacts';
|
||||||
import GroupsApi from './groups';
|
|
||||||
import LaunchApi from './launch';
|
|
||||||
import GraphApi from './graph';
|
|
||||||
import S3Api from './s3';
|
|
||||||
import GcpApi from './gcp';
|
import GcpApi from './gcp';
|
||||||
|
import GraphApi from './graph';
|
||||||
|
import GroupsApi from './groups';
|
||||||
import { HarkApi } from './hark';
|
import { HarkApi } from './hark';
|
||||||
|
import InviteApi from './invite';
|
||||||
|
import LaunchApi from './launch';
|
||||||
|
import LocalApi from './local';
|
||||||
|
import MetadataApi from './metadata';
|
||||||
|
import S3Api from './s3';
|
||||||
import SettingsApi from './settings';
|
import SettingsApi from './settings';
|
||||||
|
|
||||||
export default class GlobalApi extends BaseApi<StoreState> {
|
export default class GlobalApi extends BaseApi<StoreState> {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import BaseApi from './base';
|
import { Content, Enc, GraphNode, GroupPolicy, Path, Patp, Post, Resource } from '@urbit/api';
|
||||||
import { StoreState } from '../store/type';
|
|
||||||
import { Patp, Path, Resource } from '@urbit/api';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { decToUd, deSig, resourceAsPath, unixToDa } from '~/logic/lib/util';
|
||||||
import { makeResource, resourceFromPath } from '../lib/group';
|
import { makeResource, resourceFromPath } from '../lib/group';
|
||||||
import { GroupPolicy, Enc, Post, Content, GraphNode } from '@urbit/api';
|
import { StoreState } from '../store/type';
|
||||||
import { numToUd, unixToDa, decToUd, deSig, resourceAsPath } from '~/logic/lib/util';
|
import BaseApi from './base';
|
||||||
|
|
||||||
export const createBlankNodeWithChildPost = (
|
export const createBlankNodeWithChildPost = (
|
||||||
parentIndex = '',
|
parentIndex = '',
|
||||||
@ -185,13 +184,12 @@ export default class GraphApi extends BaseApi<StoreState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
eval(cord: string) {
|
eval(cord: string): Promise<string[] | undefined> {
|
||||||
return this.spider('graph-view-action', 'tang', 'graph-eval', {
|
return this.spider('graph-view-action', 'tang', 'graph-eval', {
|
||||||
eval: cord
|
eval: cord
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
addGraph(ship: Patp, name: string, graph: any, mark: any) {
|
addGraph(ship: Patp, name: string, graph: any, mark: any) {
|
||||||
return this.storeAction({
|
return this.storeAction({
|
||||||
'add-graph': {
|
'add-graph': {
|
||||||
@ -265,7 +263,7 @@ export default class GraphApi extends BaseApi<StoreState> {
|
|||||||
'resource',
|
'resource',
|
||||||
'graph-create-group-feed',
|
'graph-create-group-feed',
|
||||||
{
|
{
|
||||||
"create-group-feed": { resource: group, vip }
|
'create-group-feed': { resource: group, vip }
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return resource;
|
return resource;
|
||||||
@ -277,12 +275,11 @@ export default class GraphApi extends BaseApi<StoreState> {
|
|||||||
'json',
|
'json',
|
||||||
'graph-disable-group-feed',
|
'graph-disable-group-feed',
|
||||||
{
|
{
|
||||||
"disable-group-feed": { resource: group }
|
'disable-group-feed': { resource: group }
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
removePosts(ship: Patp, name: string, indices: string[]) {
|
removePosts(ship: Patp, name: string, indices: string[]) {
|
||||||
return this.hookAction(ship, {
|
return this.hookAction(ship, {
|
||||||
'remove-posts': {
|
'remove-posts': {
|
||||||
@ -369,7 +366,7 @@ export default class GraphApi extends BaseApi<StoreState> {
|
|||||||
const node = data['graph-update'];
|
const node = data['graph-update'];
|
||||||
this.store.handleEvent({
|
this.store.handleEvent({
|
||||||
data: {
|
data: {
|
||||||
"graph-update-loose": node
|
'graph-update-loose': node
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import BaseApi from './base';
|
import { Enc, Patp } from '@urbit/api';
|
||||||
import { StoreState } from '../store/type';
|
|
||||||
import { Path, Patp, Enc } from '@urbit/api';
|
|
||||||
import {
|
import {
|
||||||
GroupAction,
|
GroupAction,
|
||||||
GroupPolicy,
|
GroupPolicy,
|
||||||
Resource,
|
|
||||||
Tag,
|
GroupPolicyDiff, Resource,
|
||||||
GroupPolicyDiff
|
Tag
|
||||||
} from '@urbit/api/groups';
|
} from '@urbit/api/groups';
|
||||||
import { makeResource } from '../lib/group';
|
import { makeResource } from '../lib/group';
|
||||||
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class GroupsApi extends BaseApi<StoreState> {
|
export default class GroupsApi extends BaseApi<StoreState> {
|
||||||
remove(resource: Resource, ships: Patp[]) {
|
remove(resource: Resource, ships: Patp[]) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import BaseApi from './base';
|
import { Association, GraphNotifDescription, IndexedNotification, NotifIndex } from '@urbit/api';
|
||||||
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 { BigInteger } from 'big-integer';
|
||||||
import { getParentIndex } from '../lib/notification';
|
import { getParentIndex } from '../lib/notification';
|
||||||
|
import { dateToDa, decToUd } from '../lib/util';
|
||||||
import useHarkState from '../state/hark';
|
import useHarkState from '../state/hark';
|
||||||
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
function getHarkSize() {
|
function getHarkSize() {
|
||||||
return useHarkState.getState().notifications.size ?? 0;
|
return useHarkState.getState().notifications.size ?? 0;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import BaseApi from './base';
|
import { Serial } from '@urbit/api';
|
||||||
import { StoreState } from '../store/type';
|
import { StoreState } from '../store/type';
|
||||||
import { Serial, Path } from '@urbit/api';
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class InviteApi extends BaseApi<StoreState> {
|
export default class InviteApi extends BaseApi<StoreState> {
|
||||||
accept(app: string, uid: Serial) {
|
accept(app: string, uid: Serial) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import BaseApi from './base';
|
|
||||||
import { StoreState } from '../store/type';
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class LaunchApi extends BaseApi<StoreState> {
|
export default class LaunchApi extends BaseApi<StoreState> {
|
||||||
add(name: string, tile = { basic : { title: '', linkedUrl: '', iconUrl: '' } }) {
|
add(name: string, tile = { basic : { title: '', linkedUrl: '', iconUrl: '' } }) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import BaseApi from './base';
|
|
||||||
import { StoreState } from '../store/type';
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class LocalApi extends BaseApi<StoreState> {
|
export default class LocalApi extends BaseApi<StoreState> {
|
||||||
getBaseHash() {
|
getBaseHash() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import BaseApi from './base';
|
import { Association, Metadata, MetadataUpdatePreview, Path } from '@urbit/api';
|
||||||
import { StoreState } from '../store/type';
|
|
||||||
import { Path, Patp, Association, Metadata, MetadataUpdatePreview } from '@urbit/api';
|
|
||||||
import { uxToHex } from '../lib/util';
|
import { uxToHex } from '../lib/util';
|
||||||
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class MetadataApi extends BaseApi<StoreState> {
|
export default class MetadataApi extends BaseApi<StoreState> {
|
||||||
metadataAdd(appName: string, resource: Path, group: Path, title: string, description: string, dateCreated: string, color: string, moduleName: string) {
|
metadataAdd(appName: string, resource: Path, group: Path, title: string, description: string, dateCreated: string, color: string, moduleName: string) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import BaseApi from './base';
|
|
||||||
import { StoreState } from '../store/type';
|
import { StoreState } from '../store/type';
|
||||||
import { S3Update } from '../../types/s3-update';
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class S3Api extends BaseApi<StoreState> {
|
export default class S3Api extends BaseApi<StoreState> {
|
||||||
setCurrentBucket(bucket: string) {
|
setCurrentBucket(bucket: string) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import BaseApi from './base';
|
import {
|
||||||
import { StoreState } from '../store/type';
|
Bucket, Key,
|
||||||
import { Key,
|
|
||||||
Value,
|
SettingsUpdate, Value
|
||||||
Bucket,
|
|
||||||
SettingsUpdate
|
|
||||||
} from '@urbit/api/settings';
|
} from '@urbit/api/settings';
|
||||||
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseApi from './base';
|
||||||
|
|
||||||
export default class SettingsApi extends BaseApi<StoreState> {
|
export default class SettingsApi extends BaseApi<StoreState> {
|
||||||
private storeAction(action: SettingsUpdate): Promise<any> {
|
private storeAction(action: SettingsUpdate): Promise<any> {
|
||||||
|
@ -7,14 +7,12 @@
|
|||||||
//
|
//
|
||||||
import querystring from 'querystring';
|
import querystring from 'querystring';
|
||||||
import {
|
import {
|
||||||
StorageAcl,
|
|
||||||
StorageClient,
|
StorageClient,
|
||||||
StorageUpload,
|
StorageUpload,
|
||||||
UploadParams,
|
UploadParams,
|
||||||
UploadResult
|
UploadResult
|
||||||
} from './StorageClient';
|
} from './StorageClient';
|
||||||
|
|
||||||
|
|
||||||
const ENDPOINT = 'storage.googleapis.com';
|
const ENDPOINT = 'storage.googleapis.com';
|
||||||
|
|
||||||
class GcpUpload implements StorageUpload {
|
class GcpUpload implements StorageUpload {
|
||||||
@ -27,7 +25,7 @@ class GcpUpload implements StorageUpload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async promise(): Promise<UploadResult> {
|
async promise(): Promise<UploadResult> {
|
||||||
const {Bucket, Key, ContentType, Body} = this.#params;
|
const { Bucket, Key, ContentType, Body } = this.#params;
|
||||||
const urlParams = {
|
const urlParams = {
|
||||||
uploadType: 'media',
|
uploadType: 'media',
|
||||||
name: Key,
|
name: Key,
|
||||||
@ -50,7 +48,7 @@ class GcpUpload implements StorageUpload {
|
|||||||
console.error('GcpClient server error', await response.json());
|
console.error('GcpClient server error', await response.json());
|
||||||
throw new Error(`GcpClient: response ${response.status}`);
|
throw new Error(`GcpClient: response ${response.status}`);
|
||||||
}
|
}
|
||||||
return {Location: `https://${ENDPOINT}/${Bucket}/${Key}`};
|
return { Location: `https://${ENDPOINT}/${Bucket}/${Key}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
// Defines a StorageClient interface interoperable between S3 and GCP Storage.
|
// Defines a StorageClient interface interoperable between S3 and GCP Storage.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
// XX kind of gross. S3 needs 'public-read', GCP needs 'publicRead'.
|
// XX kind of gross. S3 needs 'public-read', GCP needs 'publicRead'.
|
||||||
// Rather than write a wrapper around S3, we offer this field here, which
|
// Rather than write a wrapper around S3, we offer this field here, which
|
||||||
// should always be passed, and will be replaced by 'publicRead' in the
|
// should always be passed, and will be replaced by 'publicRead' in the
|
||||||
// GCP client.
|
// GCP client.
|
||||||
export enum StorageAcl {
|
export enum StorageAcl {
|
||||||
PublicRead = 'public-read'
|
PublicRead = 'public-read'
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface UploadParams {
|
export interface UploadParams {
|
||||||
Bucket: string; // the bucket to upload the object to
|
Bucket: string; // the bucket to upload the object to
|
||||||
@ -16,17 +15,17 @@ export interface UploadParams {
|
|||||||
ContentType: string; // the object's mime-type
|
ContentType: string; // the object's mime-type
|
||||||
ACL: StorageAcl; // ACL, always 'public-read'
|
ACL: StorageAcl; // ACL, always 'public-read'
|
||||||
Body: File; // the object itself
|
Body: File; // the object itself
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface UploadResult {
|
export interface UploadResult {
|
||||||
Location: string;
|
Location: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
// Extra layer of indirection used by S3 client.
|
// Extra layer of indirection used by S3 client.
|
||||||
export interface StorageUpload {
|
export interface StorageUpload {
|
||||||
promise(): Promise<UploadResult>;
|
promise(): Promise<UploadResult>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface StorageClient {
|
export interface StorageClient {
|
||||||
upload(params: UploadParams): StorageUpload;
|
upload(params: UploadParams): StorageUpload;
|
||||||
};
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import bigInt, { BigInteger } from 'big-integer';
|
import { BigInteger } from 'big-integer';
|
||||||
|
|
||||||
export function max(a: BigInteger, b: BigInteger) {
|
export function max(a: BigInteger, b: BigInteger) {
|
||||||
return a.gt(b) ? a : b;
|
return a.gt(b) ? a : b;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
|
|
||||||
export type SubmitHandler = () => Promise<any>;
|
export type SubmitHandler = () => Promise<any>;
|
||||||
interface IFormGroupContext {
|
interface IFormGroupContext {
|
||||||
@ -12,7 +12,7 @@ const fallback: IFormGroupContext = {
|
|||||||
addSubmit: () => {},
|
addSubmit: () => {},
|
||||||
onDirty: () => {},
|
onDirty: () => {},
|
||||||
onErrors: () => {},
|
onErrors: () => {},
|
||||||
submitAll: () => Promise.resolve(),
|
submitAll: () => Promise.resolve()
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormGroupContext = React.createContext(fallback);
|
export const FormGroupContext = React.createContext(fallback);
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
import GlobalApi from '../api/global';
|
import GlobalApi from '../api/global';
|
||||||
import useStorageState from '../state/storage';
|
import useStorageState from '../state/storage';
|
||||||
|
|
||||||
|
|
||||||
class GcpManager {
|
class GcpManager {
|
||||||
#api: GlobalApi | null = null;
|
#api: GlobalApi | null = null;
|
||||||
|
|
||||||
@ -59,15 +58,15 @@ class GcpManager {
|
|||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
#consecutiveFailures: number = 0;
|
#consecutiveFailures = 0;
|
||||||
#configured: boolean = false;
|
#configured = false;
|
||||||
|
|
||||||
private refreshLoop() {
|
private refreshLoop() {
|
||||||
if (!this.#configured) {
|
if (!this.#configured) {
|
||||||
this.#api!.gcp.isConfigured()
|
this.#api!.gcp.isConfigured()
|
||||||
.then((configured) => {
|
.then((configured) => {
|
||||||
if (configured === undefined) {
|
if (configured === undefined) {
|
||||||
throw new Error("can't check whether GCP is configured?");
|
throw new Error('can\'t check whether GCP is configured?');
|
||||||
}
|
}
|
||||||
this.#configured = configured;
|
this.#configured = configured;
|
||||||
if (this.#configured) {
|
if (this.#configured) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
import { Path, PatpNoSig } from '@urbit/api';
|
||||||
|
import { Group, Resource, roleTags, RoleTags } from '@urbit/api/groups';
|
||||||
import _ from 'lodash';
|
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(
|
export function roleForShip(
|
||||||
group: Group,
|
group: Group,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import { IndexedNotification, NotificationGraphConfig, Unreads } from '@urbit/api';
|
||||||
import bigInt, { BigInteger } from 'big-integer';
|
import bigInt, { BigInteger } from 'big-integer';
|
||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import { Unreads, NotificationGraphConfig, IndexedNotification } from '@urbit/api';
|
|
||||||
|
|
||||||
export function getLastSeen(
|
export function getLastSeen(
|
||||||
unreads: Unreads,
|
unreads: Unreads,
|
||||||
@ -36,13 +36,13 @@ export function getNotificationCount(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isWatching(
|
export function isWatching(
|
||||||
config: NotificationGraphConfig,
|
config: NotificationGraphConfig,
|
||||||
graph: string,
|
graph: string,
|
||||||
index = "/"
|
index = '/'
|
||||||
) {
|
) {
|
||||||
return !!config.watching.find(
|
return Boolean(config.watching.find(
|
||||||
watch => watch.graph === graph && watch.index === index
|
watch => watch.graph === graph && watch.index === index
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNotificationKey(time: BigInteger, notification: IndexedNotification): string {
|
export function getNotificationKey(time: BigInteger, notification: IndexedNotification): string {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export function useIdlingState() {
|
export function useIdlingState() {
|
||||||
const [idling, setIdling] = useState(false);
|
const [idling, setIdling] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function blur() {
|
function blur() {
|
||||||
setIdling(true);
|
setIdling(true);
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ export function useIdlingState() {
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('blur', blur);
|
window.removeEventListener('blur', blur);
|
||||||
window.removeEventListener('focus', focus);
|
window.removeEventListener('focus', focus);
|
||||||
}
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return idling;
|
return idling;
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import useLocalState, { LocalState } from "~/logic/state/local";
|
import useLocalState from '~/logic/state/local';
|
||||||
import useSettingsState from "~/logic/state/settings";
|
import useSettingsState from '~/logic/state/settings';
|
||||||
import GlobalApi from "../api/global";
|
import { BackgroundConfig, RemoteContentPolicy } from '~/types';
|
||||||
import { BackgroundConfig, RemoteContentPolicy } from "~/types";
|
import GlobalApi from '../api/global';
|
||||||
|
|
||||||
const getBackgroundString = (bg: BackgroundConfig) => {
|
const getBackgroundString = (bg: BackgroundConfig) => {
|
||||||
if (bg?.type === "url") {
|
if (bg?.type === 'url') {
|
||||||
return bg.url;
|
return bg.url;
|
||||||
} else if (bg?.type === "color") {
|
} else if (bg?.type === 'color') {
|
||||||
return bg.color;
|
return bg.color;
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -18,17 +18,17 @@ export function useMigrateSettings(api: GlobalApi) {
|
|||||||
const { display, remoteContentPolicy, calm } = useSettingsState();
|
const { display, remoteContentPolicy, calm } = useSettingsState();
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
let promises: Promise<any>[] = [];
|
const promises: Promise<any>[] = [];
|
||||||
|
|
||||||
if (local.hideAvatars !== calm.hideAvatars) {
|
if (local.hideAvatars !== calm.hideAvatars) {
|
||||||
promises.push(
|
promises.push(
|
||||||
api.settings.putEntry("calm", "hideAvatars", local.hideAvatars)
|
api.settings.putEntry('calm', 'hideAvatars', local.hideAvatars)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local.hideNicknames !== calm.hideNicknames) {
|
if (local.hideNicknames !== calm.hideNicknames) {
|
||||||
promises.push(
|
promises.push(
|
||||||
api.settings.putEntry("calm", "hideNicknames", local.hideNicknames)
|
api.settings.putEntry('calm', 'hideNicknames', local.hideNicknames)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,15 +38,15 @@ export function useMigrateSettings(api: GlobalApi) {
|
|||||||
) {
|
) {
|
||||||
promises.push(
|
promises.push(
|
||||||
api.settings.putEntry(
|
api.settings.putEntry(
|
||||||
"display",
|
'display',
|
||||||
"background",
|
'background',
|
||||||
getBackgroundString(local.background)
|
getBackgroundString(local.background)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
promises.push(
|
promises.push(
|
||||||
api.settings.putEntry(
|
api.settings.putEntry(
|
||||||
"display",
|
'display',
|
||||||
"backgroundType",
|
'backgroundType',
|
||||||
local.background?.type
|
local.background?.type
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -57,12 +57,12 @@ export function useMigrateSettings(api: GlobalApi) {
|
|||||||
const localVal = local.remoteContentPolicy[key];
|
const localVal = local.remoteContentPolicy[key];
|
||||||
if (localVal !== remoteContentPolicy[key]) {
|
if (localVal !== remoteContentPolicy[key]) {
|
||||||
promises.push(
|
promises.push(
|
||||||
api.settings.putEntry("remoteContentPolicy", key, localVal)
|
api.settings.putEntry('remoteContentPolicy', key, localVal)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
localStorage.removeItem("localReducer");
|
localStorage.removeItem('localReducer');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { GraphNotifIndex, GraphNotificationContents } from '@urbit/api';
|
import { GraphNotificationContents, GraphNotifIndex } from '@urbit/api';
|
||||||
|
|
||||||
export function getParentIndex(
|
export function getParentIndex(
|
||||||
idx: GraphNotifIndex,
|
idx: GraphNotifIndex,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { cite } from '~/logic/lib/util';
|
|
||||||
import { isChannelAdmin } from '~/logic/lib/group';
|
import { isChannelAdmin } from '~/logic/lib/group';
|
||||||
|
import { cite } from '~/logic/lib/util';
|
||||||
|
|
||||||
const makeIndexes = () => new Map([
|
const makeIndexes = () => new Map([
|
||||||
['ships', []],
|
['ships', []],
|
||||||
@ -23,7 +23,7 @@ const result = function(title, link, app, host) {
|
|||||||
const shipIndex = function(contacts) {
|
const shipIndex = function(contacts) {
|
||||||
const ships = [];
|
const ships = [];
|
||||||
Object.keys(contacts).map((e) => {
|
Object.keys(contacts).map((e) => {
|
||||||
return ships.push(result(e, `/~profile/${e}`, 'profile', contacts[e]?.status || ""));
|
return ships.push(result(e, `/~profile/${e}`, 'profile', contacts[e]?.status || ''));
|
||||||
});
|
});
|
||||||
return ships;
|
return ships;
|
||||||
};
|
};
|
||||||
@ -38,11 +38,11 @@ const commandIndex = function (currentGroup, groups, associations) {
|
|||||||
? (association.metadata.vip === 'member-metadata' || isChannelAdmin(group, currentGroup))
|
? (association.metadata.vip === 'member-metadata' || isChannelAdmin(group, currentGroup))
|
||||||
: !currentGroup; // home workspace or hasn't loaded
|
: !currentGroup; // home workspace or hasn't loaded
|
||||||
const workspace = currentGroup || '/home';
|
const workspace = currentGroup || '/home';
|
||||||
commands.push(result(`Groups: Create`, `/~landscape/new`, 'Groups', null));
|
commands.push(result('Groups: Create', '/~landscape/new', 'Groups', null));
|
||||||
if (canAdd) {
|
if (canAdd) {
|
||||||
commands.push(result(`Channel: Create`, `/~landscape${workspace}/new`, 'Groups', null));
|
commands.push(result('Channel: Create', `/~landscape${workspace}/new`, 'Groups', null));
|
||||||
}
|
}
|
||||||
commands.push(result(`Groups: Join`, `/~landscape/join`, 'Groups', null));
|
commands.push(result('Groups: Join', '/~landscape/join', 'Groups', null));
|
||||||
|
|
||||||
return commands;
|
return commands;
|
||||||
};
|
};
|
||||||
@ -80,7 +80,7 @@ const otherIndex = function(config) {
|
|||||||
logout: result('Log Out', '/~/logout', 'logout', null)
|
logout: result('Log Out', '/~/logout', 'logout', null)
|
||||||
};
|
};
|
||||||
other.push(result('Tutorial', '/?tutorial=true', 'tutorial', null));
|
other.push(result('Tutorial', '/?tutorial=true', 'tutorial', null));
|
||||||
for(let cat of config.categories) {
|
for(const cat of config.categories) {
|
||||||
if(idx[cat]) {
|
if(idx[cat]) {
|
||||||
other.push(idx[cat]);
|
other.push(idx[cat]);
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ export default function index(contacts, associations, apps, currentGroup, groups
|
|||||||
}).map((e) => {
|
}).map((e) => {
|
||||||
// iterate through each app's metadata object
|
// iterate through each app's metadata object
|
||||||
Object.keys(associations[e])
|
Object.keys(associations[e])
|
||||||
.filter((association) => !associations?.[e]?.[association]?.metadata?.hidden)
|
.filter(association => !associations?.[e]?.[association]?.metadata?.hidden)
|
||||||
.map((association) => {
|
.map((association) => {
|
||||||
const each = associations[e][association];
|
const each = associations[e][association];
|
||||||
let title = each.resource;
|
let title = each.resource;
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Association,
|
ReferenceContent, resourceFromPath
|
||||||
resourceFromPath,
|
} from '@urbit/api';
|
||||||
Group,
|
|
||||||
ReferenceContent,
|
|
||||||
} from "@urbit/api";
|
|
||||||
|
|
||||||
export function getPermalinkForGraph(
|
export function getPermalinkForGraph(
|
||||||
group: string,
|
group: string,
|
||||||
graph: string,
|
graph: string,
|
||||||
index = ""
|
index = ''
|
||||||
) {
|
) {
|
||||||
const groupLink = getPermalinkForAssociatedGroup(group);
|
const groupLink = getPermalinkForAssociatedGroup(group);
|
||||||
const { ship, name } = resourceFromPath(graph);
|
const { ship, name } = resourceFromPath(graph);
|
||||||
@ -21,16 +18,15 @@ function getPermalinkForAssociatedGroup(group: string) {
|
|||||||
return `web+urbitgraph://group/${ship}/${name}`;
|
return `web+urbitgraph://group/${ship}/${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Permalink = GraphPermalink | GroupPermalink;
|
type Permalink = GraphPermalink | GroupPermalink;
|
||||||
|
|
||||||
interface GroupPermalink {
|
interface GroupPermalink {
|
||||||
type: "group";
|
type: 'group';
|
||||||
group: string;
|
group: string;
|
||||||
link: string;
|
link: string;
|
||||||
}
|
}
|
||||||
interface GraphPermalink {
|
interface GraphPermalink {
|
||||||
type: "graph";
|
type: 'graph';
|
||||||
link: string;
|
link: string;
|
||||||
graph: string;
|
graph: string;
|
||||||
group: string;
|
group: string;
|
||||||
@ -43,16 +39,16 @@ function parseGraphPermalink(
|
|||||||
segments: string[]
|
segments: string[]
|
||||||
): GraphPermalink | null {
|
): GraphPermalink | null {
|
||||||
const [kind, ship, name, ...index] = segments;
|
const [kind, ship, name, ...index] = segments;
|
||||||
if (kind !== "graph") {
|
if (kind !== 'graph') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const graph = `/ship/${ship}/${name}`;
|
const graph = `/ship/${ship}/${name}`;
|
||||||
return {
|
return {
|
||||||
type: "graph",
|
type: 'graph',
|
||||||
link: link.slice(16),
|
link: link.slice(16),
|
||||||
graph,
|
graph,
|
||||||
group,
|
group,
|
||||||
index: `/${index.join("/")}`,
|
index: `/${index.join('/')}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +60,7 @@ export function permalinkToReference(link: Permalink): ReferenceContent {
|
|||||||
group: link.group,
|
group: link.group,
|
||||||
index: link.index
|
index: link.index
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
return { reference };
|
return { reference };
|
||||||
} else {
|
} else {
|
||||||
const reference = {
|
const reference = {
|
||||||
@ -89,22 +85,22 @@ export function referenceToPermalink({ reference }: ReferenceContent): Permalink
|
|||||||
type: 'group',
|
type: 'group',
|
||||||
link,
|
link,
|
||||||
...reference
|
...reference
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parsePermalink(url: string): Permalink | null {
|
export function parsePermalink(url: string): Permalink | null {
|
||||||
const [kind, ...rest] = url.slice(17).split("/");
|
const [kind, ...rest] = url.slice(17).split('/');
|
||||||
if (kind === "group") {
|
if (kind === 'group') {
|
||||||
const [ship, name, ...graph] = rest;
|
const [ship, name, ...graph] = rest;
|
||||||
const group = `/ship/${ship}/${name}`;
|
const group = `/ship/${ship}/${name}`;
|
||||||
if (graph.length > 0) {
|
if (graph.length > 0) {
|
||||||
return parseGraphPermalink(url, group, graph);
|
return parseGraphPermalink(url, group, graph);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: "group",
|
type: 'group',
|
||||||
group,
|
group,
|
||||||
link: url.slice(11),
|
link: url.slice(11)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Post, GraphNode } from '@urbit/api';
|
import { GraphNode, Post } from '@urbit/api';
|
||||||
|
|
||||||
export const buntPost = (): Post => ({
|
export const buntPost = (): Post => ({
|
||||||
author: '',
|
author: '',
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Post, GraphNode, TextContent } from '@urbit/api';
|
import { GraphNode, Post, TextContent } from '@urbit/api';
|
||||||
|
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||||
|
import bigInt, { BigInteger } from 'big-integer';
|
||||||
import { buntPost } from '~/logic/lib/post';
|
import { buntPost } from '~/logic/lib/post';
|
||||||
import { unixToDa } from '~/logic/lib/util';
|
import { unixToDa } from '~/logic/lib/util';
|
||||||
import BigIntOrderedMap from "@urbit/api/lib/BigIntOrderedMap";
|
|
||||||
import bigInt, { BigInteger } from 'big-integer';
|
|
||||||
|
|
||||||
export function newPost(
|
export function newPost(
|
||||||
title: string,
|
title: string,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { memo } from 'react';
|
|
||||||
import { sigil, reactRenderer } from '@tlon/sigil-js';
|
|
||||||
import { Box } from '@tlon/indigo-react';
|
import { Box } from '@tlon/indigo-react';
|
||||||
|
import { reactRenderer, sigil } from '@tlon/sigil-js';
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
export const foregroundFromBackground = (background) => {
|
export const foregroundFromBackground = (background) => {
|
||||||
const rgb = {
|
const rgb = {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import urbitOb from 'urbit-ob';
|
import urbitOb from 'urbit-ob';
|
||||||
import { parsePermalink, permalinkToReference } from "~/logic/lib/permalinks";
|
import { parsePermalink, permalinkToReference } from '~/logic/lib/permalinks';
|
||||||
|
|
||||||
const URL_REGEX = new RegExp(String(/^(([\w\-\+]+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+\w)/.source));
|
const URL_REGEX = new RegExp(String(/^(([\w\-\+]+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+\w)/.source));
|
||||||
|
|
||||||
@ -9,14 +9,14 @@ const isUrl = (string) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const isRef = (str) => {
|
const isRef = (str) => {
|
||||||
return isUrl(str) && str.startsWith("web+urbitgraph://");
|
return isUrl(str) && str.startsWith('web+urbitgraph://');
|
||||||
}
|
};
|
||||||
|
|
||||||
const tokenizeMessage = (text) => {
|
const tokenizeMessage = (text) => {
|
||||||
let messages = [];
|
const messages = [];
|
||||||
let message = [];
|
let message = [];
|
||||||
let isInCodeBlock = false;
|
let isInCodeBlock = false;
|
||||||
let endOfCodeBlock = false;
|
let endOfCodeBlock = false;
|
||||||
@ -78,7 +78,6 @@ const tokenizeMessage = (text) => {
|
|||||||
}
|
}
|
||||||
messages.push({ mention: str });
|
messages.push({ mention: str });
|
||||||
message = [];
|
message = [];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
message.push(str);
|
message.push(str);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Associations } from '@urbit/api';
|
import { Associations } from '@urbit/api';
|
||||||
import { TutorialProgress } from '~/types';
|
|
||||||
import { AlignX, AlignY } from '~/logic/lib/relativePosition';
|
import { AlignX, AlignY } from '~/logic/lib/relativePosition';
|
||||||
|
import { TutorialProgress } from '~/types';
|
||||||
import { Direction } from '~/views/components/Triangle';
|
import { Direction } from '~/views/components/Triangle';
|
||||||
|
|
||||||
export const MODAL_WIDTH = 256;
|
export const MODAL_WIDTH = 256;
|
||||||
@ -92,7 +92,7 @@ export const progressDetails: Record<TutorialProgress, StepDetail> = {
|
|||||||
alignY: 'top',
|
alignY: 'top',
|
||||||
arrow: 'East',
|
arrow: 'East',
|
||||||
offsetX: MODAL_WIDTH + 24,
|
offsetX: MODAL_WIDTH + 24,
|
||||||
offsetY: 80,
|
offsetY: 80
|
||||||
},
|
},
|
||||||
channels: {
|
channels: {
|
||||||
title: 'Channels',
|
title: 'Channels',
|
||||||
@ -157,17 +157,17 @@ export const progressDetails: Record<TutorialProgress, StepDetail> = {
|
|||||||
alignX: 'right',
|
alignX: 'right',
|
||||||
arrow: 'South',
|
arrow: 'South',
|
||||||
offsetX: -300 + MODAL_WIDTH / 2,
|
offsetX: -300 + MODAL_WIDTH / 2,
|
||||||
offsetY: -4,
|
offsetY: -4
|
||||||
},
|
},
|
||||||
leap: {
|
leap: {
|
||||||
title: 'Leap',
|
title: 'Leap',
|
||||||
description:
|
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}`,
|
url: `/~profile/~${window.ship}`,
|
||||||
alignY: "top",
|
alignY: 'top',
|
||||||
alignX: "left",
|
alignX: 'left',
|
||||||
arrow: "North",
|
arrow: 'North',
|
||||||
offsetX: 76,
|
offsetX: 76,
|
||||||
offsetY: -48,
|
offsetY: -48
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { writeText } from "./util";
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useCallback, useState, useMemo } from "react";
|
import { writeText } from './util';
|
||||||
|
|
||||||
export function useCopy(copied: string, display: string) {
|
export function useCopy(copied: string, display: string) {
|
||||||
const [didCopy, setDidCopy] = useState(false);
|
const [didCopy, setDidCopy] = useState(false);
|
||||||
@ -11,9 +11,9 @@ export function useCopy(copied: string, display: string) {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
}, [copied]);
|
}, [copied]);
|
||||||
|
|
||||||
const copyDisplay = useMemo(() => (didCopy ? "Copied" : display), [
|
const copyDisplay = useMemo(() => (didCopy ? 'Copied' : display), [
|
||||||
didCopy,
|
didCopy,
|
||||||
display,
|
display
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return { copyDisplay, doCopy, didCopy };
|
return { copyDisplay, doCopy, didCopy };
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useMemo, useEffect } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
function validateDragEvent(e: DragEvent): FileList | File[] | true | null {
|
function validateDragEvent(e: DragEvent): FileList | File[] | true | null {
|
||||||
const files: File[] = [];
|
const files: File[] = [];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useMemo, useCallback } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
export function useDropdown<C>(
|
export function useDropdown<C>(
|
||||||
candidates: C[],
|
candidates: C[],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, RefObject, useRef, useState } from 'react';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { RefObject, useEffect, useState } from 'react';
|
||||||
import usePreviousValue from './usePreviousValue';
|
import usePreviousValue from './usePreviousValue';
|
||||||
|
|
||||||
export function distanceToBottom(el: HTMLElement) {
|
export function distanceToBottom(el: HTMLElement) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
function retrieve<T>(key: string, initial: T): T {
|
function retrieve<T>(key: string, initial: T): T {
|
||||||
const s = localStorage.getItem(key);
|
const s = localStorage.getItem(key);
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
|
import { Box } from '@tlon/indigo-react';
|
||||||
import React, {
|
import React, {
|
||||||
useState,
|
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef
|
useRef, useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { PropFunc } from '~/types';
|
||||||
import { Box } from '@tlon/indigo-react';
|
|
||||||
import { ModalOverlay } from '~/views/components/ModalOverlay';
|
import { ModalOverlay } from '~/views/components/ModalOverlay';
|
||||||
import { Portal } from '~/views/components/Portal';
|
import { Portal } from '~/views/components/Portal';
|
||||||
import { PropFunc } from '~/types';
|
|
||||||
|
|
||||||
type ModalFunc = (dismiss: () => void) => JSX.Element;
|
type ModalFunc = (dismiss: () => void) => JSX.Element;
|
||||||
interface UseModalProps {
|
interface UseModalProps {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, RefObject } from 'react';
|
import { RefObject, useEffect } from 'react';
|
||||||
|
|
||||||
export function useOutsideClick(
|
export function useOutsideClick(
|
||||||
ref: RefObject<HTMLElement | null | undefined>,
|
ref: RefObject<HTMLElement | null | undefined>,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useMemo, useCallback } from "react";
|
import _ from 'lodash';
|
||||||
import { useLocation } from "react-router-dom";
|
import { useCallback, useMemo } from 'react';
|
||||||
import _ from "lodash";
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
function mergeQuery(search: URLSearchParams, added: Record<string, string>) {
|
function mergeQuery(search: URLSearchParams, added: Record<string, string>) {
|
||||||
_.forIn(added, (v, k) => {
|
_.forIn(added, (v, k) => {
|
||||||
@ -32,7 +32,7 @@ export function useQuery() {
|
|||||||
mergeQuery(q, params);
|
mergeQuery(q, params);
|
||||||
return {
|
return {
|
||||||
pathname: path,
|
pathname: path,
|
||||||
search: q.toString(),
|
search: q.toString()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[search, pathname]
|
[search, pathname]
|
||||||
@ -41,6 +41,6 @@ export function useQuery() {
|
|||||||
return {
|
return {
|
||||||
query,
|
query,
|
||||||
appendQuery,
|
appendQuery,
|
||||||
toQuery,
|
toQuery
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useEffect, useState } from 'react';
|
||||||
import {unstable_batchedUpdates} from "react-dom";
|
import { unstable_batchedUpdates } from 'react-dom';
|
||||||
|
|
||||||
export type IOInstance<I, P, O> = (
|
export type IOInstance<I, P, O> = (
|
||||||
input: I
|
input: I
|
||||||
@ -29,7 +29,7 @@ export function useRunIO<I, O>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reject(new Error("useRunIO: key changed"));
|
reject(new Error('useRunIO: key changed'));
|
||||||
setDone(false);
|
setDone(false);
|
||||||
setOutput(null);
|
setOutput(null);
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MouseEvent, useCallback, useState, useEffect } from 'react';
|
import { MouseEvent, useCallback, useEffect, useState } from 'react';
|
||||||
export type AsyncClickableState = 'waiting' | 'error' | 'loading' | 'success';
|
export type AsyncClickableState = 'waiting' | 'error' | 'loading' | 'success';
|
||||||
|
|
||||||
export function useStatelessAsyncClickable(
|
export function useStatelessAsyncClickable(
|
||||||
|
@ -1,22 +1,16 @@
|
|||||||
import { useCallback, useMemo, useEffect, useRef, useState } from 'react';
|
|
||||||
import {
|
|
||||||
GcpState,
|
|
||||||
S3State,
|
|
||||||
StorageState
|
|
||||||
} from '../../types';
|
|
||||||
import S3 from 'aws-sdk/clients/s3';
|
import S3 from 'aws-sdk/clients/s3';
|
||||||
import GcpClient from './GcpClient';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { StorageClient, StorageAcl } from './StorageClient';
|
|
||||||
import { dateToDa, deSig } from './util';
|
|
||||||
import useStorageState from '../state/storage';
|
import useStorageState from '../state/storage';
|
||||||
|
import GcpClient from './GcpClient';
|
||||||
|
import { StorageAcl, StorageClient } from './StorageClient';
|
||||||
|
import { dateToDa, deSig } from './util';
|
||||||
|
|
||||||
export interface IuseStorage {
|
export interface IuseStorage {
|
||||||
canUpload: boolean;
|
canUpload: boolean;
|
||||||
upload: (file: File, bucket: string) => Promise<string>;
|
upload: (file: File, bucket: string) => Promise<string>;
|
||||||
uploadDefault: (file: File) => Promise<string>;
|
uploadDefault: (file: File) => Promise<string>;
|
||||||
uploading: boolean;
|
uploading: boolean;
|
||||||
promptUpload: () => Promise<unknown>;
|
promptUpload: () => Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
||||||
@ -54,7 +48,7 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const upload = useCallback(
|
const upload = useCallback(
|
||||||
async (file: File, bucket: string) => {
|
async (file: File, bucket: string): Promise<string> => {
|
||||||
if (client.current === null) {
|
if (client.current === null) {
|
||||||
throw new Error('Storage not ready');
|
throw new Error('Storage not ready');
|
||||||
}
|
}
|
||||||
@ -83,7 +77,7 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
|||||||
[client, setUploading]
|
[client, setUploading]
|
||||||
);
|
);
|
||||||
|
|
||||||
const uploadDefault = useCallback(async (file: File) => {
|
const uploadDefault = useCallback(async (file: File): Promise<string> => {
|
||||||
if (s3.configuration.currentBucket === '') {
|
if (s3.configuration.currentBucket === '') {
|
||||||
throw new Error('current bucket not set');
|
throw new Error('current bucket not set');
|
||||||
}
|
}
|
||||||
@ -91,7 +85,7 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
|||||||
}, [s3, upload]);
|
}, [s3, upload]);
|
||||||
|
|
||||||
const promptUpload = useCallback(
|
const promptUpload = useCallback(
|
||||||
() => {
|
(): Promise<string> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const fileSelector = document.createElement('input');
|
const fileSelector = document.createElement('input');
|
||||||
fileSelector.setAttribute('type', 'file');
|
fileSelector.setAttribute('type', 'file');
|
||||||
@ -101,10 +95,10 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
|||||||
const files = fileSelector.files;
|
const files = fileSelector.files;
|
||||||
if (!files || files.length <= 0) {
|
if (!files || files.length <= 0) {
|
||||||
reject();
|
reject();
|
||||||
return;
|
} else {
|
||||||
|
uploadDefault(files[0]).then(resolve);
|
||||||
|
document.body.removeChild(fileSelector);
|
||||||
}
|
}
|
||||||
uploadDefault(files[0]).then(resolve);
|
|
||||||
document.body.removeChild(fileSelector);
|
|
||||||
});
|
});
|
||||||
document.body.appendChild(fileSelector);
|
document.body.appendChild(fileSelector);
|
||||||
fileSelector.click();
|
fileSelector.click();
|
||||||
@ -113,7 +107,7 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
|||||||
[uploadDefault]
|
[uploadDefault]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {canUpload, upload, uploadDefault, uploading, promptUpload};
|
return { canUpload, upload, uploadDefault, uploading, promptUpload };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useStorage;
|
export default useStorage;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useState, useCallback } from "react";
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
export function useToggleState(initial: boolean) {
|
export function useToggleState(initial: boolean) {
|
||||||
const [state, setState] = useState(initial);
|
const [state, setState] = useState(initial);
|
||||||
|
|
||||||
const toggle = useCallback(() => {
|
const toggle = useCallback(() => {
|
||||||
setState((s) => !s);
|
setState(s => !s);
|
||||||
}, [setState]);
|
}, [setState]);
|
||||||
|
|
||||||
return [state, toggle] as const;
|
return [state, toggle] as const;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export function useWaitForProps<P>(props: P, timeout = 0) {
|
export function useWaitForProps<P>(props: P, timeout = 0) {
|
||||||
const [resolve, setResolve] = useState<() => void>(() => () => {});
|
const [resolve, setResolve] = useState<() => void>(() => () => {});
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
import { useEffect, useState, useCallback, useMemo } from 'react';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import { IconRef } from '~/types';
|
|
||||||
import f, { compose, memoize } from 'lodash/fp';
|
|
||||||
import bigInt, { BigInteger } from 'big-integer';
|
|
||||||
import { Association, Contact } from '@urbit/api';
|
import { Association, Contact } from '@urbit/api';
|
||||||
import { enableMapSet } from 'immer';
|
|
||||||
import useSettingsState from '../state/settings';
|
|
||||||
|
|
||||||
import anyAscii from 'any-ascii';
|
import anyAscii from 'any-ascii';
|
||||||
|
import bigInt, { BigInteger } from 'big-integer';
|
||||||
|
import { enableMapSet } from 'immer';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import f from 'lodash/fp';
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { IconRef } from '~/types';
|
||||||
|
import useSettingsState from '../state/settings';
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
@ -202,7 +201,7 @@ export const hexToUx = (hex) => {
|
|||||||
return `0x${ux}`;
|
return `0x${ux}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function writeText(str: string) {
|
export function writeText(str: string | null): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const range = document.createRange();
|
const range = document.createRange();
|
||||||
range.selectNodeContents(document.body);
|
range.selectNodeContents(document.body);
|
||||||
@ -401,7 +400,7 @@ export function pluralize(text: string, isPlural = false, vowel = false) {
|
|||||||
export function useShowNickname(contact: Contact | null, hide?: boolean): boolean {
|
export function useShowNickname(contact: Contact | null, hide?: boolean): boolean {
|
||||||
const hideState = useSettingsState(state => state.calm.hideNicknames);
|
const hideState = useSettingsState(state => state.calm.hideNicknames);
|
||||||
const hideNicknames = typeof hide !== 'undefined' ? hide : hideState;
|
const hideNicknames = typeof hide !== 'undefined' ? hide : hideState;
|
||||||
return !!(contact && contact.nickname && !hideNicknames);
|
return Boolean(contact && contact.nickname && !hideNicknames);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface useHoveringInterface {
|
interface useHoveringInterface {
|
||||||
@ -414,14 +413,13 @@ interface useHoveringInterface {
|
|||||||
|
|
||||||
export const useHovering = (): useHoveringInterface => {
|
export const useHovering = (): useHoveringInterface => {
|
||||||
const [hovering, setHovering] = useState(false);
|
const [hovering, setHovering] = useState(false);
|
||||||
const onMouseOver = useCallback(() => setHovering(true), [])
|
const onMouseOver = useCallback(() => setHovering(true), []);
|
||||||
const onMouseLeave = useCallback(() => setHovering(false), [])
|
const onMouseLeave = useCallback(() => setHovering(false), []);
|
||||||
const bind = useMemo(() => ({
|
const bind = useMemo(() => ({
|
||||||
onMouseOver,
|
onMouseOver,
|
||||||
onMouseLeave,
|
onMouseLeave
|
||||||
}), [onMouseLeave, onMouseOver]);
|
}), [onMouseLeave, onMouseOver]);
|
||||||
|
|
||||||
|
|
||||||
return useMemo(() => ({ hovering, bind }), [hovering, bind]);
|
return useMemo(() => ({ hovering, bind }), [hovering, bind]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import React, {
|
import React, {
|
||||||
useContext,
|
useCallback, useContext,
|
||||||
useState,
|
|
||||||
useCallback,
|
useEffect, useState
|
||||||
useLayoutEffect,
|
} from 'react';
|
||||||
useRef,
|
import { Primitive } from '~/types';
|
||||||
useEffect,
|
import usePreviousValue from './usePreviousValue';
|
||||||
} from "react";
|
|
||||||
import usePreviousValue from "./usePreviousValue";
|
|
||||||
import {Primitive} from "~/types";
|
|
||||||
|
|
||||||
export interface VirtualContextProps {
|
export interface VirtualContextProps {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
@ -15,7 +12,7 @@ export interface VirtualContextProps {
|
|||||||
}
|
}
|
||||||
const fallback: VirtualContextProps = {
|
const fallback: VirtualContextProps = {
|
||||||
save: () => {},
|
save: () => {},
|
||||||
restore: () => {},
|
restore: () => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const VirtualContext = React.createContext(fallback);
|
export const VirtualContext = React.createContext(fallback);
|
||||||
@ -27,7 +24,7 @@ export function useVirtual() {
|
|||||||
export const withVirtual = <P extends {}>(Component: React.ComponentType<P>) =>
|
export const withVirtual = <P extends {}>(Component: React.ComponentType<P>) =>
|
||||||
React.forwardRef((props: P, ref) => (
|
React.forwardRef((props: P, ref) => (
|
||||||
<VirtualContext.Consumer>
|
<VirtualContext.Consumer>
|
||||||
{(context) => <Component ref={ref} {...props} {...context} />}
|
{context => <Component ref={ref} {...props} {...context} />}
|
||||||
</VirtualContext.Consumer>
|
</VirtualContext.Consumer>
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -52,7 +49,7 @@ export function useVirtualResizeState(s: boolean) {
|
|||||||
|
|
||||||
export function useVirtualResizeProp(prop: Primitive) {
|
export function useVirtualResizeProp(prop: Primitive) {
|
||||||
const { save, restore } = useVirtual();
|
const { save, restore } = useVirtual();
|
||||||
const oldProp = usePreviousValue(prop)
|
const oldProp = usePreviousValue(prop);
|
||||||
|
|
||||||
if(prop !== oldProp) {
|
if(prop !== oldProp) {
|
||||||
save();
|
save();
|
||||||
@ -61,6 +58,4 @@ export function useVirtualResizeProp(prop: Primitive) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestAnimationFrame(restore);
|
requestAnimationFrame(restore);
|
||||||
}, [prop]);
|
}, [prop]);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
import { UseStore } from "zustand";
|
import { UseStore } from 'zustand';
|
||||||
import { BaseState } from "../state/base";
|
import { BaseState } from '../state/base';
|
||||||
|
|
||||||
const withStateo = <
|
const withStateo = <
|
||||||
StateType extends BaseState<any>
|
StateType extends BaseState<any>
|
||||||
@ -15,8 +15,8 @@ const withStateo = <
|
|||||||
(object, key) => ({ ...object, [key]: state[key] }), {}
|
(object, key) => ({ ...object, [key]: state[key] }), {}
|
||||||
)
|
)
|
||||||
) : useState();
|
) : useState();
|
||||||
return <Component ref={ref} {...state} {...props} />
|
return <Component ref={ref} {...state} {...props} />;
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const withState = <
|
const withState = <
|
||||||
@ -24,10 +24,10 @@ const withState = <
|
|||||||
stateKey extends keyof StateType
|
stateKey extends keyof StateType
|
||||||
>(
|
>(
|
||||||
Component: any,
|
Component: any,
|
||||||
stores: ([UseStore<StateType>, stateKey[]])[],
|
stores: ([UseStore<StateType>, stateKey[]])[]
|
||||||
) => {
|
) => {
|
||||||
return React.forwardRef((props, ref) => {
|
return React.forwardRef((props, ref) => {
|
||||||
let stateProps: unknown = {};
|
const stateProps: unknown = {};
|
||||||
stores.forEach(([store, keys]) => {
|
stores.forEach(([store, keys]) => {
|
||||||
const storeProps = Array.isArray(keys)
|
const storeProps = Array.isArray(keys)
|
||||||
? store(state => keys.reduce(
|
? store(state => keys.reduce(
|
||||||
@ -36,8 +36,8 @@ const withState = <
|
|||||||
: store();
|
: store();
|
||||||
Object.assign(stateProps, storeProps);
|
Object.assign(stateProps, storeProps);
|
||||||
});
|
});
|
||||||
return <Component ref={ref} {...stateProps} {...props} />
|
return <Component ref={ref} {...stateProps} {...props} />;
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export default withState;
|
export default withState;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { StoreState } from '../store/type';
|
|
||||||
import { Cage } from '~/types/cage';
|
import { Cage } from '~/types/cage';
|
||||||
|
import { StoreState } from '../store/type';
|
||||||
|
|
||||||
type LocalState = Pick<StoreState, 'connection'>;
|
type LocalState = Pick<StoreState, 'connection'>;
|
||||||
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { compose } from 'lodash/fp';
|
|
||||||
|
|
||||||
import { ContactUpdate } from '@urbit/api';
|
import { ContactUpdate } from '@urbit/api';
|
||||||
|
import _ from 'lodash';
|
||||||
import useContactState, { ContactState } from '../state/contact';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useContactState, { ContactState } from '../state/contact';
|
||||||
|
|
||||||
export const ContactReducer = (json) => {
|
export const ContactReducer = (json) => {
|
||||||
const data: ContactUpdate = _.get(json, 'contact-update', false);
|
const data: ContactUpdate = _.get(json, 'contact-update', false);
|
||||||
@ -69,7 +65,7 @@ const edit = (json: ContactUpdate, state: ContactState): ContactState => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const value = data['edit-field'][field];
|
const value = data['edit-field'][field];
|
||||||
|
|
||||||
if(field === 'add-group') {
|
if(field === 'add-group') {
|
||||||
state.contacts[ship].groups.push(value);
|
state.contacts[ship].groups.push(value);
|
||||||
} else if (field === 'remove-group') {
|
} else if (field === 'remove-group') {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import {StoreState} from '../store/type';
|
import type { Cage } from '~/types/cage';
|
||||||
import type {GcpToken} from '../../types/gcp-state';
|
import type { GcpToken } from '../../types/gcp-state';
|
||||||
import type {Cage} from '~/types/cage';
|
|
||||||
import useStorageState, { StorageState } from '../state/storage';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useStorageState, { StorageState } from '../state/storage';
|
||||||
|
|
||||||
export default class GcpReducer {
|
export default class GcpReducer {
|
||||||
reduce(json: Cage) {
|
reduce(json: Cage) {
|
||||||
@ -13,21 +12,21 @@ export default class GcpReducer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reduceToken = (json: Cage, state: StorageState): StorageState => {
|
const reduceToken = (json: Cage, state: StorageState): StorageState => {
|
||||||
let data = json['gcp-token'];
|
const data = json['gcp-token'];
|
||||||
if (data) {
|
if (data) {
|
||||||
setToken(data, state);
|
setToken(data, state);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const setToken = (data: any, state: StorageState): StorageState => {
|
const setToken = (data: any, state: StorageState): StorageState => {
|
||||||
if (isToken(data)) {
|
if (isToken(data)) {
|
||||||
state.gcp.token = data;
|
state.gcp.token = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const isToken = (token: any): token is GcpToken => {
|
const isToken = (token: any): token is GcpToken => {
|
||||||
return (typeof(token.accessKey) === 'string' &&
|
return (typeof(token.accessKey) === 'string' &&
|
||||||
typeof(token.expiresIn) === 'number');
|
typeof(token.expiresIn) === 'number');
|
||||||
}
|
};
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import _ from 'lodash';
|
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||||
import BigIntOrderedMap from "@urbit/api/lib/BigIntOrderedMap";
|
import bigInt, { BigInteger } from 'big-integer';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import bigInt, { BigInteger } from "big-integer";
|
import _ from 'lodash';
|
||||||
import useGraphState, { GraphState } from '../state/graph';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useGraphState, { GraphState } from '../state/graph';
|
||||||
|
|
||||||
export const GraphReducer = (json) => {
|
export const GraphReducer = (json) => {
|
||||||
const data = _.get(json, 'graph-update', false);
|
const data = _.get(json, 'graph-update', false);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
reduceState<GraphState, any>(useGraphState, data, [
|
reduceState<GraphState, any>(useGraphState, data, [
|
||||||
keys,
|
keys,
|
||||||
@ -36,13 +36,13 @@ const addNodesLoose = (json: any, state: GraphState): GraphState => {
|
|||||||
_.set(state.looseNodes, [resource], indices);
|
_.set(state.looseNodes, [resource], indices);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const keys = (json, state: GraphState): GraphState => {
|
const keys = (json, state: GraphState): GraphState => {
|
||||||
const data = _.get(json, 'keys', false);
|
const data = _.get(json, 'keys', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
state.graphKeys = new Set(data.map((res) => {
|
state.graphKeys = new Set(data.map((res) => {
|
||||||
let resource = res.ship + '/' + res.name;
|
const resource = res.ship + '/' + res.name;
|
||||||
return resource;
|
return resource;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -52,36 +52,32 @@ const keys = (json, state: GraphState): GraphState => {
|
|||||||
const processNode = (node) => {
|
const processNode = (node) => {
|
||||||
// is empty
|
// is empty
|
||||||
if (!node.children) {
|
if (!node.children) {
|
||||||
return produce(node, draft => {
|
return produce(node, (draft) => {
|
||||||
draft.children = new BigIntOrderedMap();
|
draft.children = new BigIntOrderedMap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// is graph
|
// is graph
|
||||||
return produce(node, draft => {
|
return produce(node, (draft) => {
|
||||||
draft.children = new BigIntOrderedMap()
|
draft.children = new BigIntOrderedMap()
|
||||||
.gas(_.map(draft.children, (item, idx) =>
|
.gas(_.map(draft.children, (item, idx) =>
|
||||||
[bigInt(idx), processNode(item)] as [BigInteger, any]
|
[bigInt(idx), processNode(item)] as [BigInteger, any]
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const addGraph = (json, state: GraphState): GraphState => {
|
const addGraph = (json, state: GraphState): GraphState => {
|
||||||
|
|
||||||
|
|
||||||
const data = _.get(json, 'add-graph', false);
|
const data = _.get(json, 'add-graph', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (!('graphs' in state)) {
|
if (!('graphs' in state)) {
|
||||||
state.graphs = {};
|
state.graphs = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
let resource = data.resource.ship + '/' + data.resource.name;
|
const resource = data.resource.ship + '/' + data.resource.name;
|
||||||
state.graphs[resource] = new BigIntOrderedMap();
|
state.graphs[resource] = new BigIntOrderedMap();
|
||||||
state.graphTimesentMap[resource] = {};
|
state.graphTimesentMap[resource] = {};
|
||||||
|
|
||||||
|
state.graphs[resource] = state.graphs[resource].gas(Object.keys(data.graph).map((idx) => {
|
||||||
state.graphs[resource] = state.graphs[resource].gas(Object.keys(data.graph).map(idx => {
|
|
||||||
return [bigInt(idx), processNode(data.graph[idx])];
|
return [bigInt(idx), processNode(data.graph[idx])];
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -93,11 +89,10 @@ const addGraph = (json, state: GraphState): GraphState => {
|
|||||||
const removeGraph = (json, state: GraphState): GraphState => {
|
const removeGraph = (json, state: GraphState): GraphState => {
|
||||||
const data = _.get(json, 'remove-graph', false);
|
const data = _.get(json, 'remove-graph', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
||||||
if (!('graphs' in state)) {
|
if (!('graphs' in state)) {
|
||||||
state.graphs = {};
|
state.graphs = {};
|
||||||
}
|
}
|
||||||
let resource = data.ship + '/' + data.name;
|
const resource = data.ship + '/' + data.name;
|
||||||
state.graphKeys.delete(resource);
|
state.graphKeys.delete(resource);
|
||||||
delete state.graphs[resource];
|
delete state.graphs[resource];
|
||||||
}
|
}
|
||||||
@ -108,7 +103,7 @@ const mapifyChildren = (children) => {
|
|||||||
return new BigIntOrderedMap().gas(
|
return new BigIntOrderedMap().gas(
|
||||||
_.map(children, (node, idx) => {
|
_.map(children, (node, idx) => {
|
||||||
idx = idx && idx.startsWith('/') ? idx.slice(1) : idx;
|
idx = idx && idx.startsWith('/') ? idx.slice(1) : idx;
|
||||||
const nd = {...node, children: mapifyChildren(node.children || {}) };
|
const nd = { ...node, children: mapifyChildren(node.children || {}) };
|
||||||
return [bigInt(idx), nd];
|
return [bigInt(idx), nd];
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@ -121,12 +116,12 @@ const addNodes = (json, state) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set parent of graph
|
// set parent of graph
|
||||||
let parNode = graph.get(index[0]);
|
const parNode = graph.get(index[0]);
|
||||||
if (!parNode) {
|
if (!parNode) {
|
||||||
console.error('parent node does not exist, cannot add child');
|
console.error('parent node does not exist, cannot add child');
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
return graph.set(index[0], produce(parNode, draft => {
|
return graph.set(index[0], produce(parNode, (draft) => {
|
||||||
draft.children = _addNode(draft.children, index.slice(1), node);
|
draft.children = _addNode(draft.children, index.slice(1), node);
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@ -137,7 +132,7 @@ const addNodes = (json, state) => {
|
|||||||
} else {
|
} else {
|
||||||
const child = graph.get(index[0]);
|
const child = graph.get(index[0]);
|
||||||
if (child) {
|
if (child) {
|
||||||
return graph.set(index[0], produce(child, draft => {
|
return graph.set(index[0], produce(child, (draft) => {
|
||||||
draft.children = _remove(draft.children, index.slice(1));
|
draft.children = _remove(draft.children, index.slice(1));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -148,10 +143,12 @@ const addNodes = (json, state) => {
|
|||||||
|
|
||||||
const _killByFuzzyTimestamp = (graph, resource, timestamp) => {
|
const _killByFuzzyTimestamp = (graph, resource, timestamp) => {
|
||||||
if (state.graphTimesentMap[resource][timestamp]) {
|
if (state.graphTimesentMap[resource][timestamp]) {
|
||||||
let index = state.graphTimesentMap[resource][timestamp];
|
const index = state.graphTimesentMap[resource][timestamp];
|
||||||
|
|
||||||
if (index.split('/').length === 0) { return graph; }
|
if (index.split('/').length === 0) {
|
||||||
let indexArr = index.split('/').slice(1).map((ind) => {
|
return graph;
|
||||||
|
}
|
||||||
|
const indexArr = index.split('/').slice(1).map((ind) => {
|
||||||
return bigInt(ind);
|
return bigInt(ind);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -174,10 +171,12 @@ const addNodes = (json, state) => {
|
|||||||
|
|
||||||
const data = _.get(json, 'add-nodes', false);
|
const data = _.get(json, 'add-nodes', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (!('graphs' in state)) { return state; }
|
if (!('graphs' in state)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
let resource = data.resource.ship + '/' + data.resource.name;
|
const resource = data.resource.ship + '/' + data.resource.name;
|
||||||
if (!(resource in state.graphs)) {
|
if (!(resource in state.graphs)) {
|
||||||
state.graphs[resource] = new BigIntOrderedMap();
|
state.graphs[resource] = new BigIntOrderedMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,48 +185,47 @@ const addNodes = (json, state) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.graphKeys.add(resource);
|
state.graphKeys.add(resource);
|
||||||
|
|
||||||
let indices = Array.from(Object.keys(data.nodes));
|
const indices = Array.from(Object.keys(data.nodes));
|
||||||
|
|
||||||
indices.sort((a, b) => {
|
indices.sort((a, b) => {
|
||||||
let aArr = a.split('/');
|
const aArr = a.split('/');
|
||||||
let bArr = b.split('/');
|
const bArr = b.split('/');
|
||||||
return aArr.length - bArr.length;
|
return aArr.length - bArr.length;
|
||||||
});
|
});
|
||||||
|
|
||||||
indices.forEach((index) => {
|
indices.forEach((index) => {
|
||||||
let node = data.nodes[index];
|
const node = data.nodes[index];
|
||||||
const old = state.graphs[resource].size;
|
const old = state.graphs[resource].size;
|
||||||
state.graphs[resource] = _removePending(state.graphs[resource], node.post, resource);
|
state.graphs[resource] = _removePending(state.graphs[resource], node.post, resource);
|
||||||
const newSize = state.graphs[resource].size;
|
const newSize = state.graphs[resource].size;
|
||||||
|
|
||||||
|
if (index.split('/').length === 0) {
|
||||||
if (index.split('/').length === 0) { return; }
|
return;
|
||||||
let indexArr = index.split('/').slice(1).map((ind) => {
|
}
|
||||||
|
const indexArr = index.split('/').slice(1).map((ind) => {
|
||||||
return bigInt(ind);
|
return bigInt(ind);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexArr.length === 0) { return state; }
|
if (indexArr.length === 0) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
if (node.post.pending) {
|
if (node.post.pending) {
|
||||||
state.graphTimesentMap[resource][node.post['time-sent']] = index;
|
state.graphTimesentMap[resource][node.post['time-sent']] = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
state.graphs[resource] = _addNode(
|
state.graphs[resource] = _addNode(
|
||||||
state.graphs[resource],
|
state.graphs[resource],
|
||||||
indexArr,
|
indexArr,
|
||||||
produce(node, draft => {
|
produce(node, (draft) => {
|
||||||
draft.children = mapifyChildren(draft?.children || {});
|
draft.children = mapifyChildren(draft?.children || {});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
if(newSize !== old) {
|
if(newSize !== old) {
|
||||||
console.log(`${resource}, (${old}, ${newSize}, ${state.graphs[resource].size})`);
|
console.log(`${resource}, (${old}, ${newSize}, ${state.graphs[resource].size})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
@ -239,7 +237,7 @@ const removePosts = (json, state: GraphState): GraphState => {
|
|||||||
if (child) {
|
if (child) {
|
||||||
return graph.set(index[0], {
|
return graph.set(index[0], {
|
||||||
post: child.post.hash || '',
|
post: child.post.hash || '',
|
||||||
children: child.children
|
children: child.children
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -249,8 +247,8 @@ const removePosts = (json, state: GraphState): GraphState => {
|
|||||||
} else {
|
} else {
|
||||||
const child = graph.get(index[0]);
|
const child = graph.get(index[0]);
|
||||||
if (child) {
|
if (child) {
|
||||||
return graph.set(index[0], produce(draft => {
|
return graph.set(index[0], produce((draft) => {
|
||||||
draft.children = _remove(draft.children, index.slice(1))
|
draft.children = _remove(draft.children, index.slice(1));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return graph;
|
return graph;
|
||||||
@ -262,11 +260,15 @@ const removePosts = (json, state: GraphState): GraphState => {
|
|||||||
if (data) {
|
if (data) {
|
||||||
const { ship, name } = data.resource;
|
const { ship, name } = data.resource;
|
||||||
const res = `${ship}/${name}`;
|
const res = `${ship}/${name}`;
|
||||||
if (!(res in state.graphs)) { return state; }
|
if (!(res in state.graphs)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
data.indices.forEach((index) => {
|
data.indices.forEach((index) => {
|
||||||
if (index.split('/').length === 0) { return; }
|
if (index.split('/').length === 0) {
|
||||||
let indexArr = index.split('/').slice(1).map((ind) => {
|
return;
|
||||||
|
}
|
||||||
|
const indexArr = index.split('/').slice(1).map((ind) => {
|
||||||
return bigInt(ind);
|
return bigInt(ind);
|
||||||
});
|
});
|
||||||
state.graphs[res] = _remove(state.graphs[res], indexArr);
|
state.graphs[res] = _remove(state.graphs[res], indexArr);
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
|
import { Enc } from '@urbit/api';
|
||||||
|
import {
|
||||||
|
Group,
|
||||||
|
|
||||||
|
GroupPolicy, GroupUpdate,
|
||||||
|
|
||||||
|
InvitePolicy, InvitePolicyDiff, OpenPolicy, OpenPolicyDiff, Tags
|
||||||
|
} from '@urbit/api/groups';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Cage } from '~/types/cage';
|
import { Cage } from '~/types/cage';
|
||||||
import {
|
|
||||||
GroupUpdate,
|
|
||||||
Group,
|
|
||||||
Tags,
|
|
||||||
GroupPolicy,
|
|
||||||
OpenPolicyDiff,
|
|
||||||
OpenPolicy,
|
|
||||||
InvitePolicyDiff,
|
|
||||||
InvitePolicy
|
|
||||||
} from '@urbit/api/groups';
|
|
||||||
import { Enc } from '@urbit/api';
|
|
||||||
import { resourceAsPath } from '../lib/util';
|
import { resourceAsPath } from '../lib/util';
|
||||||
import useGroupState, { GroupState } from '../state/group';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useGroupState, { GroupState } from '../state/group';
|
||||||
|
|
||||||
function decodeGroup(group: Enc<Group>): Group {
|
function decodeGroup(group: Enc<Group>): Group {
|
||||||
const members = new Set(group.members);
|
const members = new Set(group.members);
|
||||||
@ -69,11 +66,10 @@ export default class GroupReducer {
|
|||||||
addGroup,
|
addGroup,
|
||||||
removeGroup,
|
removeGroup,
|
||||||
changePolicy,
|
changePolicy,
|
||||||
expose,
|
expose
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
const initial = (json: GroupUpdate, state: GroupState): GroupState => {
|
const initial = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
const data = json['initial'];
|
const data = json['initial'];
|
||||||
@ -81,7 +77,7 @@ const initial = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
state.groups = _.mapValues(data, decodeGroup);
|
state.groups = _.mapValues(data, decodeGroup);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const initialGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
const initialGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if ('initialGroup' in json) {
|
if ('initialGroup' in json) {
|
||||||
@ -90,7 +86,7 @@ const initialGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
state.groups[path] = decodeGroup(group);
|
state.groups[path] = decodeGroup(group);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const addGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
const addGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if ('addGroup' in json) {
|
if ('addGroup' in json) {
|
||||||
@ -104,7 +100,7 @@ const addGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const removeGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
const removeGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if('removeGroup' in json) {
|
if('removeGroup' in json) {
|
||||||
@ -113,7 +109,7 @@ const removeGroup = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
delete state.groups[resourcePath];
|
delete state.groups[resourcePath];
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const addMembers = (json: GroupUpdate, state: GroupState): GroupState => {
|
const addMembers = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if ('addMembers' in json) {
|
if ('addMembers' in json) {
|
||||||
@ -130,7 +126,7 @@ const addMembers = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const removeMembers = (json: GroupUpdate, state: GroupState): GroupState => {
|
const removeMembers = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if ('removeMembers' in json) {
|
if ('removeMembers' in json) {
|
||||||
@ -141,7 +137,7 @@ const removeMembers = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const addTag = (json: GroupUpdate, state: GroupState): GroupState => {
|
const addTag = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if ('addTag' in json) {
|
if ('addTag' in json) {
|
||||||
@ -177,7 +173,7 @@ const removeTag = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
_.set(tags, tagAccessors, tagged);
|
_.set(tags, tagAccessors, tagged);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const changePolicy = (json: GroupUpdate, state: GroupState): GroupState => {
|
const changePolicy = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if ('changePolicy' in json && state) {
|
if ('changePolicy' in json && state) {
|
||||||
@ -195,7 +191,7 @@ const changePolicy = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const expose = (json: GroupUpdate, state: GroupState): GroupState => {
|
const expose = (json: GroupUpdate, state: GroupState): GroupState => {
|
||||||
if( 'expose' in json && state) {
|
if( 'expose' in json && state) {
|
||||||
@ -204,7 +200,7 @@ const expose = (json: GroupUpdate, state: GroupState): GroupState => {
|
|||||||
state.groups[resourcePath].hidden = false;
|
state.groups[resourcePath].hidden = false;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const inviteChangePolicy = (diff: InvitePolicyDiff, policy: InvitePolicy) => {
|
const inviteChangePolicy = (diff: InvitePolicyDiff, policy: InvitePolicy) => {
|
||||||
if ('addInvites' in diff) {
|
if ('addInvites' in diff) {
|
||||||
@ -220,7 +216,7 @@ const inviteChangePolicy = (diff: InvitePolicyDiff, policy: InvitePolicy) => {
|
|||||||
} else {
|
} else {
|
||||||
console.log('bad policy change');
|
console.log('bad policy change');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const openChangePolicy = (diff: OpenPolicyDiff, policy: OpenPolicy) => {
|
const openChangePolicy = (diff: OpenPolicyDiff, policy: OpenPolicy) => {
|
||||||
if ('allowRanks' in diff) {
|
if ('allowRanks' in diff) {
|
||||||
@ -246,4 +242,4 @@ const openChangePolicy = (diff: OpenPolicyDiff, policy: OpenPolicy) => {
|
|||||||
} else {
|
} else {
|
||||||
console.log('bad policy change');
|
console.log('bad policy change');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { GroupUpdate } from '@urbit/api/groups';
|
import { GroupUpdate } from '@urbit/api/groups';
|
||||||
import { resourceAsPath } from '~/logic/lib/util';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
import useGroupState, { GroupState } from '../state/group';
|
import useGroupState, { GroupState } from '../state/group';
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ const started = (json: any, state: GroupState): GroupState => {
|
|||||||
state.pendingJoin[resource] = request;
|
state.pendingJoin[resource] = request;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const progress = (json: any, state: GroupState): GroupState => {
|
const progress = (json: any, state: GroupState): GroupState => {
|
||||||
const data = json.progress;
|
const data = json.progress;
|
||||||
@ -26,7 +25,6 @@ const progress = (json: any, state: GroupState): GroupState => {
|
|||||||
const { progress, resource } = data;
|
const { progress, resource } = data;
|
||||||
state.pendingJoin[resource].progress = progress;
|
state.pendingJoin[resource].progress = progress;
|
||||||
if(progress === 'done') {
|
if(progress === 'done') {
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
delete state.pendingJoin[resource];
|
delete state.pendingJoin[resource];
|
||||||
}, 10000);
|
}, 10000);
|
||||||
@ -41,8 +39,7 @@ const hide = (json: any, state: GroupState) => {
|
|||||||
state.pendingJoin[data].hidden = true;
|
state.pendingJoin[data].hidden = true;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const GroupViewReducer = (json: any) => {
|
export const GroupViewReducer = (json: any) => {
|
||||||
const data = json['group-view-update'];
|
const data = json['group-view-update'];
|
||||||
|
@ -2,13 +2,13 @@ import {
|
|||||||
NotifIndex,
|
NotifIndex,
|
||||||
Timebox
|
Timebox
|
||||||
} from '@urbit/api';
|
} from '@urbit/api';
|
||||||
import { makePatDa } from '~/logic/lib/util';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||||
import useHarkState, { HarkState } from '../state/hark';
|
import { BigInteger } from 'big-integer';
|
||||||
|
import _ from 'lodash';
|
||||||
import { compose } from 'lodash/fp';
|
import { compose } from 'lodash/fp';
|
||||||
|
import { makePatDa } from '~/logic/lib/util';
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
import {BigInteger} from 'big-integer';
|
import useHarkState, { HarkState } from '../state/hark';
|
||||||
|
|
||||||
export const HarkReducer = (json: any) => {
|
export const HarkReducer = (json: any) => {
|
||||||
const data = _.get(json, 'harkUpdate', false);
|
const data = _.get(json, 'harkUpdate', false);
|
||||||
@ -22,7 +22,7 @@ export const HarkReducer = (json: any) => {
|
|||||||
graphIgnore,
|
graphIgnore,
|
||||||
graphListen,
|
graphListen,
|
||||||
graphWatchSelf,
|
graphWatchSelf,
|
||||||
graphMentions,
|
graphMentions
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
const groupHookData = _.get(json, 'hark-group-hook-update', false);
|
const groupHookData = _.get(json, 'hark-group-hook-update', false);
|
||||||
@ -30,7 +30,7 @@ export const HarkReducer = (json: any) => {
|
|||||||
reduceState<HarkState, any>(useHarkState, groupHookData, [
|
reduceState<HarkState, any>(useHarkState, groupHookData, [
|
||||||
groupInitial,
|
groupInitial,
|
||||||
groupListen,
|
groupListen,
|
||||||
groupIgnore,
|
groupIgnore
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -52,9 +52,9 @@ function reduce(data, state) {
|
|||||||
unreadEach,
|
unreadEach,
|
||||||
seenIndex,
|
seenIndex,
|
||||||
removeGraph,
|
removeGraph,
|
||||||
readAll,
|
readAll
|
||||||
];
|
];
|
||||||
const reducer = compose(reducers.map(r => s => {
|
const reducer = compose(reducers.map(r => (s) => {
|
||||||
return r(data, s);
|
return r(data, s);
|
||||||
}));
|
}));
|
||||||
return reducer(state);
|
return reducer(state);
|
||||||
@ -63,13 +63,13 @@ function reduce(data, state) {
|
|||||||
function calculateCount(json: any, state: HarkState) {
|
function calculateCount(json: any, state: HarkState) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
_.forEach(state.unreads.graph, (graphs) => {
|
_.forEach(state.unreads.graph, (graphs) => {
|
||||||
_.forEach(graphs, graph => {
|
_.forEach(graphs, (graph) => {
|
||||||
count += (graph?.notifications || []).length;
|
count += (graph?.notifications || []).length;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
_.forEach(state.unreads.group, group => {
|
_.forEach(state.unreads.group, (group) => {
|
||||||
count += (group?.notifications || []).length;
|
count += (group?.notifications || []).length;
|
||||||
})
|
});
|
||||||
state.notificationsCount = count;
|
state.notificationsCount = count;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ function updateUnreads(state: HarkState, index: NotifIndex, f: (us: Set<string>)
|
|||||||
if(!('graph' in index)) {
|
if(!('graph' in index)) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
let unreads: any = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], new Set<string>());
|
const unreads: any = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], new Set<string>());
|
||||||
f(unreads);
|
f(unreads);
|
||||||
|
|
||||||
_.set(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], unreads);
|
_.set(state.unreads.graph, [index.graph.graph, index.graph.index, 'unreads'], unreads);
|
||||||
@ -294,19 +294,18 @@ function removeNotificationFromUnread(state: HarkState, index: NotifIndex, time:
|
|||||||
const path = [index.graph.graph, index.graph.index, 'notifications'];
|
const path = [index.graph.graph, index.graph.index, 'notifications'];
|
||||||
const curr = _.get(state.unreads.graph, path, []);
|
const curr = _.get(state.unreads.graph, path, []);
|
||||||
_.set(state.unreads.graph, path,
|
_.set(state.unreads.graph, path,
|
||||||
curr.filter(c => !(c.time.eq(time) && notifIdxEqual(c.index, index))),
|
curr.filter(c => !(c.time.eq(time) && notifIdxEqual(c.index, index)))
|
||||||
);
|
);
|
||||||
} else if ('group' in index) {
|
} else if ('group' in index) {
|
||||||
const path = [index.group.group, 'notifications'];
|
const path = [index.group.group, 'notifications'];
|
||||||
const curr = _.get(state.unreads.group, path, []);
|
const curr = _.get(state.unreads.group, path, []);
|
||||||
_.set(state.unreads.group, path,
|
_.set(state.unreads.group, path,
|
||||||
curr.filter(c => !(c.time.eq(time) && notifIdxEqual(c.index, index))),
|
curr.filter(c => !(c.time.eq(time) && notifIdxEqual(c.index, index)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNotificationStats(state: HarkState, index: NotifIndex, statField: 'unreads' | 'last', f: (x: number) => number) {
|
function updateNotificationStats(state: HarkState, index: NotifIndex, statField: 'unreads' | 'last', f: (x: number) => number) {
|
||||||
|
|
||||||
if('graph' in index) {
|
if('graph' in index) {
|
||||||
const curr: any = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, statField], 0);
|
const curr: any = _.get(state.unreads.graph, [index.graph.graph, index.graph.index, statField], 0);
|
||||||
_.set(state.unreads.graph, [index.graph.graph, index.graph.index, statField], f(curr));
|
_.set(state.unreads.graph, [index.graph.graph, index.graph.index, statField], f(curr));
|
||||||
@ -359,7 +358,7 @@ const timebox = (json: any, state: HarkState): HarkState => {
|
|||||||
function more(json: any, state: HarkState): HarkState {
|
function more(json: any, state: HarkState): HarkState {
|
||||||
const data = _.get(json, 'more', false);
|
const data = _.get(json, 'more', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
_.forEach(data, d => {
|
_.forEach(data, (d) => {
|
||||||
reduce(d, state);
|
reduce(d, state);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -431,7 +430,7 @@ function archive(json: any, state: HarkState): HarkState {
|
|||||||
const data = _.get(json, 'archive', false);
|
const data = _.get(json, 'archive', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
const { index } = data;
|
const { index } = data;
|
||||||
removeNotificationFromUnread(state, index, makePatDa(data.time))
|
removeNotificationFromUnread(state, index, makePatDa(data.time));
|
||||||
const time = makePatDa(data.time);
|
const time = makePatDa(data.time);
|
||||||
const timebox = state.notifications.get(time);
|
const timebox = state.notifications.get(time);
|
||||||
if (!timebox) {
|
if (!timebox) {
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { compose } from 'lodash/fp';
|
|
||||||
|
|
||||||
import { InviteUpdate } from '@urbit/api/invite';
|
import { InviteUpdate } from '@urbit/api/invite';
|
||||||
|
import _ from 'lodash';
|
||||||
import { Cage } from '~/types/cage';
|
import { Cage } from '~/types/cage';
|
||||||
import useInviteState, { InviteState } from '../state/invite';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useInviteState, { InviteState } from '../state/invite';
|
||||||
|
|
||||||
export default class InviteReducer {
|
export default class InviteReducer {
|
||||||
reduce(json: Cage) {
|
reduce(json: Cage) {
|
||||||
@ -17,7 +14,7 @@ export default class InviteReducer {
|
|||||||
deleteInvite,
|
deleteInvite,
|
||||||
invite,
|
invite,
|
||||||
accepted,
|
accepted,
|
||||||
decline,
|
decline
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,7 +26,7 @@ const initial = (json: InviteUpdate, state: InviteState): InviteState => {
|
|||||||
state.invites = data;
|
state.invites = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const create = (json: InviteUpdate, state: InviteState): InviteState => {
|
const create = (json: InviteUpdate, state: InviteState): InviteState => {
|
||||||
const data = _.get(json, 'create', false);
|
const data = _.get(json, 'create', false);
|
||||||
@ -37,7 +34,7 @@ const create = (json: InviteUpdate, state: InviteState): InviteState => {
|
|||||||
state.invites[data] = {};
|
state.invites[data] = {};
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const deleteInvite = (json: InviteUpdate, state: InviteState): InviteState => {
|
const deleteInvite = (json: InviteUpdate, state: InviteState): InviteState => {
|
||||||
const data = _.get(json, 'delete', false);
|
const data = _.get(json, 'delete', false);
|
||||||
@ -45,7 +42,7 @@ const deleteInvite = (json: InviteUpdate, state: InviteState): InviteState => {
|
|||||||
delete state.invites[data];
|
delete state.invites[data];
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const invite = (json: InviteUpdate, state: InviteState): InviteState => {
|
const invite = (json: InviteUpdate, state: InviteState): InviteState => {
|
||||||
const data = _.get(json, 'invite', false);
|
const data = _.get(json, 'invite', false);
|
||||||
@ -53,7 +50,7 @@ const invite = (json: InviteUpdate, state: InviteState): InviteState => {
|
|||||||
state.invites[data.term][data.uid] = data.invite;
|
state.invites[data.term][data.uid] = data.invite;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const accepted = (json: InviteUpdate, state: InviteState): InviteState => {
|
const accepted = (json: InviteUpdate, state: InviteState): InviteState => {
|
||||||
const data = _.get(json, 'accepted', false);
|
const data = _.get(json, 'accepted', false);
|
||||||
@ -61,7 +58,7 @@ const accepted = (json: InviteUpdate, state: InviteState): InviteState => {
|
|||||||
delete state.invites[data.term][data.uid];
|
delete state.invites[data.term][data.uid];
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const decline = (json: InviteUpdate, state: InviteState): InviteState => {
|
const decline = (json: InviteUpdate, state: InviteState): InviteState => {
|
||||||
const data = _.get(json, 'decline', false);
|
const data = _.get(json, 'decline', false);
|
||||||
@ -69,4 +66,4 @@ const decline = (json: InviteUpdate, state: InviteState): InviteState => {
|
|||||||
delete state.invites[data.term][data.uid];
|
delete state.invites[data.term][data.uid];
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { LaunchUpdate, WeatherState } from '~/types/launch-update';
|
|
||||||
import { Cage } from '~/types/cage';
|
import { Cage } from '~/types/cage';
|
||||||
import useLaunchState, { LaunchState } from '../state/launch';
|
import { LaunchUpdate, WeatherState } from '~/types/launch-update';
|
||||||
import { compose } from 'lodash/fp';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useLaunchState, { LaunchState } from '../state/launch';
|
||||||
|
|
||||||
export default class LaunchReducer {
|
export default class LaunchReducer {
|
||||||
reduce(json: Cage) {
|
reduce(json: Cage) {
|
||||||
@ -14,29 +13,29 @@ export default class LaunchReducer {
|
|||||||
changeFirstTime,
|
changeFirstTime,
|
||||||
changeOrder,
|
changeOrder,
|
||||||
changeFirstTime,
|
changeFirstTime,
|
||||||
changeIsShown,
|
changeIsShown
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const weatherData: WeatherState | boolean | Record<string, never> = _.get(json, 'weather', false);
|
const weatherData: WeatherState | boolean | Record<string, never> = _.get(json, 'weather', false);
|
||||||
if (weatherData) {
|
if (weatherData) {
|
||||||
useLaunchState.getState().set(state => {
|
useLaunchState.getState().set((state) => {
|
||||||
state.weather = weatherData;
|
state.weather = weatherData;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const locationData = _.get(json, 'location', false);
|
const locationData = _.get(json, 'location', false);
|
||||||
if (locationData) {
|
if (locationData) {
|
||||||
useLaunchState.getState().set(state => {
|
useLaunchState.getState().set((state) => {
|
||||||
state.userLocation = locationData;
|
state.userLocation = locationData;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseHash = _.get(json, 'baseHash', false);
|
const baseHash = _.get(json, 'baseHash', false);
|
||||||
if (baseHash) {
|
if (baseHash) {
|
||||||
useLaunchState.getState().set(state => {
|
useLaunchState.getState().set((state) => {
|
||||||
state.baseHash = baseHash;
|
state.baseHash = baseHash;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,12 +43,12 @@ export default class LaunchReducer {
|
|||||||
export const initial = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
export const initial = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
||||||
const data = _.get(json, 'initial', false);
|
const data = _.get(json, 'initial', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach((key) => {
|
||||||
state[key] = data[key];
|
state[key] = data[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const changeFirstTime = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
export const changeFirstTime = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
||||||
const data = _.get(json, 'changeFirstTime', false);
|
const data = _.get(json, 'changeFirstTime', false);
|
||||||
@ -57,7 +56,7 @@ export const changeFirstTime = (json: LaunchUpdate, state: LaunchState): LaunchS
|
|||||||
state.firstTime = data;
|
state.firstTime = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const changeOrder = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
export const changeOrder = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
||||||
const data = _.get(json, 'changeOrder', false);
|
const data = _.get(json, 'changeOrder', false);
|
||||||
@ -65,7 +64,7 @@ export const changeOrder = (json: LaunchUpdate, state: LaunchState): LaunchState
|
|||||||
state.tileOrdering = data;
|
state.tileOrdering = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const changeIsShown = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
export const changeIsShown = (json: LaunchUpdate, state: LaunchState): LaunchState => {
|
||||||
const data = _.get(json, 'changeIsShown', false);
|
const data = _.get(json, 'changeIsShown', false);
|
||||||
@ -76,4 +75,4 @@ export const changeIsShown = (json: LaunchUpdate, state: LaunchState): LaunchSta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { compose } from 'lodash/fp';
|
|
||||||
|
|
||||||
import { MetadataUpdate } from '@urbit/api/metadata';
|
import { MetadataUpdate } from '@urbit/api/metadata';
|
||||||
|
import _ from 'lodash';
|
||||||
import { Cage } from '~/types/cage';
|
import { Cage } from '~/types/cage';
|
||||||
import useMetadataState, { MetadataState } from '../state/metadata';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
|
import useMetadataState, { MetadataState } from '../state/metadata';
|
||||||
|
|
||||||
export default class MetadataReducer {
|
export default class MetadataReducer {
|
||||||
reduce(json: Cage) {
|
reduce(json: Cage) {
|
||||||
@ -16,7 +13,7 @@ export default class MetadataReducer {
|
|||||||
add,
|
add,
|
||||||
update,
|
update,
|
||||||
remove,
|
remove,
|
||||||
groupInitial,
|
groupInitial
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +25,7 @@ const groupInitial = (json: MetadataUpdate, state: MetadataState): MetadataState
|
|||||||
associations(data, state);
|
associations(data, state);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const associations = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
const associations = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
||||||
const data = _.get(json, 'associations', false);
|
const data = _.get(json, 'associations', false);
|
||||||
@ -50,7 +47,7 @@ const associations = (json: MetadataUpdate, state: MetadataState): MetadataState
|
|||||||
state.associations = metadata;
|
state.associations = metadata;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const add = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
const add = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
||||||
const data = _.get(json, 'add', false);
|
const data = _.get(json, 'add', false);
|
||||||
@ -70,7 +67,7 @@ const add = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
|||||||
state.associations = metadata;
|
state.associations = metadata;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const update = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
const update = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
||||||
const data = _.get(json, 'update-metadata', false);
|
const data = _.get(json, 'update-metadata', false);
|
||||||
@ -90,7 +87,7 @@ const update = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
|||||||
state.associations = metadata;
|
state.associations = metadata;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const remove = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
const remove = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
||||||
const data = _.get(json, 'remove', false);
|
const data = _.get(json, 'remove', false);
|
||||||
@ -105,4 +102,4 @@ const remove = (json: MetadataUpdate, state: MetadataState): MetadataState => {
|
|||||||
state.associations = metadata;
|
state.associations = metadata;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { compose } from 'lodash/fp';
|
|
||||||
import { Cage } from '~/types/cage';
|
import { Cage } from '~/types/cage';
|
||||||
import { S3Update } from '~/types/s3-update';
|
import { S3Update } from '~/types/s3-update';
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
import useStorageState, { StorageState } from '../state/storage';
|
import useStorageState, { StorageState } from '../state/storage';
|
||||||
|
|
||||||
|
|
||||||
export default class S3Reducer {
|
export default class S3Reducer {
|
||||||
reduce(json: Cage) {
|
reduce(json: Cage) {
|
||||||
const data = _.get(json, 's3-update', false);
|
const data = _.get(json, 's3-update', false);
|
||||||
@ -18,7 +16,7 @@ export default class S3Reducer {
|
|||||||
removeBucket,
|
removeBucket,
|
||||||
endpoint,
|
endpoint,
|
||||||
accessKeyId,
|
accessKeyId,
|
||||||
secretAccessKey,
|
secretAccessKey
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,7 +28,7 @@ const credentials = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
state.s3.credentials = data;
|
state.s3.credentials = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const configuration = (json: S3Update, state: StorageState): StorageState => {
|
const configuration = (json: S3Update, state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'configuration', false);
|
const data = _.get(json, 'configuration', false);
|
||||||
@ -41,7 +39,7 @@ const configuration = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const currentBucket = (json: S3Update, state: StorageState): StorageState => {
|
const currentBucket = (json: S3Update, state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'setCurrentBucket', false);
|
const data = _.get(json, 'setCurrentBucket', false);
|
||||||
@ -49,7 +47,7 @@ const currentBucket = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
state.s3.configuration.currentBucket = data;
|
state.s3.configuration.currentBucket = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const addBucket = (json: S3Update, state: StorageState): StorageState => {
|
const addBucket = (json: S3Update, state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'addBucket', false);
|
const data = _.get(json, 'addBucket', false);
|
||||||
@ -58,7 +56,7 @@ const addBucket = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
state.s3.configuration.buckets.add(data);
|
state.s3.configuration.buckets.add(data);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const removeBucket = (json: S3Update, state: StorageState): StorageState => {
|
const removeBucket = (json: S3Update, state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'removeBucket', false);
|
const data = _.get(json, 'removeBucket', false);
|
||||||
@ -66,7 +64,7 @@ const removeBucket = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
state.s3.configuration.buckets.delete(data);
|
state.s3.configuration.buckets.delete(data);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const endpoint = (json: S3Update, state: StorageState): StorageState => {
|
const endpoint = (json: S3Update, state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'setEndpoint', false);
|
const data = _.get(json, 'setEndpoint', false);
|
||||||
@ -74,7 +72,7 @@ const endpoint = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
state.s3.credentials.endpoint = data;
|
state.s3.credentials.endpoint = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const accessKeyId = (json: S3Update , state: StorageState): StorageState => {
|
const accessKeyId = (json: S3Update , state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'setAccessKeyId', false);
|
const data = _.get(json, 'setAccessKeyId', false);
|
||||||
@ -82,7 +80,7 @@ const accessKeyId = (json: S3Update , state: StorageState): StorageState => {
|
|||||||
state.s3.credentials.accessKeyId = data;
|
state.s3.credentials.accessKeyId = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const secretAccessKey = (json: S3Update, state: StorageState): StorageState => {
|
const secretAccessKey = (json: S3Update, state: StorageState): StorageState => {
|
||||||
const data = _.get(json, 'setSecretAccessKey', false);
|
const data = _.get(json, 'setSecretAccessKey', false);
|
||||||
@ -90,4 +88,4 @@ const secretAccessKey = (json: S3Update, state: StorageState): StorageState => {
|
|||||||
state.s3.credentials.secretAccessKey = data;
|
state.s3.credentials.secretAccessKey = data;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
};
|
||||||
|
@ -1,26 +1,25 @@
|
|||||||
|
import { SettingsUpdate } from '@urbit/api/settings';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import useSettingsState, { SettingsState } from '~/logic/state/settings';
|
import useSettingsState, { SettingsState } from '~/logic/state/settings';
|
||||||
import { SettingsUpdate } from '@urbit/api/settings';
|
|
||||||
import { reduceState } from '../state/base';
|
import { reduceState } from '../state/base';
|
||||||
import { string } from 'prop-types';
|
|
||||||
|
|
||||||
export default class SettingsReducer {
|
export default class SettingsReducer {
|
||||||
reduce(json: any) {
|
reduce(json: any) {
|
||||||
let data = json["settings-event"];
|
let data = json['settings-event'];
|
||||||
if (data) {
|
if (data) {
|
||||||
reduceState<SettingsState, SettingsUpdate>(useSettingsState, data, [
|
reduceState<SettingsState, SettingsUpdate>(useSettingsState, data, [
|
||||||
this.putBucket,
|
this.putBucket,
|
||||||
this.delBucket,
|
this.delBucket,
|
||||||
this.putEntry,
|
this.putEntry,
|
||||||
this.delEntry,
|
this.delEntry
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
data = json["settings-data"];
|
data = json['settings-data'];
|
||||||
if (data) {
|
if (data) {
|
||||||
reduceState<SettingsState, SettingsUpdate>(useSettingsState, data, [
|
reduceState<SettingsState, SettingsUpdate>(useSettingsState, data, [
|
||||||
this.getAll,
|
this.getAll,
|
||||||
this.getBucket,
|
this.getBucket,
|
||||||
this.getEntry,
|
this.getEntry
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +27,7 @@ export default class SettingsReducer {
|
|||||||
putBucket(json: SettingsUpdate, state: SettingsState): SettingsState {
|
putBucket(json: SettingsUpdate, state: SettingsState): SettingsState {
|
||||||
const data = _.get(json, 'put-bucket', false);
|
const data = _.get(json, 'put-bucket', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
state[data["bucket-key"]] = data.bucket;
|
state[data['bucket-key']] = data.bucket;
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -63,7 +62,7 @@ export default class SettingsReducer {
|
|||||||
getAll(json: any, state: SettingsState): SettingsState {
|
getAll(json: any, state: SettingsState): SettingsState {
|
||||||
const data = _.get(json, 'all');
|
const data = _.get(json, 'all');
|
||||||
if(data) {
|
if(data) {
|
||||||
_.mergeWith(state, data, (obj, src) => _.isArray(src) ? src : undefined)
|
_.mergeWith(state, data, (obj, src) => _.isArray(src) ? src : undefined);
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import produce, { setAutoFreeze } from "immer";
|
import produce, { setAutoFreeze } from 'immer';
|
||||||
import { compose } from "lodash/fp";
|
import { compose } from 'lodash/fp';
|
||||||
import create, { State, UseStore } from "zustand";
|
import create, { State, UseStore } from 'zustand';
|
||||||
import { persist, devtools } from "zustand/middleware";
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
setAutoFreeze(false);
|
setAutoFreeze(false);
|
||||||
|
|
||||||
|
|
||||||
export const stateSetter = <StateType>(
|
export const stateSetter = <StateType>(
|
||||||
fn: (state: StateType) => void,
|
fn: (state: StateType) => void,
|
||||||
set
|
set
|
||||||
@ -22,7 +21,7 @@ export const reduceState = <
|
|||||||
reducers: ((data: UpdateType, state: StateType) => StateType)[]
|
reducers: ((data: UpdateType, state: StateType) => StateType)[]
|
||||||
): void => {
|
): void => {
|
||||||
const reducer = compose(reducers.map(r => sta => r(data, sta)));
|
const reducer = compose(reducers.map(r => sta => r(data, sta)));
|
||||||
state.getState().set(state => {
|
state.getState().set((state) => {
|
||||||
reducer(state);
|
reducer(state);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -36,10 +35,10 @@ export const stateStorageKey = (stateName: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
(window as any).clearStates = () => {
|
(window as any).clearStates = () => {
|
||||||
stateStorageKeys.forEach(key => {
|
stateStorageKeys.forEach((key) => {
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface BaseState<StateType> extends State {
|
export interface BaseState<StateType> extends State {
|
||||||
set: (fn: (state: StateType) => void) => void;
|
set: (fn: (state: StateType) => void) => void;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Patp, Rolodex, Scry, Contact } from "@urbit/api";
|
import { Contact, Patp, Rolodex } from '@urbit/api';
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { BaseState, createState } from "./base";
|
import { BaseState, createState } from './base';
|
||||||
import {useCallback} from "react";
|
|
||||||
|
|
||||||
export interface ContactState extends BaseState<ContactState> {
|
export interface ContactState extends BaseState<ContactState> {
|
||||||
contacts: Rolodex;
|
contacts: Rolodex;
|
||||||
@ -13,7 +12,7 @@ export interface ContactState extends BaseState<ContactState> {
|
|||||||
const useContactState = createState<ContactState>('Contact', {
|
const useContactState = createState<ContactState>('Contact', {
|
||||||
contacts: {},
|
contacts: {},
|
||||||
nackedContacts: new Set(),
|
nackedContacts: new Set(),
|
||||||
isContactPublic: false,
|
isContactPublic: false
|
||||||
// fetchIsAllowed: async (
|
// fetchIsAllowed: async (
|
||||||
// entity,
|
// entity,
|
||||||
// name,
|
// name,
|
||||||
@ -36,7 +35,7 @@ export function useContact(ship: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useOurContact() {
|
export function useOurContact() {
|
||||||
return useContact(`~${window.ship}`)
|
return useContact(`~${window.ship}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useContactState;
|
export default useContactState;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Graphs, decToUd, numToUd, GraphNode, deSig, Association, resourceFromPath } from "@urbit/api";
|
import { Association, deSig, GraphNode, Graphs, resourceFromPath } from '@urbit/api';
|
||||||
import {useCallback} from "react";
|
import { useCallback } from 'react';
|
||||||
|
import { BaseState, createState } from './base';
|
||||||
import { BaseState, createState } from "./base";
|
|
||||||
|
|
||||||
export interface GraphState extends BaseState<GraphState> {
|
export interface GraphState extends BaseState<GraphState> {
|
||||||
graphs: Graphs;
|
graphs: Graphs;
|
||||||
@ -22,14 +21,14 @@ export interface GraphState extends BaseState<GraphState> {
|
|||||||
// getYoungerSiblings: (ship: string, resource: string, count: number, index?: string) => Promise<void>;
|
// getYoungerSiblings: (ship: string, resource: string, count: number, index?: string) => Promise<void>;
|
||||||
// getGraphSubset: (ship: string, resource: string, start: string, end: string) => Promise<void>;
|
// getGraphSubset: (ship: string, resource: string, start: string, end: string) => Promise<void>;
|
||||||
// getNode: (ship: string, resource: string, index: string) => Promise<void>;
|
// getNode: (ship: string, resource: string, index: string) => Promise<void>;
|
||||||
};
|
}
|
||||||
|
|
||||||
const useGraphState = createState<GraphState>('Graph', {
|
const useGraphState = createState<GraphState>('Graph', {
|
||||||
graphs: {},
|
graphs: {},
|
||||||
graphKeys: new Set(),
|
graphKeys: new Set(),
|
||||||
looseNodes: {},
|
looseNodes: {},
|
||||||
pendingIndices: {},
|
pendingIndices: {},
|
||||||
graphTimesentMap: {},
|
graphTimesentMap: {}
|
||||||
// getKeys: async () => {
|
// getKeys: async () => {
|
||||||
// const api = useApi();
|
// const api = useApi();
|
||||||
// const keys = await api.scry({
|
// const keys = await api.scry({
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import { Path, JoinRequests, Association, Group } from "@urbit/api";
|
import { Association, Group, JoinRequests } from '@urbit/api';
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { BaseState, createState } from "./base";
|
import { BaseState, createState } from './base';
|
||||||
import {useCallback} from "react";
|
|
||||||
|
|
||||||
export interface GroupState extends BaseState<GroupState> {
|
export interface GroupState extends BaseState<GroupState> {
|
||||||
groups: {
|
groups: {
|
||||||
[group: string]: Group;
|
[group: string]: Group;
|
||||||
}
|
}
|
||||||
pendingJoin: JoinRequests;
|
pendingJoin: JoinRequests;
|
||||||
};
|
}
|
||||||
|
|
||||||
const useGroupState = createState<GroupState>('Group', {
|
const useGroupState = createState<GroupState>('Group', {
|
||||||
groups: {},
|
groups: {},
|
||||||
pendingJoin: {},
|
pendingJoin: {}
|
||||||
}, ['groups']);
|
}, ['groups']);
|
||||||
|
|
||||||
export function useGroup(group: string) {
|
export function useGroup(group: string) {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { NotificationGraphConfig, Timebox, Unreads, dateToDa } from "@urbit/api";
|
import { NotificationGraphConfig, Timebox, Unreads } from '@urbit/api';
|
||||||
import BigIntOrderedMap from "@urbit/api/lib/BigIntOrderedMap";
|
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||||
|
|
||||||
// import { harkGraphHookReducer, harkGroupHookReducer, harkReducer } from "~/logic/subscription/hark";
|
// import { harkGraphHookReducer, harkGroupHookReducer, harkReducer } from "~/logic/subscription/hark";
|
||||||
import { BaseState, createState } from "./base";
|
import { BaseState, createState } from './base';
|
||||||
|
|
||||||
export const HARK_FETCH_MORE_COUNT = 3;
|
export const HARK_FETCH_MORE_COUNT = 3;
|
||||||
|
|
||||||
@ -15,9 +14,9 @@ export interface HarkState extends BaseState<HarkState> {
|
|||||||
notifications: BigIntOrderedMap<Timebox>;
|
notifications: BigIntOrderedMap<Timebox>;
|
||||||
notificationsCount: number;
|
notificationsCount: number;
|
||||||
notificationsGraphConfig: NotificationGraphConfig; // TODO unthread this everywhere
|
notificationsGraphConfig: NotificationGraphConfig; // TODO unthread this everywhere
|
||||||
notificationsGroupConfig: string[];
|
notificationsGroupConfig: string[];
|
||||||
unreads: Unreads;
|
unreads: Unreads;
|
||||||
};
|
}
|
||||||
|
|
||||||
const useHarkState = createState<HarkState>('Hark', {
|
const useHarkState = createState<HarkState>('Hark', {
|
||||||
archivedNotifications: new BigIntOrderedMap<Timebox>(),
|
archivedNotifications: new BigIntOrderedMap<Timebox>(),
|
||||||
@ -62,8 +61,7 @@ const useHarkState = createState<HarkState>('Hark', {
|
|||||||
unreads: {
|
unreads: {
|
||||||
graph: {},
|
graph: {},
|
||||||
group: {}
|
group: {}
|
||||||
},
|
}
|
||||||
}, ['notifications', 'archivedNotifications', 'unreads', 'notificationsCount']);
|
}, ['notifications', 'archivedNotifications', 'unreads', 'notificationsCount']);
|
||||||
|
|
||||||
|
|
||||||
export default useHarkState;
|
export default useHarkState;
|
||||||
|
@ -3,10 +3,10 @@ import { BaseState, createState } from './base';
|
|||||||
|
|
||||||
export interface InviteState extends BaseState<InviteState> {
|
export interface InviteState extends BaseState<InviteState> {
|
||||||
invites: Invites;
|
invites: Invites;
|
||||||
};
|
}
|
||||||
|
|
||||||
const useInviteState = createState<InviteState>('Invite', {
|
const useInviteState = createState<InviteState>('Invite', {
|
||||||
invites: {},
|
invites: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default useInviteState;
|
export default useInviteState;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { Tile, WeatherState } from "~/types/launch-update";
|
import { Tile, WeatherState } from '~/types/launch-update';
|
||||||
|
import { BaseState, createState } from './base';
|
||||||
import { BaseState, createState } from "./base";
|
|
||||||
|
|
||||||
|
|
||||||
export interface LaunchState extends BaseState<LaunchState> {
|
export interface LaunchState extends BaseState<LaunchState> {
|
||||||
firstTime: boolean;
|
firstTime: boolean;
|
||||||
@ -12,7 +10,7 @@ export interface LaunchState extends BaseState<LaunchState> {
|
|||||||
weather: WeatherState | null | Record<string, never> | boolean,
|
weather: WeatherState | null | Record<string, never> | boolean,
|
||||||
userLocation: string | null;
|
userLocation: string | null;
|
||||||
baseHash: string | null;
|
baseHash: string | null;
|
||||||
};
|
}
|
||||||
|
|
||||||
const useLaunchState = createState<LaunchState>('Launch', {
|
const useLaunchState = createState<LaunchState>('Launch', {
|
||||||
firstTime: true,
|
firstTime: true,
|
||||||
@ -23,5 +21,4 @@ const useLaunchState = createState<LaunchState>('Launch', {
|
|||||||
baseHash: null
|
baseHash: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default useLaunchState;
|
||||||
export default useLaunchState;
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import React from 'react';
|
|
||||||
import f from 'lodash/fp';
|
|
||||||
import create, { State } from 'zustand';
|
|
||||||
import { persist } from 'zustand/middleware';
|
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress, LeapCategories } from "~/types/local-update";
|
import f from 'lodash/fp';
|
||||||
|
import React from 'react';
|
||||||
|
import create, { State } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { BackgroundConfig, LeapCategories, RemoteContentPolicy, TutorialProgress, tutorialProgress } from '~/types/local-update';
|
||||||
|
|
||||||
export interface LocalState {
|
export interface LocalState {
|
||||||
theme: 'light' | 'dark' | 'auto';
|
theme: 'light' | 'dark' | 'auto';
|
||||||
@ -27,7 +26,7 @@ export interface LocalState {
|
|||||||
suspendedFocus?: HTMLElement;
|
suspendedFocus?: HTMLElement;
|
||||||
toggleOmnibox: () => void;
|
toggleOmnibox: () => void;
|
||||||
set: (fn: (state: LocalState) => void) => void
|
set: (fn: (state: LocalState) => void) => void
|
||||||
};
|
}
|
||||||
|
|
||||||
type LocalStateZus = LocalState & State;
|
type LocalStateZus = LocalState & State;
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { useCallback } from 'react';
|
import { Association, Associations } from '@urbit/api';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { MetadataUpdatePreview, Association, Associations } from "@urbit/api";
|
import { useCallback } from 'react';
|
||||||
|
import { BaseState, createState } from './base';
|
||||||
import { BaseState, createState } from "./base";
|
|
||||||
|
|
||||||
export const METADATA_MAX_PREVIEW_WAIT = 150000;
|
export const METADATA_MAX_PREVIEW_WAIT = 150000;
|
||||||
|
|
||||||
export interface MetadataState extends BaseState<MetadataState> {
|
export interface MetadataState extends BaseState<MetadataState> {
|
||||||
associations: Associations;
|
associations: Associations;
|
||||||
// preview: (group: string) => Promise<MetadataUpdatePreview>;
|
// preview: (group: string) => Promise<MetadataUpdatePreview>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function useAssocForGraph(graph: string) {
|
export function useAssocForGraph(graph: string) {
|
||||||
return useMetadataState(useCallback(s => s.associations.graph[graph] as Association | undefined, [graph]));
|
return useMetadataState(useCallback(s => s.associations.graph[graph] as Association | undefined, [graph]));
|
||||||
@ -25,12 +24,12 @@ export function useGraphsForGroup(group: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const useMetadataState = createState<MetadataState>('Metadata', {
|
const useMetadataState = createState<MetadataState>('Metadata', {
|
||||||
associations: { groups: {}, graph: {}, contacts: {}, chat: {}, link: {}, publish: {} },
|
associations: { groups: {}, graph: {}, contacts: {}, chat: {}, link: {}, publish: {} }
|
||||||
// preview: async (group): Promise<MetadataUpdatePreview> => {
|
// preview: async (group): Promise<MetadataUpdatePreview> => {
|
||||||
// return new Promise<MetadataUpdatePreview>((resolve, reject) => {
|
// return new Promise<MetadataUpdatePreview>((resolve, reject) => {
|
||||||
// const api = useApi();
|
// const api = useApi();
|
||||||
// let done = false;
|
// let done = false;
|
||||||
|
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// if (done) {
|
// if (done) {
|
||||||
// return;
|
// return;
|
||||||
@ -38,7 +37,7 @@ const useMetadataState = createState<MetadataState>('Metadata', {
|
|||||||
// done = true;
|
// done = true;
|
||||||
// reject(new Error('offline'));
|
// reject(new Error('offline'));
|
||||||
// }, METADATA_MAX_PREVIEW_WAIT);
|
// }, METADATA_MAX_PREVIEW_WAIT);
|
||||||
|
|
||||||
// api.subscribe({
|
// api.subscribe({
|
||||||
// app: 'metadata-pull-hook',
|
// app: 'metadata-pull-hook',
|
||||||
// path: `/preview${group}`,
|
// path: `/preview${group}`,
|
||||||
@ -68,5 +67,4 @@ const useMetadataState = createState<MetadataState>('Metadata', {
|
|||||||
// },
|
// },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default useMetadataState;
|
export default useMetadataState;
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import { RemoteContentPolicy, LeapCategories, leapCategories } from "~/types/local-update";
|
|
||||||
import { BaseState, createState } from '~/logic/state/base';
|
import { BaseState, createState } from '~/logic/state/base';
|
||||||
|
import { LeapCategories, leapCategories, RemoteContentPolicy } from '~/types/local-update';
|
||||||
|
|
||||||
export interface SettingsState extends BaseState<SettingsState> {
|
export interface SettingsState extends BaseState<SettingsState> {
|
||||||
display: {
|
display: {
|
||||||
backgroundType: 'none' | 'url' | 'color';
|
backgroundType: 'none' | 'url' | 'color';
|
||||||
background?: string;
|
background?: string;
|
||||||
dark: boolean;
|
dark: boolean;
|
||||||
theme: "light" | "dark" | "auto";
|
theme: 'light' | 'dark' | 'auto';
|
||||||
};
|
};
|
||||||
calm: {
|
calm: {
|
||||||
hideNicknames: boolean;
|
hideNicknames: boolean;
|
||||||
@ -25,7 +24,7 @@ export interface SettingsState extends BaseState<SettingsState> {
|
|||||||
seen: boolean;
|
seen: boolean;
|
||||||
joined?: number;
|
joined?: number;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export const selectSettingsState =
|
export const selectSettingsState =
|
||||||
<K extends keyof SettingsState>(keys: K[]) => f.pick<SettingsState, K>(keys);
|
<K extends keyof SettingsState>(keys: K[]) => f.pick<SettingsState, K>(keys);
|
||||||
@ -39,7 +38,7 @@ const useSettingsState = createState<SettingsState>('Settings', {
|
|||||||
backgroundType: 'none',
|
backgroundType: 'none',
|
||||||
background: undefined,
|
background: undefined,
|
||||||
dark: false,
|
dark: false,
|
||||||
theme: "auto"
|
theme: 'auto'
|
||||||
},
|
},
|
||||||
calm: {
|
calm: {
|
||||||
hideNicknames: false,
|
hideNicknames: false,
|
||||||
@ -55,7 +54,7 @@ const useSettingsState = createState<SettingsState>('Settings', {
|
|||||||
videoShown: true
|
videoShown: true
|
||||||
},
|
},
|
||||||
leap: {
|
leap: {
|
||||||
categories: leapCategories,
|
categories: leapCategories
|
||||||
},
|
},
|
||||||
tutorial: {
|
tutorial: {
|
||||||
seen: true,
|
seen: true,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BaseState, createState } from "./base";
|
import { BaseState, createState } from './base';
|
||||||
|
|
||||||
export interface GcpToken {
|
export interface GcpToken {
|
||||||
accessKey: string;
|
accessKey: string;
|
||||||
@ -17,7 +17,7 @@ export interface StorageState extends BaseState<StorageState> {
|
|||||||
};
|
};
|
||||||
credentials: any | null; // TODO better type
|
credentials: any | null; // TODO better type
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const useStorageState = createState<StorageState>('Storage', {
|
const useStorageState = createState<StorageState>('Storage', {
|
||||||
gcp: {},
|
gcp: {},
|
||||||
@ -26,8 +26,8 @@ const useStorageState = createState<StorageState>('Storage', {
|
|||||||
buckets: new Set(),
|
buckets: new Set(),
|
||||||
currentBucket: ''
|
currentBucket: ''
|
||||||
},
|
},
|
||||||
credentials: null,
|
credentials: null
|
||||||
}
|
}
|
||||||
}, ['s3']);
|
}, ['s3']);
|
||||||
|
|
||||||
export default useStorageState;
|
export default useStorageState;
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import BaseStore from './base';
|
|
||||||
import InviteReducer from '../reducers/invite-update';
|
|
||||||
import MetadataReducer from '../reducers/metadata-update';
|
|
||||||
|
|
||||||
import { StoreState } from './type';
|
|
||||||
import { Cage } from '~/types/cage';
|
|
||||||
import S3Reducer from '../reducers/s3-update';
|
|
||||||
import { GraphReducer } from '../reducers/graph-update';
|
|
||||||
import { HarkReducer } from '../reducers/hark-update';
|
|
||||||
import { ContactReducer } from '../reducers/contact-update';
|
|
||||||
import GroupReducer from '../reducers/group-update';
|
|
||||||
import LaunchReducer from '../reducers/launch-update';
|
|
||||||
import ConnectionReducer from '../reducers/connection';
|
|
||||||
import SettingsReducer from '../reducers/settings-update';
|
|
||||||
import GcpReducer from '../reducers/gcp-reducer';
|
|
||||||
import { GroupViewReducer } from '../reducers/group-view';
|
|
||||||
import { unstable_batchedUpdates } from 'react-dom';
|
import { unstable_batchedUpdates } from 'react-dom';
|
||||||
|
import { Cage } from '~/types/cage';
|
||||||
|
import ConnectionReducer from '../reducers/connection';
|
||||||
|
import { ContactReducer } from '../reducers/contact-update';
|
||||||
|
import GcpReducer from '../reducers/gcp-reducer';
|
||||||
|
import { GraphReducer } from '../reducers/graph-update';
|
||||||
|
import GroupReducer from '../reducers/group-update';
|
||||||
|
import { GroupViewReducer } from '../reducers/group-view';
|
||||||
|
import { HarkReducer } from '../reducers/hark-update';
|
||||||
|
import InviteReducer from '../reducers/invite-update';
|
||||||
|
import LaunchReducer from '../reducers/launch-update';
|
||||||
|
import MetadataReducer from '../reducers/metadata-update';
|
||||||
|
import S3Reducer from '../reducers/s3-update';
|
||||||
|
import SettingsReducer from '../reducers/settings-update';
|
||||||
|
import BaseStore from './base';
|
||||||
|
import { StoreState } from './type';
|
||||||
|
|
||||||
export default class GlobalStore extends BaseStore<StoreState> {
|
export default class GlobalStore extends BaseStore<StoreState> {
|
||||||
inviteReducer = new InviteReducer();
|
inviteReducer = new InviteReducer();
|
||||||
@ -42,7 +40,7 @@ export default class GlobalStore extends BaseStore<StoreState> {
|
|||||||
|
|
||||||
initialState(): StoreState {
|
initialState(): StoreState {
|
||||||
return {
|
return {
|
||||||
connection: 'connected',
|
connection: 'connected'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import BaseStore from '../store/base';
|
|
||||||
import BaseApi from '../api/base';
|
|
||||||
import { Path } from '@urbit/api';
|
import { Path } from '@urbit/api';
|
||||||
|
import BaseApi from '../api/base';
|
||||||
|
import BaseStore from '../store/base';
|
||||||
|
|
||||||
export default class BaseSubscription<S extends object> {
|
export default class BaseSubscription<S extends object> {
|
||||||
private errorCount = 0;
|
private errorCount = 0;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import BaseSubscription from './base';
|
|
||||||
import { StoreState } from '../store/type';
|
|
||||||
import { Path } from '@urbit/api';
|
import { Path } from '@urbit/api';
|
||||||
import _ from 'lodash';
|
import { StoreState } from '../store/type';
|
||||||
|
import BaseSubscription from './base';
|
||||||
|
|
||||||
export default class GlobalSubscription extends BaseSubscription<StoreState> {
|
export default class GlobalSubscription extends BaseSubscription<StoreState> {
|
||||||
openSubscriptions: any = {};
|
openSubscriptions: any = {};
|
||||||
@ -25,7 +23,7 @@ export default class GlobalSubscription extends BaseSubscription<StoreState> {
|
|||||||
this.subscribe('/all', 'group-view');
|
this.subscribe('/all', 'group-view');
|
||||||
this.subscribe('/nacks', 'contact-pull-hook');
|
this.subscribe('/nacks', 'contact-pull-hook');
|
||||||
this.clearQueue();
|
this.clearQueue();
|
||||||
|
|
||||||
this.subscribe('/updates', 'graph-store');
|
this.subscribe('/updates', 'graph-store');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +37,8 @@ export default class GlobalSubscription extends BaseSubscription<StoreState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(id) {
|
unsubscribe(id) {
|
||||||
for (let key in Object.keys(this.openSubscriptions)) {
|
for (const key in Object.keys(this.openSubscriptions)) {
|
||||||
let val = this.openSubscriptions[key];
|
const val = this.openSubscriptions[key];
|
||||||
if (id === val.id) {
|
if (id === val.id) {
|
||||||
delete this.openSubscriptions[`${val.app}${val.path}`];
|
delete this.openSubscriptions[`${val.app}${val.path}`];
|
||||||
super.unsubscribe(id);
|
super.unsubscribe(id);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
if ("serviceWorker" in navigator && process.env.NODE_ENV !== 'development') {
|
if ('serviceWorker' in navigator && process.env.NODE_ENV !== 'development') {
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register("/~landscape/js/bundle/serviceworker.js", {
|
navigator.serviceWorker.register('/~landscape/js/bundle/serviceworker.js', {
|
||||||
scope: "/",
|
scope: '/'
|
||||||
}).then(reg => {
|
}).then((reg) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
import { registerRoute } from 'workbox-routing';
|
|
||||||
import {
|
|
||||||
NetworkFirst,
|
|
||||||
StaleWhileRevalidate,
|
|
||||||
CacheFirst,
|
|
||||||
} from 'workbox-strategies';
|
|
||||||
|
|
||||||
// Used for filtering matches based on status code, header, or both
|
// Used for filtering matches based on status code, header, or both
|
||||||
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
|
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
|
||||||
// Used to limit entries in cache, remove entries after a certain period of time
|
// Used to limit entries in cache, remove entries after a certain period of time
|
||||||
import { ExpirationPlugin } from 'workbox-expiration';
|
import { ExpirationPlugin } from 'workbox-expiration';
|
||||||
|
import { registerRoute } from 'workbox-routing';
|
||||||
|
import {
|
||||||
|
CacheFirst, NetworkFirst,
|
||||||
|
StaleWhileRevalidate
|
||||||
|
} from 'workbox-strategies';
|
||||||
|
|
||||||
// generate a different sw for every build, to bust cache properly
|
// generate a different sw for every build, to bust cache properly
|
||||||
const hash = process.env.LANDSCAPE_SHORTHASH;
|
const hash = process.env.LANDSCAPE_SHORTHASH;
|
||||||
|
|
||||||
self.addEventListener("install", ev => {
|
self.addEventListener('install', (ev) => {
|
||||||
self.skipWaiting();
|
self.skipWaiting();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('activate', ev => {
|
self.addEventListener('activate', (ev) => {
|
||||||
ev.waitUntil(clients.claim());
|
ev.waitUntil(clients.claim());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -33,10 +30,10 @@ registerRoute(
|
|||||||
plugins: [
|
plugins: [
|
||||||
// Ensure that only requests that result in a 200 status are cached
|
// Ensure that only requests that result in a 200 status are cached
|
||||||
new CacheableResponsePlugin({
|
new CacheableResponsePlugin({
|
||||||
statuses: [200],
|
statuses: [200]
|
||||||
}),
|
})
|
||||||
],
|
]
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy
|
// Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy
|
||||||
@ -53,12 +50,12 @@ registerRoute(
|
|||||||
plugins: [
|
plugins: [
|
||||||
// Ensure that only requests that result in a 200 status are cached
|
// Ensure that only requests that result in a 200 status are cached
|
||||||
new CacheableResponsePlugin({
|
new CacheableResponsePlugin({
|
||||||
statuses: [200],
|
statuses: [200]
|
||||||
}),
|
})
|
||||||
],
|
]
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cache images with a Cache First strategy
|
// Cache images with a Cache First strategy
|
||||||
registerRoute(
|
registerRoute(
|
||||||
// Check to see if the request's destination is style for an image
|
// Check to see if the request's destination is style for an image
|
||||||
@ -70,13 +67,13 @@ registerRoute(
|
|||||||
plugins: [
|
plugins: [
|
||||||
// Ensure that only requests that result in a 200 status are cached
|
// Ensure that only requests that result in a 200 status are cached
|
||||||
new CacheableResponsePlugin({
|
new CacheableResponsePlugin({
|
||||||
statuses: [200],
|
statuses: [200]
|
||||||
}),
|
}),
|
||||||
// Don't cache more than 50 items, and expire them after 30 days
|
// Don't cache more than 50 items, and expire them after 30 days
|
||||||
new ExpirationPlugin({
|
new ExpirationPlugin({
|
||||||
maxEntries: 50,
|
maxEntries: 50,
|
||||||
maxAgeSeconds: 60 * 60 * 24 * 30, // 30 Days
|
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 Days
|
||||||
}),
|
})
|
||||||
],
|
]
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { LocalUpdate } from './local-update';
|
|
||||||
import { LaunchUpdate, WeatherState } from './launch-update';
|
|
||||||
import { ConnectionStatus } from './connection';
|
|
||||||
import { ContactUpdate, GroupUpdate, InviteUpdate, MetadataUpdate } from '@urbit/api';
|
import { ContactUpdate, GroupUpdate, InviteUpdate, MetadataUpdate } from '@urbit/api';
|
||||||
import { SettingsUpdate } from '@urbit/api/settings';
|
import { SettingsUpdate } from '@urbit/api/settings';
|
||||||
|
import { ConnectionStatus } from './connection';
|
||||||
|
import { LaunchUpdate, WeatherState } from './launch-update';
|
||||||
|
import { LocalUpdate } from './local-update';
|
||||||
|
|
||||||
interface MarksToTypes {
|
interface MarksToTypes {
|
||||||
readonly json: any;
|
readonly json: any;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
export interface GcpToken {
|
export interface GcpToken {
|
||||||
accessKey: string;
|
accessKey: string;
|
||||||
expiresIn: number;
|
expiresIn: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface GcpState {
|
export interface GcpState {
|
||||||
token?: GcpToken
|
token?: GcpToken
|
||||||
};
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
export * from './cage';
|
export * from './cage';
|
||||||
export * from './connection';
|
export * from './connection';
|
||||||
|
export * from './gcp-state';
|
||||||
export * from './global';
|
export * from './global';
|
||||||
export * from './launch-update';
|
export * from './launch-update';
|
||||||
export * from './local-update';
|
export * from './local-update';
|
||||||
export * from './storage-state';
|
|
||||||
export * from './gcp-state';
|
|
||||||
export * from './s3-update';
|
export * from './s3-update';
|
||||||
export * from './workspace';
|
export * from './storage-state';
|
||||||
export * from './util';
|
export * from './util';
|
||||||
|
export * from './workspace';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export const tutorialProgress = ['hidden', 'start', 'group-desc', 'channels', 'chat', 'link', 'publish', 'profile', 'leap', 'notifications', 'done', 'exit'] as const;
|
export const tutorialProgress = ['hidden', 'start', 'group-desc', 'channels', 'chat', 'link', 'publish', 'profile', 'leap', 'notifications', 'done', 'exit'] as const;
|
||||||
|
|
||||||
export const leapCategories = ["mychannel", "messages", "updates", "profile", "logout"];
|
export const leapCategories = ['mychannel', 'messages', 'updates', 'profile', 'logout'];
|
||||||
|
|
||||||
export type LeapCategories = typeof leapCategories[number];
|
export type LeapCategories = typeof leapCategories[number];
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import {GcpState} from './gcp-state';
|
import { GcpState } from './gcp-state';
|
||||||
import {S3State} from './s3-update';
|
import { S3State } from './s3-update';
|
||||||
|
|
||||||
|
|
||||||
export interface StorageState {
|
export interface StorageState {
|
||||||
gcp: GcpState;
|
gcp: GcpState;
|
||||||
s3: S3State;
|
s3: S3State;
|
||||||
};
|
}
|
||||||
|
@ -1,42 +1,34 @@
|
|||||||
import { hot } from 'react-hot-loader/root';
|
import dark from '@tlon/indigo-dark';
|
||||||
import 'react-hot-loader';
|
import light from '@tlon/indigo-light';
|
||||||
import * as React from 'react';
|
|
||||||
import { BrowserRouter as Router, withRouter } from 'react-router-dom';
|
|
||||||
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
|
|
||||||
import { sigil as sigiljs, stringRenderer } from '@tlon/sigil-js';
|
import { sigil as sigiljs, stringRenderer } from '@tlon/sigil-js';
|
||||||
import Helmet from 'react-helmet';
|
|
||||||
|
|
||||||
import Mousetrap from 'mousetrap';
|
import Mousetrap from 'mousetrap';
|
||||||
import 'mousetrap-global-bind';
|
import 'mousetrap-global-bind';
|
||||||
|
import * as React from 'react';
|
||||||
import './css/indigo-static.css';
|
import Helmet from 'react-helmet';
|
||||||
import './css/fonts.css';
|
import 'react-hot-loader';
|
||||||
import './apps/chat/css/custom.css';
|
import { hot } from 'react-hot-loader/root';
|
||||||
import './landscape/css/custom.css';
|
import { BrowserRouter as Router, withRouter } from 'react-router-dom';
|
||||||
|
import styled, { ThemeProvider } from 'styled-components';
|
||||||
import light from '@tlon/indigo-light';
|
|
||||||
import dark from '@tlon/indigo-dark';
|
|
||||||
|
|
||||||
import { Text, Anchor, Row } from '@tlon/indigo-react';
|
|
||||||
|
|
||||||
import { Content } from './landscape/components/Content';
|
|
||||||
import StatusBar from './components/StatusBar';
|
|
||||||
import Omnibox from './components/leap/Omnibox';
|
|
||||||
import ErrorBoundary from '~/views/components/ErrorBoundary';
|
|
||||||
import { TutorialModal } from '~/views/landscape/components/TutorialModal';
|
|
||||||
|
|
||||||
import GlobalStore from '~/logic/store/store';
|
|
||||||
import GlobalSubscription from '~/logic/subscription/global';
|
|
||||||
import GlobalApi from '~/logic/api/global';
|
import GlobalApi from '~/logic/api/global';
|
||||||
import { uxToHex } from '~/logic/lib/util';
|
import gcpManager from '~/logic/lib/gcpManager';
|
||||||
import { foregroundFromBackground } from '~/logic/lib/sigil';
|
import { foregroundFromBackground } from '~/logic/lib/sigil';
|
||||||
|
import { uxToHex } from '~/logic/lib/util';
|
||||||
import withState from '~/logic/lib/withState';
|
import withState from '~/logic/lib/withState';
|
||||||
import useLocalState from '~/logic/state/local';
|
|
||||||
import useContactState from '~/logic/state/contact';
|
import useContactState from '~/logic/state/contact';
|
||||||
import useGroupState from '~/logic/state/group';
|
import useGroupState from '~/logic/state/group';
|
||||||
|
import useLocalState from '~/logic/state/local';
|
||||||
import useSettingsState from '~/logic/state/settings';
|
import useSettingsState from '~/logic/state/settings';
|
||||||
import gcpManager from '~/logic/lib/gcpManager';
|
import GlobalStore from '~/logic/store/store';
|
||||||
|
import GlobalSubscription from '~/logic/subscription/global';
|
||||||
|
import ErrorBoundary from '~/views/components/ErrorBoundary';
|
||||||
|
import { TutorialModal } from '~/views/landscape/components/TutorialModal';
|
||||||
|
import './apps/chat/css/custom.css';
|
||||||
|
import Omnibox from './components/leap/Omnibox';
|
||||||
|
import StatusBar from './components/StatusBar';
|
||||||
|
import './css/fonts.css';
|
||||||
|
import './css/indigo-static.css';
|
||||||
|
import { Content } from './landscape/components/Content';
|
||||||
|
import './landscape/css/custom.css';
|
||||||
|
|
||||||
const Root = withState(styled.div`
|
const Root = withState(styled.div`
|
||||||
font-family: ${p => p.theme.fonts.sans};
|
font-family: ${p => p.theme.fonts.sans};
|
||||||
@ -126,13 +118,13 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTheme(e) {
|
updateTheme(e) {
|
||||||
this.props.set(state => {
|
this.props.set((state) => {
|
||||||
state.dark = e.matches;
|
state.dark = e.matches;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMobile(e) {
|
updateMobile(e) {
|
||||||
this.props.set(state => {
|
this.props.set((state) => {
|
||||||
state.mobile = e.matches;
|
state.mobile = e.matches;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -155,8 +147,8 @@ class App extends React.Component {
|
|||||||
|
|
||||||
getTheme() {
|
getTheme() {
|
||||||
const { props } = this;
|
const { props } = this;
|
||||||
return ((props.dark && props?.display?.theme == "auto") ||
|
return ((props.dark && props?.display?.theme == 'auto') ||
|
||||||
props?.display?.theme == "dark"
|
props?.display?.theme == 'dark'
|
||||||
) ? dark : light;
|
) ? dark : light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,33 +1,20 @@
|
|||||||
import React, {
|
|
||||||
useRef,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
useMemo,
|
|
||||||
} from 'react';
|
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
|
||||||
import { Col } from '@tlon/indigo-react';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import bigInt, { BigInteger } from 'big-integer';
|
|
||||||
|
|
||||||
import { Association } from '@urbit/api/metadata';
|
|
||||||
import { StoreState } from '~/logic/store/type';
|
|
||||||
import { useFileDrag } from '~/logic/lib/useDrag';
|
|
||||||
import ChatWindow from './components/ChatWindow';
|
|
||||||
import ChatInput from './components/ChatInput';
|
|
||||||
import GlobalApi from '~/logic/api/global';
|
|
||||||
import { ShareProfile } from '~/views/apps/chat/components/ShareProfile';
|
|
||||||
import SubmitDragger from '~/views/components/SubmitDragger';
|
|
||||||
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
|
|
||||||
import { Loading } from '~/views/components/Loading';
|
|
||||||
import { isWriter, resourceFromPath } from '~/logic/lib/group';
|
|
||||||
|
|
||||||
import useContactState from '~/logic/state/contact';
|
|
||||||
import useGraphState, { useGraphForAssoc } from '~/logic/state/graph';
|
|
||||||
import useGroupState, { useGroupForAssoc } from '~/logic/state/group';
|
|
||||||
import useHarkState from '~/logic/state/hark';
|
|
||||||
import { Content, createPost, Post } from '@urbit/api';
|
import { Content, createPost, Post } from '@urbit/api';
|
||||||
|
import { Association } from '@urbit/api/metadata';
|
||||||
|
import { BigInteger } from 'big-integer';
|
||||||
|
import React, {
|
||||||
|
ReactElement, useCallback,
|
||||||
|
useEffect,
|
||||||
|
|
||||||
|
useMemo, useState
|
||||||
|
} from 'react';
|
||||||
|
import GlobalApi from '~/logic/api/global';
|
||||||
|
import { isWriter, resourceFromPath } from '~/logic/lib/group';
|
||||||
import { getPermalinkForGraph } from '~/logic/lib/permalinks';
|
import { getPermalinkForGraph } from '~/logic/lib/permalinks';
|
||||||
|
import useGraphState, { useGraphForAssoc } from '~/logic/state/graph';
|
||||||
|
import { useGroupForAssoc } from '~/logic/state/group';
|
||||||
|
import useHarkState from '~/logic/state/hark';
|
||||||
|
import { StoreState } from '~/logic/store/type';
|
||||||
|
import { Loading } from '~/views/components/Loading';
|
||||||
import { ChatPane } from './components/ChatPane';
|
import { ChatPane } from './components/ChatPane';
|
||||||
|
|
||||||
const getCurrGraphSize = (ship: string, name: string) => {
|
const getCurrGraphSize = (ship: string, name: string) => {
|
||||||
@ -36,20 +23,19 @@ const getCurrGraphSize = (ship: string, name: string) => {
|
|||||||
return graph?.size ?? 0;
|
return graph?.size ?? 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
type ChatResourceProps = StoreState & {
|
type ChatResourceProps = StoreState & {
|
||||||
association: Association;
|
association: Association;
|
||||||
api: GlobalApi;
|
api: GlobalApi;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChatResource(props: ChatResourceProps) {
|
const ChatResource = (props: ChatResourceProps): ReactElement => {
|
||||||
const { association, api } = props;
|
const { association, api } = props;
|
||||||
const { resource } = association;
|
const { resource } = association;
|
||||||
const [toShare, setToShare] = useState<string[] | string | undefined>();
|
const [toShare, setToShare] = useState<string[] | string | undefined>();
|
||||||
const group = useGroupForAssoc(association)!;
|
const group = useGroupForAssoc(association)!;
|
||||||
const graph = useGraphForAssoc(association);
|
const graph = useGraphForAssoc(association);
|
||||||
const unreads = useHarkState((state) => state.unreads);
|
const unreads = useHarkState(state => state.unreads);
|
||||||
const unreadCount =
|
const unreadCount =
|
||||||
(unreads.graph?.[resource]?.['/']?.unreads as number) || 0;
|
(unreads.graph?.[resource]?.['/']?.unreads as number) || 0;
|
||||||
const canWrite = group ? isWriter(group, resource) : false;
|
const canWrite = group ? isWriter(group, resource) : false;
|
||||||
@ -59,12 +45,12 @@ function ChatResource(props: ChatResourceProps) {
|
|||||||
const { ship, name } = resourceFromPath(resource);
|
const { ship, name } = resourceFromPath(resource);
|
||||||
props.api.graph.getNewest(ship, name, count);
|
props.api.graph.getNewest(ship, name, count);
|
||||||
setToShare(undefined);
|
setToShare(undefined);
|
||||||
(async function() {
|
(async function () {
|
||||||
if(group.hidden) {
|
if (group.hidden) {
|
||||||
const members = await props.api.contacts.disallowedShipsForOurContact(
|
const members = await props.api.contacts.disallowedShipsForOurContact(
|
||||||
Array.from(group.members)
|
Array.from(group.members)
|
||||||
);
|
);
|
||||||
if(members.length > 0) {
|
if (members.length > 0) {
|
||||||
setToShare(members);
|
setToShare(members);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -75,7 +61,7 @@ function ChatResource(props: ChatResourceProps) {
|
|||||||
groupHost,
|
groupHost,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
if(!shared) {
|
if (!shared) {
|
||||||
setToShare(association.group);
|
setToShare(association.group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +94,7 @@ function ChatResource(props: ChatResourceProps) {
|
|||||||
const expectedSize = graphSize + pageSize;
|
const expectedSize = graphSize + pageSize;
|
||||||
if (newer) {
|
if (newer) {
|
||||||
const index = graph.peekLargest()?.[0];
|
const index = graph.peekLargest()?.[0];
|
||||||
if(!index) {
|
if (!index) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await api.graph.getYoungerSiblings(
|
await api.graph.getYoungerSiblings(
|
||||||
@ -120,7 +106,7 @@ function ChatResource(props: ChatResourceProps) {
|
|||||||
return expectedSize !== getCurrGraphSize(ship.slice(1), name);
|
return expectedSize !== getCurrGraphSize(ship.slice(1), name);
|
||||||
} else {
|
} else {
|
||||||
const index = graph.peekSmallest()?.[0];
|
const index = graph.peekSmallest()?.[0];
|
||||||
if(!index) {
|
if (!index) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await api.graph.getOlderSiblings(ship, name, pageSize, `/${index.toString()}`);
|
await api.graph.getOlderSiblings(ship, name, pageSize, `/${index.toString()}`);
|
||||||
@ -131,7 +117,7 @@ function ChatResource(props: ChatResourceProps) {
|
|||||||
|
|
||||||
const onSubmit = useCallback((contents: Content[]) => {
|
const onSubmit = useCallback((contents: Content[]) => {
|
||||||
const { ship, name } = resourceFromPath(resource);
|
const { ship, name } = resourceFromPath(resource);
|
||||||
api.graph.addPost(ship, name, createPost(window.ship, contents))
|
api.graph.addPost(ship, name, createPost(window.ship, contents));
|
||||||
}, [resource]);
|
}, [resource]);
|
||||||
|
|
||||||
const dismissUnread = useCallback(() => {
|
const dismissUnread = useCallback(() => {
|
||||||
@ -164,6 +150,6 @@ function ChatResource(props: ChatResourceProps) {
|
|||||||
promptShare={toShare}
|
promptShare={toShare}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export { ChatResource };
|
export { ChatResource };
|
||||||
|
@ -1,23 +1,15 @@
|
|||||||
import React, { Component } from 'react';
|
/* eslint-disable max-lines-per-function */
|
||||||
import { UnControlled as CodeEditor } from 'react-codemirror2';
|
import { BaseTextArea, Box, Row } from '@tlon/indigo-react';
|
||||||
import { MOBILE_BROWSER_REGEX } from "~/logic/lib/util";
|
|
||||||
import CodeMirror from 'codemirror';
|
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
import { Row, BaseTextArea, Box } from '@tlon/indigo-react';
|
|
||||||
|
|
||||||
import 'codemirror/mode/markdown/markdown';
|
|
||||||
import 'codemirror/addon/display/placeholder';
|
import 'codemirror/addon/display/placeholder';
|
||||||
import 'codemirror/addon/hint/show-hint';
|
import 'codemirror/addon/hint/show-hint';
|
||||||
|
|
||||||
import 'codemirror/lib/codemirror.css';
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
import 'codemirror/mode/markdown/markdown';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { UnControlled as CodeEditor } from 'react-codemirror2';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { MOBILE_BROWSER_REGEX } from '~/logic/lib/util';
|
||||||
import '../css/custom.css';
|
import '../css/custom.css';
|
||||||
|
|
||||||
const BROWSER_REGEX =
|
|
||||||
new RegExp(String(/Android|webOS|iPhone|iPad|iPod|BlackBerry/i));
|
|
||||||
|
|
||||||
|
|
||||||
const MARKDOWN_CONFIG = {
|
const MARKDOWN_CONFIG = {
|
||||||
name: 'markdown',
|
name: 'markdown',
|
||||||
tokenTypeOverrides: {
|
tokenTypeOverrides: {
|
||||||
@ -40,12 +32,12 @@ const MARKDOWN_CONFIG = {
|
|||||||
|
|
||||||
// Until CodeMirror supports options.inputStyle = 'textarea' on mobile,
|
// Until CodeMirror supports options.inputStyle = 'textarea' on mobile,
|
||||||
// we need to hack this into a regular input that has some funny behaviors
|
// we need to hack this into a regular input that has some funny behaviors
|
||||||
const inputProxy = (input) => new Proxy(input, {
|
const inputProxy = input => new Proxy(input, {
|
||||||
get(target, property) {
|
get(target, property) {
|
||||||
if(property === 'focus') {
|
if(property === 'focus') {
|
||||||
return () => {
|
return () => {
|
||||||
target.focus();
|
target.focus();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (property in target) {
|
if (property in target) {
|
||||||
return target[property];
|
return target[property];
|
||||||
@ -55,7 +47,7 @@ const inputProxy = (input) => new Proxy(input, {
|
|||||||
target.setSelectionRange(target.value.length, target.value.length);
|
target.setSelectionRange(target.value.length, target.value.length);
|
||||||
input.blur();
|
input.blur();
|
||||||
input.focus();
|
input.focus();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (property === 'setOption') {
|
if (property === 'setOption') {
|
||||||
return () => {};
|
return () => {};
|
||||||
@ -64,7 +56,9 @@ const inputProxy = (input) => new Proxy(input, {
|
|||||||
return () => target.value;
|
return () => target.value;
|
||||||
}
|
}
|
||||||
if (property === 'setValue') {
|
if (property === 'setValue') {
|
||||||
return (val) => target.value = val;
|
return (val) => {
|
||||||
|
target.value = val;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (property === 'element') {
|
if (property === 'element') {
|
||||||
return input;
|
return input;
|
||||||
@ -100,24 +94,40 @@ const MobileBox = styled(Box)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default class ChatEditor extends Component {
|
interface ChatEditorProps {
|
||||||
constructor(props) {
|
message: string;
|
||||||
|
inCodeMode: boolean;
|
||||||
|
submit: (message: string) => void;
|
||||||
|
onUnmount: (message: string) => void;
|
||||||
|
onPaste: () => void;
|
||||||
|
changeEvent: (message: string) => void;
|
||||||
|
placeholder: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChatEditorState {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ChatEditor extends Component<ChatEditorProps, ChatEditorState> {
|
||||||
|
editor: ProxyHandler<unknown> | null;
|
||||||
|
constructor(props: ChatEditorProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
message: props.message
|
message: props.message
|
||||||
};
|
};
|
||||||
|
|
||||||
this.editor = null;
|
this.editor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount(): void {
|
||||||
this.props.onUnmount(this.state.message);
|
this.props.onUnmount(this.state.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps: ChatEditorProps): void {
|
||||||
const { props } = this;
|
const { props } = this;
|
||||||
|
|
||||||
if (prevProps.message !== props.message) {
|
if (prevProps.message !== props.message && this.editor) {
|
||||||
this.editor.setValue(props.message);
|
this.editor.setValue(props.message);
|
||||||
this.editor.setOption('mode', MARKDOWN_CONFIG);
|
this.editor.setOption('mode', MARKDOWN_CONFIG);
|
||||||
this.editor?.focus();
|
this.editor?.focus();
|
||||||
@ -147,7 +157,7 @@ export default class ChatEditor extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let editorMessage = this.editor.getValue();
|
const editorMessage = this.editor.getValue();
|
||||||
if (editorMessage === '') {
|
if (editorMessage === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -203,9 +213,9 @@ export default class ChatEditor extends Component {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
// The below will ony work once codemirror's bug is fixed
|
// The below will ony work once codemirror's bug is fixed
|
||||||
spellcheck: !!MOBILE_BROWSER_REGEX.test(navigator.userAgent),
|
spellcheck: Boolean(MOBILE_BROWSER_REGEX.test(navigator.userAgent)),
|
||||||
autocorrect: !!MOBILE_BROWSER_REGEX.test(navigator.userAgent),
|
autocorrect: Boolean(MOBILE_BROWSER_REGEX.test(navigator.userAgent)),
|
||||||
autocapitalize: !!MOBILE_BROWSER_REGEX.test(navigator.userAgent)
|
autocapitalize: Boolean(MOBILE_BROWSER_REGEX.test(navigator.userAgent))
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -227,7 +237,7 @@ export default class ChatEditor extends Component {
|
|||||||
data-value={this.state.message}
|
data-value={this.state.message}
|
||||||
fontSize="1"
|
fontSize="1"
|
||||||
lineHeight="tall"
|
lineHeight="tall"
|
||||||
onClick={event => {
|
onClick={(event) => {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor.element.focus();
|
this.editor.element.focus();
|
||||||
}
|
}
|
||||||
@ -237,17 +247,18 @@ export default class ChatEditor extends Component {
|
|||||||
fontFamily={inCodeMode ? 'Source Code Pro' : 'Inter'}
|
fontFamily={inCodeMode ? 'Source Code Pro' : 'Inter'}
|
||||||
fontSize="1"
|
fontSize="1"
|
||||||
lineHeight="tall"
|
lineHeight="tall"
|
||||||
rows="1"
|
rows={1}
|
||||||
style={{ width: '100%', background: 'transparent', color: 'currentColor' }}
|
style={{ width: '100%', background: 'transparent', color: 'currentColor' }}
|
||||||
placeholder={inCodeMode ? "Code..." : "Message..."}
|
placeholder={inCodeMode ? 'Code...' : 'Message...'}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
this.messageChange(null, null, event.target.value)
|
this.messageChange(null, null, event.target.value)
|
||||||
}
|
}
|
||||||
onKeyDown={event =>
|
onKeyDown={event =>
|
||||||
this.messageChange(null, null, event.target.value)
|
this.messageChange(null, null, event.target.value)
|
||||||
}
|
}
|
||||||
ref={input => {
|
ref={(input) => {
|
||||||
if (!input) return;
|
if (!input)
|
||||||
|
return;
|
||||||
this.editor = inputProxy(input);
|
this.editor = inputProxy(input);
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
@ -263,7 +274,7 @@ export default class ChatEditor extends Component {
|
|||||||
editor.focus();
|
editor.focus();
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
</Row>
|
</Row>
|
@ -1,16 +1,14 @@
|
|||||||
|
import { BaseImage, Box, Icon, LoadingSpinner, Row } from '@tlon/indigo-react';
|
||||||
|
import { Contact, Content } from '@urbit/api';
|
||||||
import React, { Component, ReactNode } from 'react';
|
import React, { Component, ReactNode } from 'react';
|
||||||
import ChatEditor from './chat-editor';
|
|
||||||
import { IuseStorage } from '~/logic/lib/useStorage';
|
|
||||||
import { uxToHex } from '~/logic/lib/util';
|
|
||||||
import { Sigil } from '~/logic/lib/sigil';
|
|
||||||
import { createPost } from '~/logic/api/graph';
|
|
||||||
import tokenizeMessage, { isUrl } from '~/logic/lib/tokenizeMessage';
|
|
||||||
import GlobalApi from '~/logic/api/global';
|
import GlobalApi from '~/logic/api/global';
|
||||||
import { Contact, Contacts, Content, Post } from '@urbit/api';
|
import { Sigil } from '~/logic/lib/sigil';
|
||||||
import { Row, BaseImage, Box, Icon, LoadingSpinner } from '@tlon/indigo-react';
|
import tokenizeMessage from '~/logic/lib/tokenizeMessage';
|
||||||
import withStorage from '~/views/components/withStorage';
|
import { IuseStorage } from '~/logic/lib/useStorage';
|
||||||
|
import { MOBILE_BROWSER_REGEX, uxToHex } from '~/logic/lib/util';
|
||||||
import { withLocalState } from '~/logic/state/local';
|
import { withLocalState } from '~/logic/state/local';
|
||||||
import { MOBILE_BROWSER_REGEX } from "~/logic/lib/util";
|
import withStorage from '~/views/components/withStorage';
|
||||||
|
import ChatEditor from './ChatEditor';
|
||||||
|
|
||||||
type ChatInputProps = IuseStorage & {
|
type ChatInputProps = IuseStorage & {
|
||||||
api: GlobalApi;
|
api: GlobalApi;
|
||||||
@ -82,7 +80,7 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
|||||||
this.chatEditor.current.editor.setValue(url);
|
this.chatEditor.current.editor.setValue(url);
|
||||||
this.setState({ uploadingPaste: false });
|
this.setState({ uploadingPaste: false });
|
||||||
} else {
|
} else {
|
||||||
props.onSubmit([{ url }])
|
props.onSubmit([{ url }]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,4 +229,4 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
|||||||
export default withLocalState<Omit<ChatInputProps, keyof IuseStorage>, 'hideAvatars', ChatInput>(
|
export default withLocalState<Omit<ChatInputProps, keyof IuseStorage>, 'hideAvatars', ChatInput>(
|
||||||
withStorage<ChatInputProps, ChatInput>(ChatInput, { accept: 'image/*' }),
|
withStorage<ChatInputProps, ChatInput>(ChatInput, { accept: 'image/*' }),
|
||||||
['hideAvatars']
|
['hideAvatars']
|
||||||
)
|
);
|
||||||
|
@ -1,33 +1,28 @@
|
|||||||
/* eslint-disable max-lines-per-function */
|
/* eslint-disable max-lines-per-function */
|
||||||
|
import { BaseImage, Box, Col, Icon, Row, Rule, Text } from '@tlon/indigo-react';
|
||||||
|
import { Contact, Post } from '@urbit/api';
|
||||||
import bigInt from 'big-integer';
|
import bigInt from 'big-integer';
|
||||||
import React, {
|
|
||||||
useState,
|
|
||||||
useEffect,
|
|
||||||
useMemo
|
|
||||||
} from 'react';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import React, {
|
||||||
|
useEffect,
|
||||||
|
useMemo, useState
|
||||||
|
} from 'react';
|
||||||
import VisibilitySensor from 'react-visibility-sensor';
|
import VisibilitySensor from 'react-visibility-sensor';
|
||||||
import { Box, Row, Text, Rule, BaseImage, Icon, Col } from '@tlon/indigo-react';
|
import GlobalApi from '~/logic/api/global';
|
||||||
|
import { useIdlingState } from '~/logic/lib/idling';
|
||||||
import { Sigil } from '~/logic/lib/sigil';
|
import { Sigil } from '~/logic/lib/sigil';
|
||||||
|
import { useCopy } from '~/logic/lib/useCopy';
|
||||||
import {
|
import {
|
||||||
uxToHex,
|
|
||||||
cite,
|
cite,
|
||||||
useShowNickname,
|
|
||||||
useHovering,
|
daToUnix, useHovering, useShowNickname, uxToHex
|
||||||
daToUnix
|
|
||||||
} from '~/logic/lib/util';
|
} from '~/logic/lib/util';
|
||||||
import { Post } from '@urbit/api';
|
import { useContact } from '~/logic/state/contact';
|
||||||
import { Dropdown } from '~/views/components/Dropdown';
|
|
||||||
import useLocalState from '~/logic/state/local';
|
import useLocalState from '~/logic/state/local';
|
||||||
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
||||||
import useContactState, {useContact} from '~/logic/state/contact';
|
import { Dropdown } from '~/views/components/Dropdown';
|
||||||
import { useIdlingState } from '~/logic/lib/idling';
|
|
||||||
import ProfileOverlay from '~/views/components/ProfileOverlay';
|
import ProfileOverlay from '~/views/components/ProfileOverlay';
|
||||||
import {useCopy} from '~/logic/lib/useCopy';
|
import { GraphContentWide } from '~/views/landscape/components/Graph/GraphContentWide';
|
||||||
import {GraphContentWide} from '~/views/landscape/components/Graph/GraphContentWide';
|
|
||||||
import {Contact} from '@urbit/api';
|
|
||||||
import GlobalApi from '~/logic/api/global';
|
|
||||||
|
|
||||||
|
|
||||||
export const DATESTAMP_FORMAT = '[~]YYYY.M.D';
|
export const DATESTAMP_FORMAT = '[~]YYYY.M.D';
|
||||||
|
|
||||||
@ -36,7 +31,6 @@ interface DayBreakProps {
|
|||||||
shimTop?: boolean;
|
shimTop?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const DayBreak = ({ when, shimTop = false }: DayBreakProps) => (
|
export const DayBreak = ({ when, shimTop = false }: DayBreakProps) => (
|
||||||
<Row
|
<Row
|
||||||
px={2}
|
px={2}
|
||||||
@ -168,12 +162,12 @@ const MessageActions = ({ api, onReply, association, msg, isAdmin, permalink })
|
|||||||
{copyDisplay}
|
{copyDisplay}
|
||||||
</MessageActionItem>
|
</MessageActionItem>
|
||||||
{false && (isAdmin() || isOwn()) ? (
|
{false && (isAdmin() || isOwn()) ? (
|
||||||
<MessageActionItem onClick={(e) => console.log(e)} color='red'>
|
<MessageActionItem onClick={e => console.log(e)} color='red'>
|
||||||
Delete Message
|
Delete Message
|
||||||
</MessageActionItem>
|
</MessageActionItem>
|
||||||
) : null}
|
) : null}
|
||||||
{false && (
|
{false && (
|
||||||
<MessageActionItem onClick={(e) => console.log(e)}>
|
<MessageActionItem onClick={e => console.log(e)}>
|
||||||
View Signature
|
View Signature
|
||||||
</MessageActionItem>
|
</MessageActionItem>
|
||||||
)}
|
)}
|
||||||
@ -250,7 +244,7 @@ function ChatMessage(props: ChatMessageProps) {
|
|||||||
permalink
|
permalink
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let onReply = props?.onReply ?? (() => {});
|
const onReply = props?.onReply ?? (() => {});
|
||||||
const transcluded = props?.transcluded ?? 0;
|
const transcluded = props?.transcluded ?? 0;
|
||||||
const renderSigil = props.renderSigil ?? (Boolean(nextMsg && msg.author !== nextMsg.author) ||
|
const renderSigil = props.renderSigil ?? (Boolean(nextMsg && msg.author !== nextMsg.author) ||
|
||||||
!nextMsg ||
|
!nextMsg ||
|
||||||
@ -278,7 +272,7 @@ function ChatMessage(props: ChatMessageProps) {
|
|||||||
nextDate &&
|
nextDate &&
|
||||||
new Date(date).getDate() !==
|
new Date(date).getDate() !==
|
||||||
new Date(nextDate).getDate()
|
new Date(nextDate).getDate()
|
||||||
, [nextDate, date])
|
, [nextDate, date]);
|
||||||
|
|
||||||
const containerClass = `${isPending ? 'o-40' : ''} ${className}`;
|
const containerClass = `${isPending ? 'o-40' : ''} ${className}`;
|
||||||
|
|
||||||
@ -350,11 +344,11 @@ export const MessageAuthor = ({
|
|||||||
timestamp,
|
timestamp,
|
||||||
msg,
|
msg,
|
||||||
api,
|
api,
|
||||||
showOurContact,
|
showOurContact
|
||||||
}) => {
|
}) => {
|
||||||
const osDark = useLocalState((state) => state.dark);
|
const osDark = useLocalState(state => state.dark);
|
||||||
|
|
||||||
const theme = useSettingsState((s) => s.display.theme);
|
const theme = useSettingsState(s => s.display.theme);
|
||||||
const dark = theme === 'dark' || (theme === 'auto' && osDark);
|
const dark = theme === 'dark' || (theme === 'auto' && osDark);
|
||||||
let contact: Contact | null = useContact(`~${msg.author}`);
|
let contact: Contact | null = useContact(`~${msg.author}`);
|
||||||
|
|
||||||
@ -473,7 +467,7 @@ export const MessageAuthor = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
type MessageProps = { timestamp: string; timestampHover: boolean; }
|
type MessageProps = { timestamp: string; timestampHover: boolean; }
|
||||||
& Pick<ChatMessageProps, "msg" | "api" | "transcluded" | "showOurContact">
|
& Pick<ChatMessageProps, 'msg' | 'api' | 'transcluded' | 'showOurContact'>
|
||||||
|
|
||||||
export const Message = React.memo(({
|
export const Message = React.memo(({
|
||||||
timestamp,
|
timestamp,
|
||||||
|
@ -1,27 +1,18 @@
|
|||||||
import React, { useRef, useCallback, useEffect, useState } from 'react';
|
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
|
||||||
import { Col } from '@tlon/indigo-react';
|
import { Col } from '@tlon/indigo-react';
|
||||||
import _ from 'lodash';
|
import { Content, Graph, Post } from '@urbit/api';
|
||||||
import bigInt, { BigInteger } from 'big-integer';
|
import bigInt, { BigInteger } from 'big-integer';
|
||||||
|
import _ from 'lodash';
|
||||||
import { Association } from '@urbit/api/metadata';
|
import React, { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { StoreState } from '~/logic/store/type';
|
|
||||||
import { useFileDrag } from '~/logic/lib/useDrag';
|
|
||||||
import ChatWindow from './ChatWindow';
|
|
||||||
import ChatInput from './ChatInput';
|
|
||||||
import GlobalApi from '~/logic/api/global';
|
import GlobalApi from '~/logic/api/global';
|
||||||
import { ShareProfile } from '~/views/apps/chat/components/ShareProfile';
|
import { useFileDrag } from '~/logic/lib/useDrag';
|
||||||
import SubmitDragger from '~/views/components/SubmitDragger';
|
|
||||||
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
|
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
|
||||||
import { Loading } from '~/views/components/Loading';
|
import { useOurContact } from '~/logic/state/contact';
|
||||||
import { isWriter, resourceFromPath } from '~/logic/lib/group';
|
|
||||||
|
|
||||||
import useContactState, { useOurContact } from '~/logic/state/contact';
|
|
||||||
import useGraphState from '~/logic/state/graph';
|
import useGraphState from '~/logic/state/graph';
|
||||||
import useGroupState from '~/logic/state/group';
|
import ShareProfile from '~/views/apps/chat/components/ShareProfile';
|
||||||
import useHarkState from '~/logic/state/hark';
|
import { Loading } from '~/views/components/Loading';
|
||||||
import { Post, Graph, Content } from '@urbit/api';
|
import SubmitDragger from '~/views/components/SubmitDragger';
|
||||||
import { getPermalinkForGraph } from '~/logic/lib/permalinks';
|
import ChatInput from './ChatInput';
|
||||||
|
import ChatWindow from './ChatWindow';
|
||||||
|
|
||||||
interface ChatPaneProps {
|
interface ChatPaneProps {
|
||||||
/**
|
/**
|
||||||
@ -73,7 +64,7 @@ interface ChatPaneProps {
|
|||||||
promptShare?: string[] | string;
|
promptShare?: string[] | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatPane(props: ChatPaneProps) {
|
export function ChatPane(props: ChatPaneProps): ReactElement {
|
||||||
const {
|
const {
|
||||||
api,
|
api,
|
||||||
graph,
|
graph,
|
||||||
@ -87,7 +78,7 @@ export function ChatPane(props: ChatPaneProps) {
|
|||||||
promptShare = [],
|
promptShare = [],
|
||||||
fetchMessages
|
fetchMessages
|
||||||
} = props;
|
} = props;
|
||||||
const graphTimesentMap = useGraphState((state) => state.graphTimesentMap);
|
const graphTimesentMap = useGraphState(state => state.graphTimesentMap);
|
||||||
const ourContact = useOurContact();
|
const ourContact = useOurContact();
|
||||||
const chatInput = useRef<ChatInput>();
|
const chatInput = useRef<ChatInput>();
|
||||||
|
|
||||||
@ -109,7 +100,7 @@ export function ChatPane(props: ChatPaneProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const appendUnsent = useCallback(
|
const appendUnsent = useCallback(
|
||||||
(u: string) => setUnsent((s) => ({ ...s, [id]: u })),
|
(u: string) => setUnsent(s => ({ ...s, [id]: u })),
|
||||||
[id]
|
[id]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -133,7 +124,7 @@ export function ChatPane(props: ChatPaneProps) {
|
|||||||
const onReply = useCallback(
|
const onReply = useCallback(
|
||||||
(msg: Post) => {
|
(msg: Post) => {
|
||||||
const message = props.onReply(msg);
|
const message = props.onReply(msg);
|
||||||
setUnsent((s) => ({ ...s, [id]: message }));
|
setUnsent(s => ({ ...s, [id]: message }));
|
||||||
},
|
},
|
||||||
[id, props.onReply]
|
[id, props.onReply]
|
||||||
);
|
);
|
||||||
|
@ -1,43 +1,23 @@
|
|||||||
import React, { useEffect, Component, useRef, useState, useCallback } from 'react';
|
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import bigInt, { BigInteger } from 'big-integer';
|
|
||||||
|
|
||||||
import { Col } from '@tlon/indigo-react';
|
import { Col } from '@tlon/indigo-react';
|
||||||
import {
|
import {
|
||||||
Patp,
|
|
||||||
Contacts,
|
|
||||||
Association,
|
|
||||||
Associations,
|
|
||||||
Group,
|
|
||||||
Groups,
|
|
||||||
Graph,
|
Graph,
|
||||||
Post,
|
|
||||||
GraphNode
|
GraphNode, Post
|
||||||
} from '@urbit/api';
|
} from '@urbit/api';
|
||||||
|
import bigInt, { BigInteger } from 'big-integer';
|
||||||
|
import React, { Component } from 'react';
|
||||||
import GlobalApi from '~/logic/api/global';
|
import GlobalApi from '~/logic/api/global';
|
||||||
|
|
||||||
import VirtualScroller from '~/views/components/VirtualScroller';
|
import VirtualScroller from '~/views/components/VirtualScroller';
|
||||||
|
|
||||||
import ChatMessage, { MessagePlaceholder } from './ChatMessage';
|
import ChatMessage, { MessagePlaceholder } from './ChatMessage';
|
||||||
import { UnreadNotice } from './unread-notice';
|
import UnreadNotice from './UnreadNotice';
|
||||||
import withState from '~/logic/lib/withState';
|
|
||||||
import useGroupState from '~/logic/state/group';
|
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
|
||||||
import useGraphState from '~/logic/state/graph';
|
|
||||||
|
|
||||||
const INITIAL_LOAD = 20;
|
|
||||||
const DEFAULT_BACKLOG_SIZE = 100;
|
|
||||||
const IDLE_THRESHOLD = 64;
|
const IDLE_THRESHOLD = 64;
|
||||||
const MAX_BACKLOG_SIZE = 1000;
|
|
||||||
|
|
||||||
|
|
||||||
type ChatWindowProps = {
|
type ChatWindowProps = {
|
||||||
unreadCount: number;
|
unreadCount: number;
|
||||||
graph: Graph;
|
graph: Graph;
|
||||||
graphSize: number;
|
graphSize: number;
|
||||||
station: any;
|
station: unknown;
|
||||||
fetchMessages: (newer: boolean) => Promise<boolean>;
|
fetchMessages: (newer: boolean) => Promise<boolean>;
|
||||||
api: GlobalApi;
|
api: GlobalApi;
|
||||||
scrollTo?: BigInteger;
|
scrollTo?: BigInteger;
|
||||||
@ -58,7 +38,6 @@ interface ChatWindowState {
|
|||||||
|
|
||||||
const virtScrollerStyle = { height: '100%' };
|
const virtScrollerStyle = { height: '100%' };
|
||||||
|
|
||||||
|
|
||||||
class ChatWindow extends Component<
|
class ChatWindow extends Component<
|
||||||
ChatWindowProps,
|
ChatWindowProps,
|
||||||
ChatWindowState
|
ChatWindowState
|
||||||
@ -97,7 +76,6 @@ class ChatWindow extends Component<
|
|||||||
this.virtualList!.scrollToIndex(this.props.scrollTo);
|
this.virtualList!.scrollToIndex(this.props.scrollTo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}, this.INITIALIZATION_MAX_TIME);
|
}, this.INITIALIZATION_MAX_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,26 +99,26 @@ class ChatWindow extends Component<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissedInitialUnread() {
|
dismissedInitialUnread(): void {
|
||||||
const { unreadCount, graph } = this.props;
|
const { unreadCount, graph } = this.props;
|
||||||
|
|
||||||
return this.state.unreadIndex.eq(bigInt.zero) ? unreadCount > graph.size :
|
return this.state.unreadIndex.eq(bigInt.zero) ? unreadCount > graph.size :
|
||||||
this.state.unreadIndex.neq(graph.keys()?.[unreadCount]?.[0] ?? bigInt.zero);
|
this.state.unreadIndex.neq(graph.keys()?.[unreadCount]?.[0] ?? bigInt.zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowBlur() {
|
handleWindowBlur(): void {
|
||||||
this.setState({ idle: true });
|
this.setState({ idle: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWindowFocus() {
|
handleWindowFocus(): void {
|
||||||
this.setState({ idle: false });
|
this.setState({ idle: false });
|
||||||
if (this.virtualList?.window?.scrollTop === 0) {
|
if (this.virtualList?.window?.scrollTop === 0) {
|
||||||
this.props.dismissUnread();
|
this.props.dismissUnread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: ChatWindowProps, prevState) {
|
componentDidUpdate(prevProps: ChatWindowProps): void {
|
||||||
const { graph, unreadCount, graphSize, station } = this.props;
|
const { unreadCount, graphSize, station } = this.props;
|
||||||
if(unreadCount === 0 && prevProps.unreadCount !== unreadCount) {
|
if(unreadCount === 0 && prevProps.unreadCount !== unreadCount) {
|
||||||
this.unreadSet = true;
|
this.unreadSet = true;
|
||||||
}
|
}
|
||||||
@ -155,7 +133,6 @@ class ChatWindow extends Component<
|
|||||||
this.virtualList!.startOffset() < 5) {
|
this.virtualList!.startOffset() < 5) {
|
||||||
this.props.dismissUnread();
|
this.props.dismissUnread();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unreadCount > prevProps.unreadCount) {
|
if (unreadCount > prevProps.unreadCount) {
|
||||||
@ -168,7 +145,7 @@ class ChatWindow extends Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stayLockedIfActive() {
|
stayLockedIfActive(): void {
|
||||||
if (this.virtualList && !this.state.idle) {
|
if (this.virtualList && !this.state.idle) {
|
||||||
this.virtualList.resetScroll();
|
this.virtualList.resetScroll();
|
||||||
this.props.dismissUnread();
|
this.props.dismissUnread();
|
||||||
@ -181,7 +158,7 @@ class ChatWindow extends Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToUnread() {
|
scrollToUnread(): void {
|
||||||
const { unreadIndex } = this.state;
|
const { unreadIndex } = this.state;
|
||||||
if (unreadIndex.eq(bigInt.zero)) {
|
if (unreadIndex.eq(bigInt.zero)) {
|
||||||
return;
|
return;
|
||||||
@ -190,13 +167,12 @@ class ChatWindow extends Component<
|
|||||||
this.virtualList?.scrollToIndex(this.state.unreadIndex);
|
this.virtualList?.scrollToIndex(this.state.unreadIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
onScroll = ({ scrollTop, scrollHeight, windowHeight }) => {
|
onScroll = ({ scrollTop }) => {
|
||||||
if (!this.state.idle && scrollTop > IDLE_THRESHOLD) {
|
if (!this.state.idle && scrollTop > IDLE_THRESHOLD) {
|
||||||
this.setState({ idle: true });
|
this.setState({ idle: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
renderer = React.forwardRef(({ index, scrollWindow }, ref) => {
|
renderer = React.forwardRef(({ index, scrollWindow }, ref) => {
|
||||||
const {
|
const {
|
||||||
api,
|
api,
|
||||||
@ -205,7 +181,7 @@ class ChatWindow extends Component<
|
|||||||
onReply,
|
onReply,
|
||||||
getPermalink,
|
getPermalink,
|
||||||
dismissUnread,
|
dismissUnread,
|
||||||
isAdmin,
|
isAdmin
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const permalink = getPermalink(index);
|
const permalink = getPermalink(index);
|
||||||
const messageProps = {
|
const messageProps = {
|
||||||
@ -218,7 +194,8 @@ class ChatWindow extends Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
const msg = graph.get(index)?.post;
|
const msg = graph.get(index)?.post;
|
||||||
if (!msg) return null;
|
if (!msg)
|
||||||
|
return null;
|
||||||
if (!this.state.initialized) {
|
if (!this.state.initialized) {
|
||||||
return (
|
return (
|
||||||
<MessagePlaceholder
|
<MessagePlaceholder
|
||||||
@ -234,7 +211,7 @@ class ChatWindow extends Component<
|
|||||||
);
|
);
|
||||||
const highlighted = index.eq(this.props.scrollTo ?? bigInt.zero);
|
const highlighted = index.eq(this.props.scrollTo ?? bigInt.zero);
|
||||||
const keys = graph.keys();
|
const keys = graph.keys();
|
||||||
const graphIdx = keys.findIndex((idx) => idx.eq(index));
|
const graphIdx = keys.findIndex(idx => idx.eq(index));
|
||||||
const prevIdx = keys[graphIdx - 1];
|
const prevIdx = keys[graphIdx - 1];
|
||||||
const nextIdx = keys[graphIdx + 1];
|
const nextIdx = keys[graphIdx + 1];
|
||||||
const isLastRead: boolean = this.state.unreadIndex.eq(index);
|
const isLastRead: boolean = this.state.unreadIndex.eq(index);
|
||||||
@ -262,17 +239,15 @@ class ChatWindow extends Component<
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
unreadCount,
|
unreadCount,
|
||||||
api,
|
|
||||||
graph,
|
graph,
|
||||||
showOurContact,
|
pendingSize = 0
|
||||||
pendingSize = 0,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const unreadMsg = graph.get(this.state.unreadIndex);
|
const unreadMsg = graph.get(this.state.unreadIndex);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col height='100%' overflow='hidden' position='relative'>
|
<Col height='100%' overflow='hidden' position='relative'>
|
||||||
{ this.dismissedInitialUnread() &&
|
{ this.dismissedInitialUnread() &&
|
||||||
(<UnreadNotice
|
(<UnreadNotice
|
||||||
unreadCount={unreadCount}
|
unreadCount={unreadCount}
|
||||||
unreadMsg={
|
unreadMsg={
|
||||||
@ -284,7 +259,7 @@ class ChatWindow extends Component<
|
|||||||
}
|
}
|
||||||
dismissUnread={this.props.dismissUnread}
|
dismissUnread={this.props.dismissUnread}
|
||||||
onClick={this.scrollToUnread}
|
onClick={this.scrollToUnread}
|
||||||
/>)}
|
/>)}
|
||||||
<VirtualScroller<GraphNode>
|
<VirtualScroller<GraphNode>
|
||||||
ref={(list) => {
|
ref={(list) => {
|
||||||
this.virtualList = list;
|
this.virtualList = list;
|
||||||
@ -306,5 +281,4 @@ class ChatWindow extends Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default ChatWindow;
|
||||||
export default ChatWindow
|
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import React, {
|
import { BaseImage, Box, Row, Text } from '@tlon/indigo-react';
|
||||||
useState,
|
import { Contact } from '@urbit/api';
|
||||||
useEffect
|
import React, { ReactElement } from 'react';
|
||||||
} from 'react';
|
import GlobalApi from '~/logic/api/global';
|
||||||
import _ from 'lodash';
|
|
||||||
import { Box, Row, Text, BaseImage } from '@tlon/indigo-react';
|
|
||||||
import { uxToHex } from '~/logic/lib/util';
|
|
||||||
import { Sigil } from '~/logic/lib/sigil';
|
import { Sigil } from '~/logic/lib/sigil';
|
||||||
|
import { uxToHex } from '~/logic/lib/util';
|
||||||
|
|
||||||
export const ShareProfile = (props) => {
|
interface ShareProfileProps {
|
||||||
|
our?: Contact;
|
||||||
|
api: GlobalApi;
|
||||||
|
recipients: string | string[];
|
||||||
|
onShare: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShareProfile = (props: ShareProfileProps): ReactElement | null => {
|
||||||
const {
|
const {
|
||||||
api,
|
api,
|
||||||
showBanner,
|
|
||||||
setShowBanner,
|
|
||||||
group,
|
|
||||||
groupPath,
|
|
||||||
recipients
|
recipients
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -24,18 +25,21 @@ export const ShareProfile = (props) => {
|
|||||||
width='24px'
|
width='24px'
|
||||||
height='24px'
|
height='24px'
|
||||||
borderRadius={2}
|
borderRadius={2}
|
||||||
style={{ objectFit: 'cover' }} />
|
style={{ objectFit: 'cover' }}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Row
|
<Row
|
||||||
p={1}
|
p={1}
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
borderRadius={2}
|
borderRadius={2}
|
||||||
backgroundColor={!!props.our ? `#${uxToHex(props.our.color)}` : "#000000"}>
|
backgroundColor={props.our ? `#${uxToHex(props.our.color)}` : '#000000'}
|
||||||
|
>
|
||||||
<Sigil
|
<Sigil
|
||||||
ship={window.ship}
|
ship={window.ship}
|
||||||
size={16}
|
size={16}
|
||||||
color={!!props.our ? `#${uxToHex(props.our.color)}` : "#000000"}
|
color={props.our ? `#${uxToHex(props.our.color)}` : '#000000'}
|
||||||
icon />
|
icon
|
||||||
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -48,8 +52,8 @@ export const ShareProfile = (props) => {
|
|||||||
}
|
}
|
||||||
} else if(recipients.length > 0) {
|
} else if(recipients.length > 0) {
|
||||||
await api.contacts.allowShips(recipients);
|
await api.contacts.allowShips(recipients);
|
||||||
await Promise.all(recipients.map(r => api.contacts.share(r)))
|
await Promise.all(recipients.map(r => api.contacts.share(r)));
|
||||||
}
|
}
|
||||||
props.onShare();
|
props.onShare();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,3 +76,5 @@ export const ShareProfile = (props) => {
|
|||||||
</Row>
|
</Row>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default ShareProfile;
|
@ -1,11 +1,9 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { Box, Center, Icon, Text } from '@tlon/indigo-react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Box, Text, Center, Icon } from '@tlon/indigo-react';
|
import React, { ReactElement } from 'react';
|
||||||
import VisibilitySensor from 'react-visibility-sensor';
|
|
||||||
|
|
||||||
import Timestamp from '~/views/components/Timestamp';
|
import Timestamp from '~/views/components/Timestamp';
|
||||||
|
|
||||||
export const UnreadNotice = (props) => {
|
const UnreadNotice = (props): ReactElement | null => {
|
||||||
const { unreadCount, unreadMsg, dismissUnread, onClick } = props;
|
const { unreadCount, unreadMsg, dismissUnread, onClick } = props;
|
||||||
|
|
||||||
if (unreadCount === 0) {
|
if (unreadCount === 0) {
|
||||||
@ -20,7 +18,7 @@ export const UnreadNotice = (props) => {
|
|||||||
p='12px'
|
p='12px'
|
||||||
width='100%'
|
width='100%'
|
||||||
position='absolute'
|
position='absolute'
|
||||||
zIndex='1'
|
zIndex={1}
|
||||||
className='unread-notice'
|
className='unread-notice'
|
||||||
>
|
>
|
||||||
<Center>
|
<Center>
|
||||||
@ -66,3 +64,5 @@ export const UnreadNotice = (props) => {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default UnreadNotice;
|
@ -1,15 +1,18 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import { Center, Text } from '@tlon/indigo-react';
|
||||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
import React, { ReactElement } from 'react';
|
||||||
import { Center, Text } from "@tlon/indigo-react";
|
import { Route, Switch, useHistory } from 'react-router-dom';
|
||||||
|
import GlobalApi from '~/logic/api/global';
|
||||||
import { deSig } from '~/logic/lib/util';
|
import { deSig } from '~/logic/lib/util';
|
||||||
import useGraphState from '~/logic/state/graph';
|
import useGraphState from '~/logic/state/graph';
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
import useMetadataState from '~/logic/state/metadata';
|
||||||
import useGroupState from '~/logic/state/group';
|
|
||||||
|
|
||||||
const GraphApp = (props) => {
|
interface GraphAppProps {
|
||||||
|
api: GlobalApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GraphApp = (props: GraphAppProps): ReactElement => {
|
||||||
const associations= useMetadataState(state => state.associations);
|
const associations= useMetadataState(state => state.associations);
|
||||||
const graphKeys = useGraphState(state => state.graphKeys);
|
const graphKeys = useGraphState(state => state.graphKeys);
|
||||||
const groups = useGroupState(state => state.groups);
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const { api } = props;
|
const { api } = props;
|
||||||
@ -24,15 +27,12 @@ const GraphApp = (props) => {
|
|||||||
const path = `/ship/~${deSig(ship)}/${name}`;
|
const path = `/ship/~${deSig(ship)}/${name}`;
|
||||||
const association = associations.graph[path];
|
const association = associations.graph[path];
|
||||||
|
|
||||||
|
|
||||||
const autoJoin = () => {
|
const autoJoin = () => {
|
||||||
try {
|
try {
|
||||||
api.graph.joinGraph(
|
api.graph.joinGraph(
|
||||||
`~${deSig(props.match.params.ship)}`,
|
`~${deSig(props.match.params.ship)}`,
|
||||||
props.match.params.name
|
props.match.params.name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
setTimeout(autoJoin, 2000);
|
setTimeout(autoJoin, 2000);
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ const GraphApp = (props) => {
|
|||||||
|
|
||||||
if(!graphKeys.has(resource)) {
|
if(!graphKeys.has(resource)) {
|
||||||
autoJoin();
|
autoJoin();
|
||||||
} else if(!!association && 'graph' in association.config) {
|
} else if(Boolean(association) && 'graph' in association.config) {
|
||||||
history.push(`/~landscape/home/resource/${association.metadata.config.graph}${path}`);
|
history.push(`/~landscape/home/resource/${association.metadata.config.graph}${path}`);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@ -52,6 +52,6 @@ const GraphApp = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default GraphApp;
|
export default GraphApp;
|
@ -1,39 +1,37 @@
|
|||||||
import React, { useState, useMemo, useEffect } from 'react';
|
/* eslint-disable max-lines-per-function */
|
||||||
import styled from 'styled-components';
|
import { Box, Button, Col, Icon, Row, Text } from '@tlon/indigo-react';
|
||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import _ from 'lodash';
|
import React, { ReactElement, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { Col, Button, Box, Row, Icon, Text } from '@tlon/indigo-react';
|
|
||||||
|
|
||||||
import './css/custom.css';
|
|
||||||
import Tiles from './components/tiles';
|
|
||||||
import Tile from './components/tiles/tile';
|
|
||||||
import Groups from './components/Groups';
|
|
||||||
import ModalButton from './components/ModalButton';
|
|
||||||
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
|
|
||||||
import { StarIcon } from '~/views/components/StarIcon';
|
|
||||||
import { writeText } from '~/logic/lib/util';
|
|
||||||
import { useModal } from "~/logic/lib/useModal";
|
|
||||||
import { NewGroup } from "~/views/landscape/components/NewGroup";
|
|
||||||
import { JoinGroup } from "~/views/landscape/components/JoinGroup";
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import useLocalState from "~/logic/state/local";
|
import styled from 'styled-components';
|
||||||
import useHarkState from '~/logic/state/hark';
|
import GlobalApi from '~/logic/api/global';
|
||||||
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
|
|
||||||
import { useQuery } from "~/logic/lib/useQuery";
|
|
||||||
import {
|
import {
|
||||||
hasTutorialGroup,
|
hasTutorialGroup,
|
||||||
TUTORIAL_GROUP,
|
|
||||||
TUTORIAL_HOST,
|
|
||||||
TUTORIAL_BOOK,
|
TUTORIAL_BOOK,
|
||||||
TUTORIAL_CHAT,
|
TUTORIAL_CHAT, TUTORIAL_GROUP,
|
||||||
|
TUTORIAL_HOST,
|
||||||
|
|
||||||
TUTORIAL_LINKS
|
TUTORIAL_LINKS
|
||||||
} from '~/logic/lib/tutorialModal';
|
} from '~/logic/lib/tutorialModal';
|
||||||
|
import { useModal } from '~/logic/lib/useModal';
|
||||||
|
import { useQuery } from '~/logic/lib/useQuery';
|
||||||
|
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
|
||||||
|
import { writeText } from '~/logic/lib/util';
|
||||||
|
import useHarkState from '~/logic/state/hark';
|
||||||
import useLaunchState from '~/logic/state/launch';
|
import useLaunchState from '~/logic/state/launch';
|
||||||
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
import useLocalState from '~/logic/state/local';
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
import useMetadataState from '~/logic/state/metadata';
|
||||||
import {useHistory} from 'react-router-dom';
|
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
||||||
|
import { StarIcon } from '~/views/components/StarIcon';
|
||||||
|
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
|
||||||
|
import { JoinGroup } from '~/views/landscape/components/JoinGroup';
|
||||||
|
import { NewGroup } from '~/views/landscape/components/NewGroup';
|
||||||
|
import Groups from './components/Groups';
|
||||||
|
import ModalButton from './components/ModalButton';
|
||||||
|
import Tiles from './components/tiles';
|
||||||
|
import Tile from './components/tiles/tile';
|
||||||
|
import './css/custom.css';
|
||||||
|
|
||||||
const ScrollbarLessBox = styled(Box)`
|
const ScrollbarLessBox = styled(Box)`
|
||||||
scrollbar-width: none !important;
|
scrollbar-width: none !important;
|
||||||
@ -45,15 +43,19 @@ const ScrollbarLessBox = styled(Box)`
|
|||||||
|
|
||||||
const tutSelector = f.pick(['tutorialProgress', 'nextTutStep', 'hideGroups']);
|
const tutSelector = f.pick(['tutorialProgress', 'nextTutStep', 'hideGroups']);
|
||||||
|
|
||||||
export default function LaunchApp(props) {
|
interface LaunchAppProps {
|
||||||
|
connection: string;
|
||||||
|
api: GlobalApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LaunchApp = (props: LaunchAppProps): ReactElement | null => {
|
||||||
const { connection } = props;
|
const { connection } = props;
|
||||||
const baseHash = useLaunchState(state => state.baseHash);
|
const baseHash = useLaunchState(state => state.baseHash);
|
||||||
const [hashText, setHashText] = useState(baseHash);
|
const [hashText, setHashText] = useState(baseHash);
|
||||||
const [exitingTut, setExitingTut] = useState(false);
|
const [exitingTut, setExitingTut] = useState(false);
|
||||||
const seen = useSettingsState(s => s?.tutorial?.seen) ?? true;
|
const seen = useSettingsState(s => s?.tutorial?.seen) ?? true;
|
||||||
const associations = useMetadataState(s => s.associations);
|
const associations = useMetadataState(s => s.associations);
|
||||||
const history = useHistory();
|
const hasLoaded = useMemo(() => Boolean(connection === 'connected'), [connection]);
|
||||||
const hasLoaded = useMemo(() => Boolean(connection === "connected"), [connection]);
|
|
||||||
const notificationsCount = useHarkState(state => state.notificationsCount);
|
const notificationsCount = useHarkState(state => state.notificationsCount);
|
||||||
const calmState = useSettingsState(selectCalmState);
|
const calmState = useSettingsState(selectCalmState);
|
||||||
const { hideUtilities } = calmState;
|
const { hideUtilities } = calmState;
|
||||||
@ -64,7 +66,7 @@ export default function LaunchApp(props) {
|
|||||||
const waiter = useWaitForProps({ ...props, associations });
|
const waiter = useWaitForProps({ ...props, associations });
|
||||||
const hashBox = (
|
const hashBox = (
|
||||||
<Box
|
<Box
|
||||||
position={["relative", "absolute"]}
|
position={['relative', 'absolute']}
|
||||||
left="0"
|
left="0"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
@ -90,20 +92,10 @@ export default function LaunchApp(props) {
|
|||||||
|
|
||||||
const { query } = useQuery();
|
const { query } = useQuery();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if(query.get('tutorial')) {
|
|
||||||
if(hasTutorialGroup({ associations })) {
|
|
||||||
nextTutStep();
|
|
||||||
} else {
|
|
||||||
showModal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [query]);
|
|
||||||
|
|
||||||
const { modal, showModal } = useModal({
|
const { modal, showModal } = useModal({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
maxWidth: '350px',
|
maxWidth: '350px',
|
||||||
modal: (dismiss) => {
|
modal: function modal(dismiss) {
|
||||||
const onDismiss = (e) => {
|
const onDismiss = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
props.api.settings.putEntry('tutorial', 'seen', true);
|
props.api.settings.putEntry('tutorial', 'seen', true);
|
||||||
@ -111,31 +103,30 @@ export default function LaunchApp(props) {
|
|||||||
};
|
};
|
||||||
const onContinue = async (e) => {
|
const onContinue = async (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if(!hasTutorialGroup({ associations })) {
|
if (!hasTutorialGroup({ associations })) {
|
||||||
await props.api.groups.join(TUTORIAL_HOST, TUTORIAL_GROUP);
|
await props.api.groups.join(TUTORIAL_HOST, TUTORIAL_GROUP);
|
||||||
await props.api.settings.putEntry('tutorial', 'joined', Date.now());
|
await props.api.settings.putEntry('tutorial', 'joined', Date.now());
|
||||||
await waiter(hasTutorialGroup);
|
await waiter(hasTutorialGroup);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[TUTORIAL_BOOK, TUTORIAL_CHAT, TUTORIAL_LINKS].map(graph =>
|
[TUTORIAL_BOOK, TUTORIAL_CHAT, TUTORIAL_LINKS].map(graph => props.api.graph.joinGraph(TUTORIAL_HOST, graph)));
|
||||||
props.api.graph.joinGraph(TUTORIAL_HOST, graph)));
|
|
||||||
|
|
||||||
await waiter(p => {
|
await waiter((p) => {
|
||||||
return `/ship/${TUTORIAL_HOST}/${TUTORIAL_CHAT}` in p.associations.graph &&
|
return `/ship/${TUTORIAL_HOST}/${TUTORIAL_CHAT}` in p.associations.graph &&
|
||||||
`/ship/${TUTORIAL_HOST}/${TUTORIAL_BOOK}` in p.associations.graph &&
|
`/ship/${TUTORIAL_HOST}/${TUTORIAL_BOOK}` in p.associations.graph &&
|
||||||
`/ship/${TUTORIAL_HOST}/${TUTORIAL_LINKS}` in p.associations.graph;
|
`/ship/${TUTORIAL_HOST}/${TUTORIAL_LINKS}` in p.associations.graph;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
nextTutStep();
|
nextTutStep();
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
};
|
||||||
return exitingTut ? (
|
return exitingTut ? (
|
||||||
<Col maxWidth="350px" p="3">
|
<Col maxWidth="350px" p="3">
|
||||||
<Icon icon="Info" fill="black"></Icon>
|
<Icon icon="Info" fill="black"></Icon>
|
||||||
<Text my="3" lineHeight="tall">
|
<Text my="3" lineHeight="tall">
|
||||||
You can always restart the tutorial by typing "tutorial" in Leap
|
You can always restart the tutorial by typing “tutorial” in Leap
|
||||||
</Text>
|
</Text>
|
||||||
<Row gapX="2" justifyContent="flex-end">
|
<Row gapX="2" justifyContent="flex-end">
|
||||||
<Button primary onClick={onDismiss}>Ok</Button>
|
<Button primary onClick={onDismiss}>Ok</Button>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
) : (
|
) : (
|
||||||
@ -145,7 +136,7 @@ export default function LaunchApp(props) {
|
|||||||
</Box>
|
</Box>
|
||||||
<Text mb="3" lineHeight="tall" fontWeight="medium">Welcome</Text>
|
<Text mb="3" lineHeight="tall" fontWeight="medium">Welcome</Text>
|
||||||
<Text mb="3" lineHeight="tall">
|
<Text mb="3" lineHeight="tall">
|
||||||
You have been invited to use Landscape, an interface to chat
|
You have been invited to use Landscape, an interface to chat
|
||||||
and interact with communities
|
and interact with communities
|
||||||
<br />
|
<br />
|
||||||
Would you like a tour of Landscape?
|
Would you like a tour of Landscape?
|
||||||
@ -160,9 +151,22 @@ export default function LaunchApp(props) {
|
|||||||
</StatelessAsyncButton>
|
</StatelessAsyncButton>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(query.get('tutorial')) {
|
||||||
|
if (hasTutorialGroup({ associations })) {
|
||||||
|
if (nextTutStep) {
|
||||||
|
nextTutStep();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [query, showModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(hasLoaded && !seen && tutorialProgress === 'hidden') {
|
if(hasLoaded && !seen && tutorialProgress === 'hidden') {
|
||||||
showModal();
|
showModal();
|
||||||
@ -197,7 +201,8 @@ export default function LaunchApp(props) {
|
|||||||
width='100%'
|
width='100%'
|
||||||
bg='scales.black20'
|
bg='scales.black20'
|
||||||
border={1}
|
border={1}
|
||||||
borderColor="lightGray">
|
borderColor="lightGray"
|
||||||
|
>
|
||||||
<Row alignItems='center'>
|
<Row alignItems='center'>
|
||||||
<Icon
|
<Icon
|
||||||
color="black"
|
color="black"
|
||||||
@ -232,9 +237,11 @@ export default function LaunchApp(props) {
|
|||||||
(<Groups />)
|
(<Groups />)
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
<Box alignSelf="flex-start" display={["block", "none"]}>{hashBox}</Box>
|
<Box alignSelf="flex-start" display={['block', 'none']}>{hashBox}</Box>
|
||||||
</ScrollbarLessBox>
|
</ScrollbarLessBox>
|
||||||
<Box display={["none", "block"]}>{hashBox}</Box>
|
<Box display={['none', 'block']}>{hashBox}</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default LaunchApp;
|
@ -1,19 +1,17 @@
|
|||||||
import React, { useRef } from 'react';
|
import { Box, Col, Text } from '@tlon/indigo-react';
|
||||||
import { Box, Text, Col } from '@tlon/indigo-react';
|
import { Association, Associations, Unreads } from '@urbit/api';
|
||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import _ from 'lodash';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
import { Associations, Association, Unreads, UnreadStats } from '@urbit/api';
|
import { getNotificationCount, getUnreadCount } from '~/logic/lib/hark';
|
||||||
|
import { TUTORIAL_GROUP, TUTORIAL_GROUP_RESOURCE, TUTORIAL_HOST } from '~/logic/lib/tutorialModal';
|
||||||
import { alphabeticalOrder } from '~/logic/lib/util';
|
import { alphabeticalOrder } from '~/logic/lib/util';
|
||||||
import { getUnreadCount, getNotificationCount } from '~/logic/lib/hark';
|
|
||||||
import Tile from '../components/tiles/tile';
|
|
||||||
import { useTutorialModal } from '~/views/components/useTutorialModal';
|
|
||||||
import useGroupState from '~/logic/state/group';
|
import useGroupState from '~/logic/state/group';
|
||||||
import useHarkState from '~/logic/state/hark';
|
import useHarkState from '~/logic/state/hark';
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
import useMetadataState from '~/logic/state/metadata';
|
||||||
import { TUTORIAL_HOST, TUTORIAL_GROUP, TUTORIAL_GROUP_RESOURCE } from '~/logic/lib/tutorialModal';
|
|
||||||
import useSettingsState, { selectCalmState, SettingsState } from '~/logic/state/settings';
|
import useSettingsState, { selectCalmState, SettingsState } from '~/logic/state/settings';
|
||||||
|
import { useTutorialModal } from '~/views/components/useTutorialModal';
|
||||||
|
import Tile from '../components/tiles/tile';
|
||||||
|
|
||||||
interface GroupsProps {}
|
interface GroupsProps {}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
import { Button, Icon, Row, Text } from '@tlon/indigo-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Row, Button, Icon, Text } from '@tlon/indigo-react';
|
|
||||||
import { useModal } from '~/logic/lib/useModal';
|
import { useModal } from '~/logic/lib/useModal';
|
||||||
|
|
||||||
const ModalButton = (props) => {
|
const ModalButton = (props) => {
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import React from 'react';
|
import React, { ReactElement } from 'react';
|
||||||
|
import GlobalApi from '~/logic/api/global';
|
||||||
|
import useLaunchState from '~/logic/state/launch';
|
||||||
import BasicTile from './tiles/basic';
|
import BasicTile from './tiles/basic';
|
||||||
import CustomTile from './tiles/custom';
|
|
||||||
import ClockTile from './tiles/clock';
|
import ClockTile from './tiles/clock';
|
||||||
|
import CustomTile from './tiles/custom';
|
||||||
import WeatherTile from './tiles/weather';
|
import WeatherTile from './tiles/weather';
|
||||||
|
|
||||||
import useLaunchState from '~/logic/state/launch';
|
interface TileProps {
|
||||||
|
api: GlobalApi;
|
||||||
|
}
|
||||||
|
|
||||||
const Tiles = (props) => {
|
const Tiles = (props: TileProps): ReactElement => {
|
||||||
const weather = useLaunchState(state => state.weather);
|
const weather = useLaunchState(state => state.weather);
|
||||||
const tileOrdering = useLaunchState(state => state.tileOrdering);
|
const tileOrdering = useLaunchState(state => state.tileOrdering);
|
||||||
const tileState = useLaunchState(state => state.tiles);
|
const tileState = useLaunchState(state => state.tiles);
|
||||||
@ -44,11 +47,12 @@ const Tiles = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
return <CustomTile key={key} />;
|
return <CustomTile key={key} />;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>{tiles}</>
|
<>{tiles}</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Tiles;
|
export default Tiles;
|
@ -1,32 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Text, Icon } from '@tlon/indigo-react';
|
|
||||||
|
|
||||||
import Tile from './tile';
|
|
||||||
|
|
||||||
export default class BasicTile extends React.PureComponent {
|
|
||||||
render() {
|
|
||||||
const { props } = this;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tile
|
|
||||||
bg={props.title === 'Terminal' ? '#000000' : 'white'}
|
|
||||||
to={props.linkedUrl}
|
|
||||||
>
|
|
||||||
<Text color={props.title === 'Terminal' ? '#ffffff' : 'black'}>
|
|
||||||
{props.title === 'Terminal'
|
|
||||||
? <Icon
|
|
||||||
icon='ChevronEast'
|
|
||||||
color='#fff'
|
|
||||||
size='12px'
|
|
||||||
display='inline-block'
|
|
||||||
verticalAlign='top'
|
|
||||||
mt='5px'
|
|
||||||
mr='2'
|
|
||||||
/>
|
|
||||||
: null
|
|
||||||
}{props.title}
|
|
||||||
</Text>
|
|
||||||
</Tile>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
import { Icon, Text } from '@tlon/indigo-react';
|
||||||
|
import React, { ReactElement } from 'react';
|
||||||
|
import Tile from './tile';
|
||||||
|
|
||||||
|
export interface BasicTileProps {
|
||||||
|
title: string;
|
||||||
|
linkedUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BasicTile = (props: BasicTileProps): ReactElement => (
|
||||||
|
<Tile
|
||||||
|
bg={props.title === 'Terminal' ? '#000000' : 'white'}
|
||||||
|
to={props.linkedUrl}
|
||||||
|
>
|
||||||
|
<Text color={props.title === 'Terminal' ? '#ffffff' : 'black'}>
|
||||||
|
{props.title === 'Terminal'
|
||||||
|
? <Icon
|
||||||
|
icon='ChevronEast'
|
||||||
|
color='#fff'
|
||||||
|
size='12px'
|
||||||
|
display='inline-block'
|
||||||
|
verticalAlign='top'
|
||||||
|
mt='5px'
|
||||||
|
mr='2'
|
||||||
|
/>
|
||||||
|
: null
|
||||||
|
}{props.title}
|
||||||
|
</Text>
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default BasicTile;
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
/* eslint-disable max-lines-per-function */
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import SunCalc from 'suncalc';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import SunCalc from 'suncalc';
|
||||||
import Tile from './tile';
|
import Tile from './tile';
|
||||||
|
|
||||||
const VIEWBOX_SIZE = 100;
|
const VIEWBOX_SIZE = 100;
|
||||||
@ -32,8 +32,6 @@ const minsToDegs = (mins) => {
|
|||||||
return (mins / 1440) * 360;
|
return (mins / 1440) * 360;
|
||||||
};
|
};
|
||||||
|
|
||||||
const radToDeg = rad => rad * (180 / Math.PI);
|
|
||||||
|
|
||||||
const degToRad = deg => deg * (Math.PI / 180);
|
const degToRad = deg => deg * (Math.PI / 180);
|
||||||
|
|
||||||
const convert = (date, referenceTime) => {
|
const convert = (date, referenceTime) => {
|
||||||
@ -42,25 +40,25 @@ const convert = (date, referenceTime) => {
|
|||||||
|
|
||||||
// https://github.com/tingletech/moon-phase
|
// https://github.com/tingletech/moon-phase
|
||||||
export const dFromPhase = (moonPhase) => {
|
export const dFromPhase = (moonPhase) => {
|
||||||
let mag, sweep, d = "m50,0";
|
let mag, sweep, d = 'm50,0';
|
||||||
if (moonPhase <= 0.25) {
|
if (moonPhase <= 0.25) {
|
||||||
sweep = [ 1, 0 ];
|
sweep = [1, 0];
|
||||||
mag = 20 - 20 * moonPhase * 4;
|
mag = 20 - 20 * moonPhase * 4;
|
||||||
} else if (moonPhase <= 0.50) {
|
} else if (moonPhase <= 0.50) {
|
||||||
sweep = [ 0, 0 ];
|
sweep = [0, 0];
|
||||||
mag = 20 * (moonPhase - 0.25) * 4;
|
mag = 20 * (moonPhase - 0.25) * 4;
|
||||||
} else if (moonPhase <= 0.75) {
|
} else if (moonPhase <= 0.75) {
|
||||||
sweep = [ 1, 1 ];
|
sweep = [1, 1];
|
||||||
mag = 20 - 20 * (moonPhase - 0.50) * 4;
|
mag = 20 - 20 * (moonPhase - 0.50) * 4;
|
||||||
} else if (moonPhase <= 1) {
|
} else if (moonPhase <= 1) {
|
||||||
sweep = [ 0, 1 ];
|
sweep = [0, 1];
|
||||||
mag = 20 * (moonPhase - 0.75) * 4;
|
mag = 20 * (moonPhase - 0.75) * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
d = d + "a" + mag + ",20 0 1," + sweep[0] + " 0,100 ";
|
d = d + 'a' + mag + ',20 0 1,' + sweep[0] + ' 0,100 ';
|
||||||
d = d + "a20,20 0 1," + sweep[1] + " 0,-100";
|
d = d + 'a20,20 0 1,' + sweep[1] + ' 0,-100';
|
||||||
return d;
|
return d;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Moon = ({ angle, ...props }) => {
|
const Moon = ({ angle, ...props }) => {
|
||||||
const phase = SunCalc.getMoonIllumination(moment().toDate()).phase.toFixed(2);
|
const phase = SunCalc.getMoonIllumination(moment().toDate()).phase.toFixed(2);
|
||||||
@ -70,7 +68,7 @@ const Moon = ({ angle, ...props }) => {
|
|||||||
<g>
|
<g>
|
||||||
<mask id="umbra">
|
<mask id="umbra">
|
||||||
<rect x="-50" y="-50" height="200" width="200" fill="black" />
|
<rect x="-50" y="-50" height="200" width="200" fill="black" />
|
||||||
<path d={dFromPhase(phase)} fill="white"/>
|
<path d={dFromPhase(phase)} fill="white" />
|
||||||
</mask>
|
</mask>
|
||||||
<circle
|
<circle
|
||||||
id="moonbg"
|
id="moonbg"
|
||||||
@ -83,7 +81,7 @@ const Moon = ({ angle, ...props }) => {
|
|||||||
<use
|
<use
|
||||||
width={CELESTIAL_BODY_SIZE}
|
width={CELESTIAL_BODY_SIZE}
|
||||||
height={CELESTIAL_BODY_SIZE}
|
height={CELESTIAL_BODY_SIZE}
|
||||||
xlinkHref="#Moon-symbol"
|
xlinkHref="#Moon-symbol"
|
||||||
id="moon"
|
id="moon"
|
||||||
x={cx}
|
x={cx}
|
||||||
y={cy}
|
y={cy}
|
||||||
@ -91,9 +89,9 @@ const Moon = ({ angle, ...props }) => {
|
|||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const Sun = ({ angle, ...props}) => (
|
const Sun = ({ angle, ...props }) => (
|
||||||
<circle
|
<circle
|
||||||
id="sun"
|
id="sun"
|
||||||
cx={CX + (RADIUS - 12) * Math.cos(degToRad(angle))}
|
cx={CX + (RADIUS - 12) * Math.cos(degToRad(angle))}
|
||||||
@ -111,7 +109,7 @@ const SvgArc = ({ start, end, ...rest }) => {
|
|||||||
const x2 = CX + RADIUS * Math.cos(degToRad(end));
|
const x2 = CX + RADIUS * Math.cos(degToRad(end));
|
||||||
const y2 = CY + RADIUS * Math.sin(degToRad(end));
|
const y2 = CY + RADIUS * Math.sin(degToRad(end));
|
||||||
|
|
||||||
const isLarge = Math.abs((start > 360 ? start - 360 : start) - end) > 180;
|
// const isLarge = Math.abs((start > 360 ? start - 360 : start) - end) > 180;
|
||||||
|
|
||||||
const d = [
|
const d = [
|
||||||
'M', CX, CY,
|
'M', CX, CY,
|
||||||
@ -120,21 +118,25 @@ const SvgArc = ({ start, end, ...rest }) => {
|
|||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
return <path d={d} {...rest} />;
|
return <path d={d} {...rest} />;
|
||||||
}
|
};
|
||||||
|
|
||||||
class ClockText extends React.Component {
|
class ClockText extends React.Component<ClockTextProps, ClockTextState> {
|
||||||
|
interval?: NodeJS.Timeout;
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
time: Date.now()
|
time: Date.now()
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
|
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearInterval(this.interval);
|
if (this.interval) {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -156,7 +158,8 @@ class ClockText extends React.Component {
|
|||||||
begin="0s"
|
begin="0s"
|
||||||
dur="1s"
|
dur="1s"
|
||||||
calcMode="discrete"
|
calcMode="discrete"
|
||||||
repeatCount="indefinite"/>
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
</tspan>
|
</tspan>
|
||||||
<tspan>{now.format('mm A')}</tspan>
|
<tspan>{now.format('mm A')}</tspan>
|
||||||
</text>
|
</text>
|
||||||
@ -167,7 +170,7 @@ class ClockText extends React.Component {
|
|||||||
fontSize="10"
|
fontSize="10"
|
||||||
fontFamily="Inter"
|
fontFamily="Inter"
|
||||||
className="date"
|
className="date"
|
||||||
>{now.format('MMM D')}<tspan style={{ fontFeatureSettings: "'sups' 1" }}>{now.format('Do').replace(now.format('D'), '')}</tspan></text>
|
>{now.format('MMM D')}<tspan style={{ fontFeatureSettings: '\'sups\' 1' }}>{now.format('Do').replace(now.format('D'), '')}</tspan></text>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -203,7 +206,6 @@ class Clock extends React.PureComponent {
|
|||||||
|
|
||||||
initGeolocation() {
|
initGeolocation() {
|
||||||
if (typeof this.props.data === 'object') {
|
if (typeof this.props.data === 'object') {
|
||||||
|
|
||||||
const { latitude: lat, longitude: lon } = this.props.data;
|
const { latitude: lat, longitude: lon } = this.props.data;
|
||||||
|
|
||||||
const suncalc = SunCalc.getTimes(new Date(), lat, lon);
|
const suncalc = SunCalc.getTimes(new Date(), lat, lon);
|
||||||
@ -252,7 +254,7 @@ class Clock extends React.PureComponent {
|
|||||||
return (
|
return (
|
||||||
<ApplyClockBg>
|
<ApplyClockBg>
|
||||||
<svg
|
<svg
|
||||||
style={{ height: '100%', width: '100%'}}
|
style={{ height: '100%', width: '100%' }}
|
||||||
viewBox={`0 0 ${VIEWBOX_SIZE} ${VIEWBOX_SIZE}`}
|
viewBox={`0 0 ${VIEWBOX_SIZE} ${VIEWBOX_SIZE}`}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||||
@ -266,14 +268,14 @@ class Clock extends React.PureComponent {
|
|||||||
</symbol>
|
</symbol>
|
||||||
<mask id="center-mask">
|
<mask id="center-mask">
|
||||||
<use xlinkHref="#border" fill="white" />
|
<use xlinkHref="#border" fill="white" />
|
||||||
<use xlinkHref="#clock-center" fill="black"/>
|
<use xlinkHref="#clock-center" fill="black" />
|
||||||
</mask>
|
</mask>
|
||||||
<symbol id="Moon-symbol" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
<symbol id="Moon-symbol" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||||
<g>
|
<g>
|
||||||
<path mask="url(#umbra)" d="m50,0 a20,20 0 1,1 0,100 a20,20 0 1,1 0,-100" fill="#fff" stroke="#000"/>
|
<path mask="url(#umbra)" d="m50,0 a20,20 0 1,1 0,100 a20,20 0 1,1 0,-100" fill="#fff" stroke="#000" />
|
||||||
</g>
|
</g>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
</defs>
|
</defs>
|
||||||
<g mask="url(#center-mask)">
|
<g mask="url(#center-mask)">
|
||||||
<use xlinkHref="#border" className="background" />
|
<use xlinkHref="#border" className="background" />
|
||||||
@ -313,12 +315,10 @@ class Clock extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ClockTile = ({ location = {} }) => (
|
const ClockTile = ({ location = {} }) => (
|
||||||
<Tile p={0} border={0} bg='transparent' boxShadow='none'>
|
<Tile p={0} border={0} bg='transparent' boxShadow='none'>
|
||||||
<Clock data={location} />
|
<Clock data={location} />
|
||||||
</Tile>
|
</Tile>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ClockTile;
|
export default ClockTile;
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import { BaseImage, Box } from '@tlon/indigo-react';
|
||||||
import { Box, BaseImage } from '@tlon/indigo-react';
|
import React from 'react';
|
||||||
import Tile from './tile';
|
import Tile from './tile';
|
||||||
|
|
||||||
export default class CustomTile extends React.PureComponent {
|
export default class CustomTile extends React.PureComponent {
|
@ -1,11 +1,9 @@
|
|||||||
|
import { Box } from '@tlon/indigo-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import defaultApps from '~/logic/lib/default-apps';
|
import defaultApps from '~/logic/lib/default-apps';
|
||||||
|
|
||||||
import { Box } from "@tlon/indigo-react";
|
|
||||||
|
|
||||||
const SquareBox = styled(Box)`
|
const SquareBox = styled(Box)`
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
@ -37,17 +35,15 @@ const Tile = React.forwardRef((props, ref) => {
|
|||||||
} else {
|
} else {
|
||||||
childElement= (<a href={to}>{childElement}</a>);
|
childElement= (<a href={to}>{childElement}</a>);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SquareBox
|
<SquareBox
|
||||||
ref={ref}
|
ref={ref}
|
||||||
position="relative"
|
position="relative"
|
||||||
borderRadius={2}
|
borderRadius={2}
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
bg={bg || "white"}
|
bg={bg || 'white'}
|
||||||
color={props?.color || 'lightGray'}
|
color={props?.color || 'lightGray'}
|
||||||
boxShadow={boxShadow || '0 0 0px 1px inset'}
|
boxShadow={boxShadow || '0 0 0px 1px inset'}
|
||||||
style={{ gridColumnStart }}
|
style={{ gridColumnStart }}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user