interface: convert ms3 store to zustand

This commit is contained in:
Tyler Brown Cifu Shuster 2021-02-26 09:23:07 -08:00
parent 041be1d8fe
commit c4d6fde8ea
29 changed files with 132 additions and 130 deletions

View File

@ -1,83 +1,95 @@
import _ from 'lodash';
import { StoreState } from '../../store/type';
import { compose } from 'lodash/fp';
import { Cage } from '~/types/cage';
import { S3Update } from '~/types/s3-update';
import useS3State, { S3State } from '../state/s3';
type S3State = Pick<StoreState, 's3'>;
export default class S3Reducer<S extends S3State> {
reduce(json: Cage, state: S) {
export default class S3Reducer {
reduce(json: Cage) {
const data = _.get(json, 's3-update', false);
if (data) {
this.credentials(data, state);
this.configuration(data, state);
this.currentBucket(data, state);
this.addBucket(data, state);
this.removeBucket(data, state);
this.endpoint(data, state);
this.accessKeyId(data, state);
this.secretAccessKey(data, state);
}
}
credentials(json: S3Update, state: S) {
const data = _.get(json, 'credentials', false);
if (data) {
state.s3.credentials = data;
}
}
configuration(json: S3Update, state: S) {
const data = _.get(json, 'configuration', false);
if (data) {
state.s3.configuration = {
buckets: new Set(data.buckets),
currentBucket: data.currentBucket
};
}
}
currentBucket(json: S3Update, state: S) {
const data = _.get(json, 'setCurrentBucket', false);
if (data && state.s3) {
state.s3.configuration.currentBucket = data;
}
}
addBucket(json: S3Update, state: S) {
const data = _.get(json, 'addBucket', false);
if (data) {
state.s3.configuration.buckets =
state.s3.configuration.buckets.add(data);
}
}
removeBucket(json: S3Update, state: S) {
const data = _.get(json, 'removeBucket', false);
if (data) {
state.s3.configuration.buckets.delete(data);
}
}
endpoint(json: S3Update, state: S) {
const data = _.get(json, 'setEndpoint', false);
if (data && state.s3.credentials) {
state.s3.credentials.endpoint = data;
}
}
accessKeyId(json: S3Update , state: S) {
const data = _.get(json, 'setAccessKeyId', false);
if (data && state.s3.credentials) {
state.s3.credentials.accessKeyId = data;
}
}
secretAccessKey(json: S3Update, state: S) {
const data = _.get(json, 'setSecretAccessKey', false);
if (data && state.s3.credentials) {
state.s3.credentials.secretAccessKey = data;
useS3State.setState(
compose([
credentials,
configuration,
currentBucket,
addBucket,
removeBucket,
endpoint,
accessKeyId,
secretAccessKey,
].map(reducer => reducer.bind(reducer, data))
)(useS3State.getState())
)
}
}
}
const credentials = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'credentials', false);
if (data) {
state.credentials = data;
}
return state;
}
const configuration = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'configuration', false);
if (data) {
state.configuration = {
buckets: new Set(data.buckets),
currentBucket: data.currentBucket
};
}
return state;
}
const currentBucket = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'setCurrentBucket', false);
if (data && state.s3) {
state.configuration.currentBucket = data;
}
return state;
}
const addBucket = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'addBucket', false);
if (data) {
state.configuration.buckets =
state.configuration.buckets.add(data);
}
return state;
}
const removeBucket = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'removeBucket', false);
if (data) {
state.configuration.buckets.delete(data);
}
return state;
}
const endpoint = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'setEndpoint', false);
if (data && state.credentials) {
state.credentials.endpoint = data;
}
return state;
}
const accessKeyId = (json: S3Update , state: S3State): S3State => {
const data = _.get(json, 'setAccessKeyId', false);
if (data && state.credentials) {
state.credentials.accessKeyId = data;
}
return state;
}
const secretAccessKey = (json: S3Update, state: S3State): S3State => {
const data = _.get(json, 'setSecretAccessKey', false);
if (data && state.credentials) {
state.credentials.secretAccessKey = data;
}
return state;
}

View File

@ -0,0 +1,26 @@
import create, { State } from "zustand";
import { persist } from "zustand/middleware";
import { stateSetter } from "../lib/util";
export interface S3State extends State {
configuration: {
buckets: Set<string>;
currentBucket: string;
};
credentials: any | null; // TODO better type
set: (fn: (state: S3State) => void) => void;
};
const useS3State = create<S3State>(persist((set, get) => ({
configuration: {
buckets: new Set(),
currentBucket: ''
},
credentials: null,
set: fn => stateSetter(fn, set)
}), {
blacklist: ['configuration'],
name: 'LandscapeS3State'
}));
export default useS3State;

