interface: addressed store feedback; cleaning up

This commit is contained in:
Tyler Brown Cifu Shuster 2021-03-02 21:45:50 -08:00
parent 6d8fe7d41f
commit 04e481f8e9
15 changed files with 126 additions and 257 deletions

View File

@ -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 } });
});
}

View File

@ -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) {

View File

@ -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
};

View 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;
})
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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
});

View 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;

View File

@ -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 };

View File

@ -5,10 +5,6 @@ export default class BaseStore<S extends object> {
this.state = this.initialState();
}
dehydrate() {}
rehydrate() {}
initialState() {
return {} as S;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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>
);