diff --git a/pkg/interface/.eslintrc.js b/pkg/interface/.eslintrc.js index 2527b85cf1..09212671c9 100644 --- a/pkg/interface/.eslintrc.js +++ b/pkg/interface/.eslintrc.js @@ -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', {}] } }; diff --git a/pkg/interface/src/views/apps/chat/components/ChatEditor.tsx b/pkg/interface/src/views/apps/chat/components/ChatEditor.tsx index d80dfb10c6..d23bd458b3 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatEditor.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatEditor.tsx @@ -113,7 +113,6 @@ interface ChatEditorProps { inCodeMode: boolean; placeholder: string; submit: (message: string) => void; - onUnmount: (message: string) => void; onPaste: (codemirrorInstance, event: ClipboardEvent) => void; } diff --git a/pkg/interface/src/views/apps/chat/components/ChatInput.tsx b/pkg/interface/src/views/apps/chat/components/ChatInput.tsx index c53ecb667f..d602977e0f 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatInput.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatInput.tsx @@ -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 void; }>; @@ -69,8 +67,9 @@ const MobileSubmitButton = ({ enabled, onSubmit }) => ( ); -export function ChatInput({ ourContact, hideAvatars, placeholder, onSubmit, onUnmount }: ChatInputProps) { +export const ChatInput = React.forwardRef(({ ourContact, hideAvatars, placeholder, onSubmit }: ChatInputProps, ref) => { const chatEditor = useRef(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) { 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 ( @@ -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 )} ); -} +}); // @ts-ignore withLocalState prop passing weirdness export default withLocalState, 'hideAvatars', ChatInput>( - // @ts-ignore withLocalState prop passing weirdness - withStorage(ChatInput, { accept: 'image/*' }), + ChatInput, ['hideAvatars'] ); diff --git a/pkg/interface/src/views/apps/chat/components/ChatPane.tsx b/pkg/interface/src/views/apps/chat/components/ChatPane.tsx index b71769f4d7..ec03898212 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatPane.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatPane.tsx @@ -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; setMessage: (message: string) => void; } -export const useChatStore = create(set => ({ +const unsentKey = 'chat-unsent'; + +export const useChatStore = create((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(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(); + 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>( - '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} /> )}