View File

@ -104,7 +104,7 @@ export default class GlobalStore extends BaseStore<StoreState> {
this.inviteReducer.reduce(data);
this.metadataReducer.reduce(data);
this.localReducer.reduce(data, this.state);
this.s3Reducer.reduce(data, this.state);
this.s3Reducer.reduce(data);
this.groupReducer.reduce(data);
this.launchReducer.reduce(data);
this.connReducer.reduce(data, this.state);

View File

@ -184,7 +184,6 @@ export function ChatResource(props: ChatResourceProps) {
(!showBanner && hasLoadedAllowed) ? contacts : modifiedContacts
}
onUnmount={appendUnsent}
s3={props.s3}
placeholder="Message..."
message={unsent[station] || ''}
deleteMessage={clearUnsent}

View File

@ -19,7 +19,6 @@ type ChatInputProps = IuseS3 & {
ourContact: unknown;
envelopes: Envelope[];
onUnmount(msg: string): void;
s3: unknown;
placeholder: string;
message: string;
deleteMessage(): void;

View File

@ -30,7 +30,6 @@ export function LinkResource(props: LinkResourceProps) {
api,
baseUrl,
groups,
s3,
} = props;
const rid = association.resource;
@ -66,7 +65,6 @@ export function LinkResource(props: LinkResourceProps) {
render={(props) => {
return (
<LinkWindow
s3={s3}
association={resource}
resource={resourcePath}
graph={graph}

View File

@ -27,7 +27,6 @@ interface LinkWindowProps {
group: Group;
path: string;
api: GlobalApi;
s3: S3State;
}
export function LinkWindow(props: LinkWindowProps) {
const { graph, api, association } = props;
@ -62,7 +61,7 @@ return;
return (
<Col key={0} mx="auto" mt="4" maxWidth="768px" width="100%" flexShrink={0} px={3}>
{ canWrite ? (
<LinkSubmit s3={props.s3} name={name} ship={ship.slice(1)} api={api} />
<LinkSubmit name={name} ship={ship.slice(1)} api={api} />
) : (
<Text>There are no links here yet. You do not have permission to post to this collection.</Text>
)
@ -94,7 +93,7 @@ return null;
return (
<React.Fragment key={index.toString()}>
<Col key={index.toString()} mx="auto" mt="4" maxWidth="768px" width="100%" flexShrink={0} px={3}>
<LinkSubmit s3={props.s3} name={name} ship={ship.slice(1)} api={api} />
<LinkSubmit name={name} ship={ship.slice(1)} api={api} />
</Col>
<LinkItem {...linkProps} />
</React.Fragment>

View File

@ -7,16 +7,16 @@ import { S3State } from '@urbit/api';
import SubmitDragger from '~/views/components/SubmitDragger';
import { createPost } from '~/logic/api/graph';
import { hasProvider } from 'oembed-parser';
import useS3State from '~/logic/state/s3';
interface LinkSubmitProps {
api: GlobalApi;
s3: S3State;
name: string;
ship: string;
}
const LinkSubmit = (props: LinkSubmitProps) => {
const { canUpload, uploadDefault, uploading, promptUpload } = useS3(props.s3);
const { canUpload, uploadDefault, uploading, promptUpload } = useS3();
const [submitFocused, setSubmitFocused] = useState(false);
const [urlFocused, setUrlFocused] = useState(false);

View File

@ -109,15 +109,15 @@ export function EditProfile(props: any): ReactElement {
<Input id="nickname" label="Name" mb={3} />
<Col width="100%">
<Text mb={2}>Description</Text>
<MarkdownField id="bio" mb={3} s3={props.s3} />
<MarkdownField id="bio" mb={3} />
</Col>
<ColorInput id="color" label="Sigil Color" mb={3} />
<Row mb={3} width="100%">
<Col pr={2} width="50%">
<ImageInput id="cover" label="Cover Image" s3={props.s3} />
<ImageInput id="cover" label="Cover Image" />
</Col>
<Col pl={2} width="50%">
<ImageInput id="avatar" label="Profile Image" s3={props.s3} />
<ImageInput id="avatar" label="Profile Image" />
</Col>
</Row>
<Checkbox mb={3} id="isPublic" label="Public Profile" />

View File

@ -108,7 +108,6 @@ export function Profile(props: any): ReactElement {
<EditProfile
ship={ship}
contact={contact}
s3={props.s3}
api={props.api}
/>
) : (

View File

@ -41,7 +41,6 @@ export default function ProfileScreen(props: any) {
hasLoaded={Object.keys(contacts).length !== 0}
contact={contact}
api={props.api}
s3={props.s3}
isEdit={isEdit}
/>
</Box>

View File

@ -30,7 +30,6 @@ export function PublishResource(props: PublishResourceProps) {
history={props.history}
match={props.match}
location={props.location}
s3={props.s3}
/>
</Box>
);

View File

@ -9,7 +9,6 @@ import { PostFormSchema, PostForm } from './NoteForm';
import GlobalApi from '~/logic/api/global';
import { getLatestRevision, editPost } from '~/logic/lib/publish';
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
import { S3State } from '~/types';
interface EditPostProps {
ship: string;
@ -17,11 +16,10 @@ interface EditPostProps {
note: GraphNode;
api: GlobalApi;
book: string;
s3: S3State;
}
export function EditPost(props: EditPostProps & RouteComponentProps): ReactElement {
const { note, book, noteId, api, ship, history, s3 } = props;
const { note, book, noteId, api, ship, history } = props;
const [revNum, title, body] = getLatestRevision(note);
const location = useLocation();
@ -58,7 +56,6 @@ export function EditPost(props: EditPostProps & RouteComponentProps): ReactEleme
cancel
history={history}
onSubmit={onSubmit}
s3={s3}
submitLabel="Update"
loadingText="Updating..."
/>

View File

@ -28,7 +28,6 @@ interface MarkdownEditorProps {
value: string;
onChange: (s: string) => void;
onBlur?: (e: any) => void;
s3: S3State;
}
const PromptIfDirty = () => {
@ -74,7 +73,7 @@ export function MarkdownEditor(
[onBlur]
);
const { uploadDefault, canUpload } = useS3(props.s3);
const { uploadDefault, canUpload } = useS3();
const onFileDrag = useCallback(
async (files: FileList | File[], e: DragEvent) => {

View File

@ -6,7 +6,6 @@ import { MarkdownEditor } from './MarkdownEditor';
export const MarkdownField = ({
id,
s3,
...rest
}: { id: string } & Parameters<typeof Box>[0]) => {
const [{ value, onBlur }, { error, touched }, { setValue }] = useField(id);
@ -35,7 +34,6 @@ export const MarkdownField = ({
onBlur={handleBlur}
value={value}
onChange={setValue}
s3={s3}
/>
<ErrorLabel mt="2" hasError={Boolean(error && touched)}>
{error}

View File

@ -9,7 +9,6 @@ import {
import { AsyncButton } from '../../../components/AsyncButton';
import { Formik, Form, FormikHelpers } from 'formik';
import { MarkdownField } from './MarkdownField';
import { S3State } from '@urbit/api';
interface PostFormProps {
initial: PostFormSchema;
@ -21,7 +20,6 @@ interface PostFormProps {
) => Promise<any>;
submitLabel: string;
loadingText: string;
s3: S3State;
}
const formSchema = Yup.object({
@ -35,7 +33,7 @@ export interface PostFormSchema {
}
export function PostForm(props: PostFormProps) {
const { initial, onSubmit, submitLabel, loadingText, s3, cancel, history } = props;
const { initial, onSubmit, submitLabel, loadingText, cancel, history } = props;
return (
<Col width="100%" height="100%" p={[2, 4]}>
@ -67,7 +65,7 @@ export function PostForm(props: PostFormProps) {
>Cancel</Button>}
</Row>
</Row>
<MarkdownField flexGrow={1} id="body" s3={s3} />
<MarkdownField flexGrow={1} id="body" />
</Form>
</Formik>
</Col>

View File

@ -18,8 +18,7 @@ interface NoteRoutesProps {
association: Association;
baseUrl?: string;
rootUrl?: string;
group: Group;
s3: S3State;
group: Group
}
export function NoteRoutes(props: NoteRoutesProps & RouteComponentProps) {

View File

@ -27,7 +27,6 @@ interface NotebookRoutesProps {
baseUrl: string;
rootUrl: string;
association: Association;
s3: S3State;
}
export function NotebookRoutes(
@ -77,7 +76,6 @@ export function NotebookRoutes(
association={props.association}
graph={graph}
baseUrl={baseUrl}
s3={props.s3}
/>
)}
/>
@ -107,7 +105,6 @@ export function NotebookRoutes(
noteId={noteIdNum}
association={props.association}
group={group}
s3={props.s3}
{...routeProps}
/>
);

View File

@ -16,7 +16,6 @@ interface NewPostProps {
graph: Graph;
association: Association;
baseUrl: string;
s3: S3State;
}
export default function NewPost(props: NewPostProps & RouteComponentProps) {
@ -53,7 +52,6 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) {
onSubmit={onSubmit}
submitLabel="Publish"
loadingText="Posting..."
s3={props.s3}
/>
);
}

View File

@ -18,12 +18,10 @@ export function BackgroundPicker({
bgType,
bgUrl,
api,
s3
}: {
bgType: BgType;
bgUrl?: string;
api: GlobalApi;
s3: S3State;
}): ReactElement {
const rowSpace = { my: 0, alignItems: 'center' };
const radioProps = { my: 4, mr: 4, name: 'bgType' };
@ -36,7 +34,6 @@ export function BackgroundPicker({
<ImageInput
ml="3"
api={api}
s3={s3}
id="bgUrl"
name="bgUrl"
label="URL"

View File

@ -34,11 +34,10 @@ interface FormSchema {
interface DisplayFormProps {
api: GlobalApi;
s3: S3State;
}
export default function DisplayForm(props: DisplayFormProps) {
const { api, s3 } = props;
const { api } = props;
const { hideAvatars, hideNicknames, background, set: setLocalState } = useLocalState();
@ -94,7 +93,6 @@ export default function DisplayForm(props: DisplayFormProps) {
bgType={props.values.bgType}
bgUrl={props.values.bgUrl}
api={api}
s3={s3}
/>
<Checkbox
label="Disable avatars"

View File

@ -12,6 +12,7 @@ import {
import { BucketList } from './BucketList';
import GlobalApi from '~/logic/api/global';
import { S3State } from '~/types/s3-update';
import useS3State from '~/logic/state/s3';
interface FormSchema {
s3bucket: string;
@ -23,11 +24,11 @@ interface FormSchema {
interface S3FormProps {
api: GlobalApi;
s3: S3State;
}
export default function S3Form(props: S3FormProps): ReactElement {
const { api, s3 } = props;
const { api } = props;
const s3 = useS3State();
const onSubmit = useCallback(
(values: FormSchema) => {

View File

@ -13,7 +13,6 @@ type ProfileProps = StoreState & { api: GlobalApi; ship: string };
export default function Settings({
api,
s3
}: ProfileProps) {
return (
<Box
@ -27,10 +26,9 @@ export default function Settings({
>
<DisplayForm
api={api}
s3={s3}
/>
<RemoteContentForm api={api} />
<S3Form api={api} s3={s3} />
<S3Form api={api} />
<SecuritySettings api={api} />
</Box>
);

View File

@ -17,14 +17,13 @@ import useS3 from '~/logic/lib/useS3';
type ImageInputProps = Parameters<typeof Box>[0] & {
id: string;
label: string;
s3: S3State;
placeholder?: string;
};
export function ImageInput(props: ImageInputProps): ReactElement {
const { id, label, s3, caption, placeholder } = props;
const { id, label, caption, placeholder } = props;
const { uploadDefault, canUpload, uploading } = useS3(s3);
const { uploadDefault, canUpload, uploading } = useS3();
const [field, meta, { setValue, setError }] = useField(id);

View File

@ -3,7 +3,7 @@ import useS3 from '~/logic/lib/useS3';
const withS3 = (Component, params = {}) => {
return React.forwardRef((props: any, ref) => {
const s3 = useS3(props.s3, params);
const s3 = useS3(params);
return <Component ref={ref} {...s3} {...props} />;
});

View File

@ -43,11 +43,10 @@ interface GroupAdminSettingsProps {
group: Group;
association: Association;
api: GlobalApi;
s3: S3State;
}
export function GroupAdminSettings(props: GroupAdminSettingsProps) {
const { group, association, s3 } = props;
const { group, association } = props;
const { metadata } = association;
const history = useHistory();
const currentPrivate = 'invite' in props.group.policy;
@ -130,7 +129,6 @@ return null;
caption="A picture for your group"
placeholder="Enter URL"
disabled={disabled}
s3={s3}
/>
<Checkbox
id="isPrivate"

View File

@ -21,8 +21,6 @@ interface GroupSettingsProps {
group: Group;
association: Association;
api: GlobalApi;
notificationsGroupConfig: GroupNotificationsConfig;
s3: S3State;
baseUrl: string;
}
export function GroupSettings(props: GroupSettingsProps) {

View File

@ -78,7 +78,6 @@ export function GroupsPane(props: GroupsPaneProps) {
association={groupAssociation!}
group={group!}
api={api}
s3={props.s3}
{...routeProps}
baseUrl={baseUrl}

View File

@ -22,7 +22,6 @@ export function PopoverRoutes(
baseUrl: string;
group: Group;
association: Association;
s3: S3State;
api: GlobalApi;
notificationsGroupConfig: GroupNotificationsConfig;
rootIdentity: Contact;
@ -124,7 +123,6 @@ export function PopoverRoutes(
group={props.group}
association={props.association}
api={props.api}
s3={props.s3}
/>
)}
{view === 'participants' && (