chat: adding localstorage sync and cleanup

This commit is contained in:
Hunter Miller 2021-07-01 11:58:21 -05:00
parent a00ab9e347
commit fa5e974ad4
4 changed files with 47 additions and 57 deletions

View File

@ -4,6 +4,9 @@ module.exports = {
'jest': true
},
rules: {
// Because we use styled system, and use
// the convention of each prop on a new line
// we probably shouldn't keep this on
'max-lines-per-function': ['off', {}]
}
};

View File

@ -113,7 +113,6 @@ interface ChatEditorProps {
inCodeMode: boolean;
placeholder: string;
submit: (message: string) => void;
onUnmount: (message: string) => void;
onPaste: (codemirrorInstance, event: ClipboardEvent) => void;
}

View File

@ -5,18 +5,16 @@ import tokenizeMessage from '~/logic/lib/tokenizeMessage';
import useStorage, { IuseStorage } from '~/logic/lib/useStorage';
import { MOBILE_BROWSER_REGEX } from '~/logic/lib/util';
import { withLocalState } from '~/logic/state/local';
import withStorage from '~/views/components/withStorage';
import ChatEditor, { CodeMirrorShim } from './ChatEditor';
import airlock from '~/logic/api';
import { ChatAvatar } from './ChatAvatar';
import { useChatStore } from './ChatPane';
import { useImperativeHandle } from 'react';
type ChatInputProps = PropsWithChildren<IuseStorage & {
hideAvatars: boolean;
ourContact?: Contact;
placeholder: string;
onUnmount(msg: string): void;
deleteMessage(): void;
onSubmit: (contents: Content[]) => void;
}>;
@ -69,8 +67,9 @@ const MobileSubmitButton = ({ enabled, onSubmit }) => (
</Box>
);
export function ChatInput({ ourContact, hideAvatars, placeholder, onSubmit, onUnmount }: ChatInputProps) {
export const ChatInput = React.forwardRef(({ ourContact, hideAvatars, placeholder, onSubmit }: ChatInputProps, ref) => {
const chatEditor = useRef<CodeMirrorShim>(null);
useImperativeHandle(ref, () => ({ uploadFiles }));
const {
message,
setMessage
@ -104,15 +103,6 @@ export function ChatInput({ ourContact, hideAvatars, placeholder, onSubmit, onUn
chatEditor.current.focus();
}
function uploadSuccess(url: string) {
if (uploadingPaste) {
chatEditor.current.setValue(url);
setUploadingPaste(false);
} else {
onSubmit([{ url }]);
}
}
function onPaste(codemirrorInstance, event: React.ClipboardEvent<HTMLTextAreaElement>) {
if (!event.clipboardData || !event.clipboardData.files.length) {
return;
@ -130,11 +120,24 @@ export function ChatInput({ ourContact, hideAvatars, placeholder, onSubmit, onUn
}
Array.from(files).forEach((file) => {
uploadDefault(file)
.then(this.uploadSuccess)
.catch(this.uploadError);
.then(uploadSuccess)
.catch(uploadError);
});
}
function uploadSuccess(url: string) {
if (uploadingPaste) {
chatEditor.current.setValue(url);
setUploadingPaste(false);
} else {
onSubmit([{ url }]);
}
}
function uploadError(error: Error) {
console.log(error);
}
return (
<InputBox>
<Row p='12px 4px 12px 12px' flexShrink={0} alignItems='center'>
@ -144,7 +147,6 @@ export function ChatInput({ ourContact, hideAvatars, placeholder, onSubmit, onUn
ref={chatEditor}
inCodeMode={inCodeMode}
submit={submit}
onUnmount={onUnmount}
onPaste={onPaste}
placeholder={placeholder}
/>
@ -181,11 +183,10 @@ export function ChatInput({ ourContact, hideAvatars, placeholder, onSubmit, onUn
)}
</InputBox>
);
}
});
// @ts-ignore withLocalState prop passing weirdness
export default withLocalState<Omit<ChatInputProps, keyof IuseStorage>, 'hideAvatars', ChatInput>(
// @ts-ignore withLocalState prop passing weirdness
withStorage<ChatInputProps, ChatInput>(ChatInput, { accept: 'image/*' }),
ChatInput,
['hideAvatars']
);

View File

@ -1,39 +1,40 @@
import { Col } from '@tlon/indigo-react';
import { Content, Graph, Post } from '@urbit/api';
import bigInt, { BigInteger } from 'big-integer';
import _ from 'lodash';
import React, { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
import create from 'zustand';
import { useFileDrag } from '~/logic/lib/useDrag';
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
import { retrieve } from '~/logic/lib/useLocalStorageState';
import { useOurContact } from '~/logic/state/contact';
import { useGraphTimesent } from '~/logic/state/graph';
import ShareProfile from '~/views/apps/chat/components/ShareProfile';
import { Loading } from '~/views/components/Loading';
import SubmitDragger from '~/views/components/SubmitDragger';
import ChatInput, { ChatInput as NakedChatInput } from './ChatInput';
import ChatInput from './ChatInput';
import ChatWindow from './ChatWindow';
interface useChatStoreType {
id: string;
message: string;
messageStore: Record<string, string>;
setMessage: (message: string) => void;
}
export const useChatStore = create<useChatStoreType>(set => ({
const unsentKey = 'chat-unsent';
export const useChatStore = create<useChatStoreType>((set, get) => ({
id: '',
message: '',
setMessage: (message: string) => set({ message })
messageStore: retrieve(unsentKey, {}),
setMessage: (message: string) => {
const store = get().messageStore;
store[get().id] = message;
localStorage.setItem(unsentKey, JSON.stringify(store));
set({ message });
}
}));
// const unsentKey = 'chat-unsent';
// export const useChatStore = create<useChatStoreType>(set => ({
// message: retrieve(unsentKey, ''),
// setMessage: (message: string) => {
// set({ message });
// localStorage.setItem(unsentKey, message);
// }
// }));
interface ChatPaneProps {
/**
* A key to uniquely identify a ChatPane instance. Should be either the
@ -100,7 +101,7 @@ export function ChatPane(props: ChatPaneProps): ReactElement {
} = props;
const graphTimesentMap = useGraphTimesent(id);
const ourContact = useOurContact();
const chatInput = useRef<NakedChatInput>();
const chatInput = useRef<{ uploadFiles: (files: FileList | File[]) => void }>();
const setMessage = useChatStore(s => s.setMessage);
const onFileDrag = useCallback(
@ -108,29 +109,19 @@ export function ChatPane(props: ChatPaneProps): ReactElement {
if (!chatInput.current) {
return;
}
(chatInput.current as NakedChatInput)?.uploadFiles(files);
chatInput.current?.uploadFiles(files);
},
[chatInput]
);
const { bind, dragging } = useFileDrag(onFileDrag);
const [, setUnsent] = useLocalStorageState<Record<string, string>>(
'chat-unsent',
{}
);
const appendUnsent = useCallback(
(u: string) => setUnsent(s => ({ ...s, [id]: u })),
[id]
);
const clearUnsent = useCallback(() => {
setUnsent((s) => {
if (id in s) {
return _.omit(s, id);
}
return s;
useEffect(() => {
const messageStore = retrieve(unsentKey, {});
useChatStore.setState({
id,
messageStore,
message: messageStore[id] || ''
});
}, [id]);
@ -145,8 +136,6 @@ export function ChatPane(props: ChatPaneProps): ReactElement {
const onReply = useCallback(
(msg: Post) => {
const message = props.onReply(msg);
console.log(message);
// setUnsent(s => ({ ...s, [id]: message }));
setMessage(message);
},
[id, props.onReply]
@ -185,9 +174,7 @@ export function ChatPane(props: ChatPaneProps): ReactElement {
ref={chatInput}
onSubmit={onSubmit}
ourContact={(promptShare.length === 0 && ourContact) || undefined}
onUnmount={appendUnsent}
placeholder="Message..."
deleteMessage={clearUnsent}
/>
)}
</Col>