mirror of
https://github.com/urbit/shrub.git
synced 2024-12-23 19:05:48 +03:00
interface: addressed store feedback; cleaning up
This commit is contained in:
parent
6d8fe7d41f
commit
04e481f8e9
@ -4,7 +4,7 @@ import { StoreState } from '../store/type';
|
||||
export default class LocalApi extends BaseApi<StoreState> {
|
||||
getBaseHash() {
|
||||
this.scry<string>('file-server', '/clay/base/hash').then((baseHash) => {
|
||||
this.store.handleEvent({ data: { local: { baseHash } } });
|
||||
this.store.handleEvent({ data: { baseHash } });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,6 @@ export function useMigrateSettings(api: GlobalApi) {
|
||||
const { display, remoteContentPolicy, calm } = useSettingsState();
|
||||
|
||||
return async () => {
|
||||
if (!localStorage?.has("localReducer")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let promises: Promise<any>[] = [];
|
||||
|
||||
if (local.hideAvatars !== calm.hideAvatars) {
|
||||
|
@ -1,13 +1,16 @@
|
||||
import { resourceAsPath } from '~/logic/lib/util';
|
||||
import { GroupUpdate } from '@urbit/api/groups';
|
||||
import { reduceState, resourceAsPath } from '~/logic/lib/util';
|
||||
import useGroupState, { GroupState } from '../state/group';
|
||||
|
||||
const initial = (json: any, state: any) => {
|
||||
const initial = (json: any, state: GroupState): GroupState => {
|
||||
const data = json.initial;
|
||||
if(data) {
|
||||
state.pendingJoin = data;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
const progress = (json: any, state: any) => {
|
||||
const progress = (json: any, state: GroupState): GroupState => {
|
||||
const data = json.progress;
|
||||
if(data) {
|
||||
const { progress, resource } = data;
|
||||
@ -18,13 +21,15 @@ const progress = (json: any, state: any) => {
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export const GroupViewReducer = (json: any, state: any) => {
|
||||
export const GroupViewReducer = (json: any) => {
|
||||
const data = json['group-view-update'];
|
||||
if(data) {
|
||||
progress(data, state);
|
||||
initial(data, state);
|
||||
if (data) {
|
||||
reduceState<GroupState, GroupUpdate>(useGroupState, data, [
|
||||
progress,
|
||||
initial
|
||||
]);
|
||||
}
|
||||
};
|
||||
// TODO zustand this file
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import { LaunchState, LaunchUpdate, WeatherState } from '~/types/launch-update';
|
||||
import { LaunchUpdate, WeatherState } from '~/types/launch-update';
|
||||
import { Cage } from '~/types/cage';
|
||||
import useLaunchState from '../state/launch';
|
||||
import useLaunchState, { LaunchState } from '../state/launch';
|
||||
import { compose } from 'lodash/fp';
|
||||
import { reduceState } from '../lib/util';
|
||||
|
||||
@ -31,6 +31,13 @@ export default class LaunchReducer {
|
||||
state.userLocation = locationData;
|
||||
});
|
||||
}
|
||||
|
||||
const baseHash = _.get(json, 'baseHash', false);
|
||||
if (baseHash) {
|
||||
useLaunchState.getState().set(state => {
|
||||
state.baseHash = baseHash;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import { StoreState } from '~/store/type';
|
||||
import { Cage } from '~/types/cage';
|
||||
import { LocalUpdate } from '~/types/local-update';
|
||||
|
||||
type LocalState = Pick<StoreState, 'baseHash'>;
|
||||
|
||||
export default class LocalReducer<S extends LocalState> {
|
||||
rehydrate(state: S) {
|
||||
try {
|
||||
const json = JSON.parse(localStorage.getItem('localReducer') || '{}');
|
||||
_.forIn(json, (value, key) => {
|
||||
state[key] = value;
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to rehydrate localStorage state', e);
|
||||
}
|
||||
}
|
||||
|
||||
dehydrate(state: S) {
|
||||
}
|
||||
reduce(json: Cage, state: S) {
|
||||
const data = json['local'];
|
||||
if (data) {
|
||||
this.baseHash(data, state);
|
||||
}
|
||||
}
|
||||
baseHash(obj: LocalUpdate, state: S) {
|
||||
if ('baseHash' in obj) {
|
||||
state.baseHash = obj.baseHash;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +1,46 @@
|
||||
import _ from 'lodash';
|
||||
import { SettingsUpdate } from '~/types/settings';
|
||||
import useSettingsState, { SettingsStateZus } from "~/logic/state/settings";
|
||||
import produce from 'immer';
|
||||
import useSettingsState, { SettingsState } from "~/logic/state/settings";
|
||||
import { SettingsUpdate } from '@urbit/api/dist/settings';
|
||||
import { reduceState } from '../lib/util';
|
||||
|
||||
export default class SettingsStateZusettingsReducer{
|
||||
export default class SettingsReducer {
|
||||
reduce(json: any) {
|
||||
const old = useSettingsState.getState();
|
||||
const newState = produce(old, state => {
|
||||
let data = json["settings-event"];
|
||||
if (data) {
|
||||
console.log(data);
|
||||
this.putBucket(data, state);
|
||||
this.delBucket(data, state);
|
||||
this.putEntry(data, state);
|
||||
this.delEntry(data, state);
|
||||
}
|
||||
data = json["settings-data"];
|
||||
if (data) {
|
||||
console.log(data);
|
||||
this.getAll(data, state);
|
||||
this.getBucket(data, state);
|
||||
this.getEntry(data, state);
|
||||
}
|
||||
});
|
||||
useSettingsState.setState(newState);
|
||||
let data = json["settings-event"];
|
||||
if (data) {
|
||||
reduceState<SettingsState, SettingsUpdate>(useSettingsState, data, [
|
||||
this.putBucket,
|
||||
this.delBucket,
|
||||
this.putEntry,
|
||||
this.delEntry,
|
||||
]);
|
||||
}
|
||||
data = json["settings-data"];
|
||||
if (data) {
|
||||
reduceState<SettingsState, SettingsUpdate>(useSettingsState, data, [
|
||||
this.getAll,
|
||||
this.getBucket,
|
||||
this.getEntry,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
putBucket(json: SettingsUpdate, state: SettingsStateZus) {
|
||||
putBucket(json: SettingsUpdate, state: SettingsState): SettingsState {
|
||||
const data = _.get(json, 'put-bucket', false);
|
||||
if (data) {
|
||||
state[data["bucket-key"]] = data.bucket;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
delBucket(json: SettingsUpdate, state: SettingsStateZus) {
|
||||
delBucket(json: SettingsUpdate, state: SettingsState): SettingsState {
|
||||
const data = _.get(json, 'del-bucket', false);
|
||||
if (data) {
|
||||
delete settings[data['bucket-key']];
|
||||
delete state[data['bucket-key']];
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
putEntry(json: SettingsUpdate, state: SettingsStateZus) {
|
||||
putEntry(json: SettingsUpdate, state: SettingsState): SettingsState {
|
||||
const data = _.get(json, 'put-entry', false);
|
||||
if (data) {
|
||||
if (!state[data["bucket-key"]]) {
|
||||
@ -48,36 +48,41 @@ export default class SettingsStateZusettingsReducer{
|
||||
}
|
||||
state[data["bucket-key"]][data["entry-key"]] = data.value;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
delEntry(json: SettingsUpdate, state: SettingsStateZus) {
|
||||
delEntry(json: SettingsUpdate, state: SettingsState): SettingsState {
|
||||
const data = _.get(json, 'del-entry', false);
|
||||
if (data) {
|
||||
delete state[data["bucket-key"]][data["entry-key"]];
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
getAll(json: any, state: SettingsStateZus) {
|
||||
getAll(json: any, state: SettingsState): SettingsState {
|
||||
const data = _.get(json, 'all');
|
||||
if(data) {
|
||||
_.merge(state, data);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
getBucket(json: any, state: SettingsStateZus) {
|
||||
getBucket(json: any, state: SettingsState): SettingsState {
|
||||
const key = _.get(json, 'bucket-key', false);
|
||||
const bucket = _.get(json, 'bucket', false);
|
||||
if (key && bucket) {
|
||||
state[key] = bucket;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
getEntry(json: any, state: SettingsStateZus) {
|
||||
getEntry(json: any, state: SettingsState) {
|
||||
const bucketKey = _.get(json, 'bucket-key', false);
|
||||
const entryKey = _.get(json, 'entry-key', false);
|
||||
const entry = _.get(json, 'entry', false);
|
||||
if (bucketKey && entryKey && entry) {
|
||||
state[bucketKey][entryKey] = entry;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Invites } from '@urbit/api';
|
||||
|
||||
import { BaseState, createState } from "./base";
|
||||
import { BaseState, createState } from '~/logic/state/base';
|
||||
|
||||
export interface InviteState extends BaseState<InviteState> {
|
||||
invites: Invites;
|
||||
|
@ -11,6 +11,7 @@ export interface LaunchState extends BaseState<LaunchState> {
|
||||
},
|
||||
weather: WeatherState | null,
|
||||
userLocation: string | null;
|
||||
baseHash: string | null;
|
||||
};
|
||||
|
||||
const useLaunchState = createState<LaunchState>('Launch', {
|
||||
@ -19,6 +20,7 @@ const useLaunchState = createState<LaunchState>('Launch', {
|
||||
tiles: {},
|
||||
weather: null,
|
||||
userLocation: null,
|
||||
baseHash: null
|
||||
});
|
||||
|
||||
|
||||
|
54
pkg/interface/src/logic/state/settings.ts
Normal file
54
pkg/interface/src/logic/state/settings.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import f from 'lodash/fp';
|
||||
import { RemoteContentPolicy, LeapCategories, leapCategories } from "~/types/local-update";
|
||||
import { BaseState, createState } from '~/logic/state/base';
|
||||
|
||||
|
||||
export interface SettingsState extends BaseState<SettingsState> {
|
||||
display: {
|
||||
backgroundType: 'none' | 'url' | 'color';
|
||||
background?: string;
|
||||
dark: boolean;
|
||||
};
|
||||
calm: {
|
||||
hideNicknames: boolean;
|
||||
hideAvatars: boolean;
|
||||
hideUnreads: boolean;
|
||||
hideGroups: boolean;
|
||||
hideUtilities: boolean;
|
||||
};
|
||||
remoteContentPolicy: RemoteContentPolicy;
|
||||
leap: {
|
||||
categories: LeapCategories[];
|
||||
}
|
||||
};
|
||||
|
||||
export const selectSettingsState =
|
||||
<K extends keyof SettingsState>(keys: K[]) => f.pick<SettingsState, K>(keys);
|
||||
|
||||
export const selectCalmState = (s: SettingsState) => s.calm;
|
||||
|
||||
const useSettingsState = createState<SettingsState>('Settings', {
|
||||
display: {
|
||||
backgroundType: 'none',
|
||||
background: undefined,
|
||||
dark: false,
|
||||
},
|
||||
calm: {
|
||||
hideNicknames: false,
|
||||
hideAvatars: false,
|
||||
hideUnreads: false,
|
||||
hideGroups: false,
|
||||
hideUtilities: false
|
||||
},
|
||||
remoteContentPolicy: {
|
||||
imageShown: true,
|
||||
oembedShown: true,
|
||||
audioShown: true,
|
||||
videoShown: true
|
||||
},
|
||||
leap: {
|
||||
categories: leapCategories,
|
||||
},
|
||||
});
|
||||
|
||||
export default useSettingsState;
|
@ -1,70 +0,0 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import f from 'lodash/fp';
|
||||
import create, { State } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import produce from 'immer';
|
||||
import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgress, LeapCategories, leapCategories } from "~/types/local-update";
|
||||
|
||||
|
||||
export interface SettingsState {
|
||||
display: {
|
||||
backgroundType: 'none' | 'url' | 'color';
|
||||
background?: string;
|
||||
dark: boolean;
|
||||
};
|
||||
calm: {
|
||||
hideNicknames: boolean;
|
||||
hideAvatars: boolean;
|
||||
hideUnreads: boolean;
|
||||
hideGroups: boolean;
|
||||
hideUtilities: boolean;
|
||||
};
|
||||
remoteContentPolicy: RemoteContentPolicy;
|
||||
leap: {
|
||||
categories: LeapCategories[];
|
||||
}
|
||||
set: (fn: (state: SettingsState) => void) => void
|
||||
};
|
||||
|
||||
export type SettingsStateZus = SettingsState & State;
|
||||
|
||||
export const selectSettingsState =
|
||||
<K extends keyof SettingsState>(keys: K[]) => f.pick<SettingsState, K>(keys);
|
||||
|
||||
export const selectCalmState = (s: SettingsState) => s.calm;
|
||||
|
||||
const useSettingsState = create<SettingsStateZus>((set) => ({
|
||||
display: {
|
||||
backgroundType: 'none',
|
||||
background: undefined,
|
||||
dark: false,
|
||||
},
|
||||
calm: {
|
||||
hideNicknames: false,
|
||||
hideAvatars: false,
|
||||
hideUnreads: false,
|
||||
hideGroups: false,
|
||||
hideUtilities: false
|
||||
},
|
||||
remoteContentPolicy: {
|
||||
imageShown: true,
|
||||
oembedShown: true,
|
||||
audioShown: true,
|
||||
videoShown: true
|
||||
},
|
||||
leap: {
|
||||
categories: leapCategories,
|
||||
},
|
||||
set: (fn: (state: SettingsState) => void) => set(produce(fn))
|
||||
}));
|
||||
|
||||
function withSettingsState<P, S extends keyof SettingsState>(Component: any, stateMemberKeys?: S[]) {
|
||||
return React.forwardRef((props: Omit<P, S>, ref) => {
|
||||
const localState = stateMemberKeys
|
||||
? useSettingsState(selectSettingsState(stateMemberKeys))
|
||||
: useSettingsState();
|
||||
return <Component ref={ref} {...localState} {...props} />
|
||||
});
|
||||
}
|
||||
|
||||
export { useSettingsState as default, withSettingsState };
|
@ -5,10 +5,6 @@ export default class BaseStore<S extends object> {
|
||||
this.state = this.initialState();
|
||||
}
|
||||
|
||||
dehydrate() {}
|
||||
|
||||
rehydrate() {}
|
||||
|
||||
initialState() {
|
||||
return {} as S;
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import { GroupViewReducer } from '../reducers/group-view';
|
||||
export default class GlobalStore extends BaseStore<StoreState> {
|
||||
inviteReducer = new InviteReducer();
|
||||
metadataReducer = new MetadataReducer();
|
||||
localReducer = new LocalReducer();
|
||||
s3Reducer = new S3Reducer();
|
||||
groupReducer = new GroupReducer();
|
||||
launchReducer = new LaunchReducer();
|
||||
@ -42,56 +41,9 @@ export default class GlobalStore extends BaseStore<StoreState> {
|
||||
console.log(_.pick(this.state, stateKeys));
|
||||
}
|
||||
|
||||
rehydrate() {
|
||||
this.localReducer.rehydrate(this.state);
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
this.localReducer.dehydrate(this.state);
|
||||
}
|
||||
|
||||
initialState(): StoreState {
|
||||
return {
|
||||
connection: 'connected',
|
||||
baseHash: null,
|
||||
invites: {},
|
||||
associations: {
|
||||
groups: {},
|
||||
graph: {}
|
||||
},
|
||||
groups: {},
|
||||
groupKeys: new Set(),
|
||||
graphs: {},
|
||||
graphKeys: new Set(),
|
||||
launch: {
|
||||
firstTime: false,
|
||||
tileOrdering: [],
|
||||
tiles: {}
|
||||
},
|
||||
weather: {},
|
||||
userLocation: null,
|
||||
s3: {
|
||||
configuration: {
|
||||
buckets: new Set(),
|
||||
currentBucket: ''
|
||||
},
|
||||
credentials: null
|
||||
},
|
||||
notifications: new BigIntOrderedMap<Timebox>(),
|
||||
archivedNotifications: new BigIntOrderedMap<Timebox>(),
|
||||
notificationsGroupConfig: [],
|
||||
notificationsGraphConfig: {
|
||||
watchOnSelf: false,
|
||||
mentions: false,
|
||||
watching: []
|
||||
},
|
||||
unreads: {
|
||||
graph: {},
|
||||
group: {}
|
||||
},
|
||||
notificationsCount: 0,
|
||||
settings: {},
|
||||
pendingJoin: {}
|
||||
};
|
||||
}
|
||||
|
||||
@ -100,10 +52,8 @@ export default class GlobalStore extends BaseStore<StoreState> {
|
||||
const tag = Object.keys(data)[0];
|
||||
const oldActions = this.pastActions[tag] || [];
|
||||
this.pastActions[tag] = [data[tag], ...oldActions.slice(0,14)];
|
||||
|
||||
this.inviteReducer.reduce(data);
|
||||
this.metadataReducer.reduce(data);
|
||||
this.localReducer.reduce(data, this.state);
|
||||
this.s3Reducer.reduce(data);
|
||||
this.groupReducer.reduce(data);
|
||||
this.launchReducer.reduce(data);
|
||||
@ -112,6 +62,6 @@ export default class GlobalStore extends BaseStore<StoreState> {
|
||||
HarkReducer(data);
|
||||
ContactReducer(data);
|
||||
this.settingsReducer.reduce(data);
|
||||
GroupViewReducer(data, this.state);
|
||||
GroupViewReducer(data);
|
||||
}
|
||||
}
|
||||
|
@ -1,49 +1,6 @@
|
||||
import { Path } from '@urbit/api';
|
||||
import { Invites } from '@urbit/api/invite';
|
||||
import { Associations } from '@urbit/api/metadata';
|
||||
import { Rolodex } from '@urbit/api/contacts';
|
||||
import { Groups } from '@urbit/api/groups';
|
||||
import { S3State } from '~/types/s3-update';
|
||||
import { LaunchState, WeatherState } from '~/types/launch-update';
|
||||
import { ConnectionStatus } from '~/types/connection';
|
||||
import { Graphs } from '@urbit/api/graph';
|
||||
import {
|
||||
Notifications,
|
||||
NotificationGraphConfig,
|
||||
GroupNotificationsConfig,
|
||||
Unreads,
|
||||
JoinRequests,
|
||||
Patp
|
||||
} from '@urbit/api';
|
||||
|
||||
export interface StoreState {
|
||||
// local state
|
||||
connection: ConnectionStatus;
|
||||
baseHash: string | null;
|
||||
|
||||
// invite state
|
||||
invites: Invites;
|
||||
// metadata state
|
||||
associations: Associations;
|
||||
// groups state
|
||||
groups: Groups;
|
||||
groupKeys: Set<Path>;
|
||||
s3: S3State;
|
||||
graphs: Graphs;
|
||||
graphKeys: Set<string>;
|
||||
|
||||
// App specific states
|
||||
// launch state
|
||||
launch: LaunchState;
|
||||
weather: WeatherState | {} | null;
|
||||
userLocation: string | null;
|
||||
|
||||
archivedNotifications: Notifications;
|
||||
notifications: Notifications;
|
||||
notificationsGraphConfig: NotificationGraphConfig;
|
||||
notificationsGroupConfig: GroupNotificationsConfig;
|
||||
notificationsCount: number,
|
||||
unreads: Unreads;
|
||||
doNotDisturb: boolean;
|
||||
pendingJoin: JoinRequests;
|
||||
}
|
||||
|
@ -31,10 +31,10 @@ import withState from '~/logic/lib/withState';
|
||||
import useLocalState from '~/logic/state/local';
|
||||
import useContactState from '~/logic/state/contact';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import { withSettingsState } from '~/logic/state/settings';
|
||||
import useSettingsState from '~/logic/state/settings';
|
||||
|
||||
|
||||
const Root = withSettingsState(styled.div`
|
||||
const Root = withState(useSettingsState, styled.div`
|
||||
font-family: ${p => p.theme.fonts.sans};
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@ -100,7 +100,6 @@ class App extends React.Component {
|
||||
}, 500);
|
||||
this.api.local.getBaseHash();
|
||||
this.api.settings.getAll();
|
||||
this.store.rehydrate();
|
||||
Mousetrap.bindGlobal(['command+/', 'ctrl+/'], (e) => {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import f from 'lodash/fp';
|
||||
import _ from 'lodash';
|
||||
|
||||
@ -30,6 +29,7 @@ import {
|
||||
TUTORIAL_CHAT,
|
||||
TUTORIAL_LINKS
|
||||
} from '~/logic/lib/tutorialModal';
|
||||
import useLaunchState from '~/logic/state/launch';
|
||||
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
||||
|
||||
|
||||
@ -44,7 +44,8 @@ const ScrollbarLessBox = styled(Box)`
|
||||
const tutSelector = f.pick(['tutorialProgress', 'nextTutStep', 'hideGroups']);
|
||||
|
||||
export default function LaunchApp(props) {
|
||||
const [hashText, setHashText] = useState(props.baseHash);
|
||||
const baseHash = useLaunchState(state => state.baseHash);
|
||||
const [hashText, setHashText] = useState(baseHash);
|
||||
const hashBox = (
|
||||
<Box
|
||||
position={["relative", "absolute"]}
|
||||
@ -58,15 +59,15 @@ export default function LaunchApp(props) {
|
||||
fontSize={0}
|
||||
cursor="pointer"
|
||||
onClick={() => {
|
||||
writeText(props.baseHash);
|
||||
writeText(baseHash);
|
||||
setHashText('copied');
|
||||
setTimeout(() => {
|
||||
setHashText(props.baseHash);
|
||||
setHashText(baseHash);
|
||||
}, 2000);
|
||||
}}
|
||||
>
|
||||
<Box backgroundColor="washedGray" p={2}>
|
||||
<Text mono bold>{hashText || props.baseHash}</Text>
|
||||
<Text mono bold>{hashText || baseHash}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user