<commit_before>import { Col } from '@tlon/indigo-react';
import { Content, Graph, Post } from '@urbit/api';
import bigInt, { BigInteger } from 'big-integer';
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import create from 'zustand';
import { useFileUpload } from '~/logic/lib/useFileUpload';
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 from './ChatInput';
import ChatWindow from './ChatWindow';

interface useChatStoreType {
  id: string;
  message: string;
  messageStore: Record<string, string>;
  setMessage: (message: string) => void;
}

const unsentKey = 'chat-unsent';

export const useChatStore = create<useChatStoreType>((set, get) => ({
  id: '',
  message: '',
  messageStore: retrieve(unsentKey, {}),
  setMessage: (message: string) => {
    const store = get().messageStore;
    store[get().id] = message;

    localStorage.setItem(unsentKey, JSON.stringify(store));
    set({ message });
  }
}));

interface ChatPaneProps {
  /**
   * A key to uniquely identify a ChatPane instance. Should be either the
   * resource for group chats or the @p for DMs
   */
  id: string;
  /**
   * The graph of the chat to render
   */
  graph: Graph;
  unreadCount: number;
  /**
   * User able to write to chat
   */
  canWrite: boolean;
  /**
   * Get contents of reply message
   */
  onReply: (msg: Post) => string;
  onDelete?: (msg: Post) => void;
  /**
   * Fetch more messages
   *
   * @param newer Get newer or older backlog
   * @returns Whether backlog is finished loading in that direction
   */
  fetchMessages: (newer: boolean) => Promise<boolean>;
  /**
   * Dismiss unreads for chat
   */
  dismissUnread: () => void;
  /**
   * Get permalink for a node
   */
  getPermalink: (idx: BigInteger) => string;
  isAdmin: boolean;
  /**
   * Post message with contents to channel
   */
  onSubmit: (contents: Content[]) => void;
  /**
   *
   * Users or group we haven't shared our contact with yet
   *
   * string[] - array of ships
   * string - path of group
   */
  promptShare?: string[] | string;
}

export function ChatPane(props: ChatPaneProps): ReactElement {
  const {
    graph,
    unreadCount,
    canWrite,
    id,
    getPermalink,
    isAdmin,
    dismissUnread,
    onSubmit,
    onDelete,
    promptShare = [],
    fetchMessages
  } = props;
  const graphTimesentMap = useGraphTimesent(id);
  const ourContact = useOurContact();
  const setMessage = useChatStore(s => s.setMessage);
  const { canUpload, drag } = useFileUpload({
    onSuccess: url => onSubmit([{ url }])
  });

  useEffect(() => {
    const messageStore = retrieve(unsentKey, {});
    useChatStore.setState({
      id,
      messageStore,
      message: messageStore[id] || ''
    });
  }, [id]);

  const scrollTo = new URLSearchParams(location.search).get('msg');

  const [showBanner, setShowBanner] = useState(false);
<commit_msg>chat: replacing manual persistence with middleware<commit_after>import { Col } from '@tlon/indigo-react';
import { Content, Graph, Post } from '@urbit/api';
import bigInt, { BigInteger } from 'big-integer';
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import create from 'zustand';
import { persist } from 'zustand/middleware';
import { useFileUpload } from '~/logic/lib/useFileUpload';
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 from './ChatInput';
import ChatWindow from './ChatWindow';

interface useChatStoreType {
  id: string;
  message: string;
  messageStore: Record<string, string>;
  restore: (id: string) => void;
  setMessage: (message: string) => void;
}

const unsentKey = 'chat-unsent';
export const useChatStore = create<useChatStoreType>(persist((set, get) => ({
  id: '',
  message: '',
  messageStore: {},
  restore: (id: string) => {
    const store = get().messageStore;
    set({
      id,
      messageStore: store,
      message: store[id] || ''
    });
  },
  setMessage: (message: string) => {
    const store = get().messageStore;
    store[get().id] = message;

    set({ message, messageStore: store });
  }
}), {
  name: unsentKey,
  whitelist: ['messageStore']
}));

interface ChatPaneProps {
  /**
   * A key to uniquely identify a ChatPane instance. Should be either the
   * resource for group chats or the @p for DMs
   */
  id: string;
  /**
   * The graph of the chat to render
   */
  graph: Graph;
  unreadCount: number;
  /**
   * User able to write to chat
   */
  canWrite: boolean;
  /**
   * Get contents of reply message
   */
  onReply: (msg: Post) => string;
  onDelete?: (msg: Post) => void;
  /**
   * Fetch more messages
   *
   * @param newer Get newer or older backlog
   * @returns Whether backlog is finished loading in that direction
   */
  fetchMessages: (newer: boolean) => Promise<boolean>;
  /**
   * Dismiss unreads for chat
   */
  dismissUnread: () => void;
  /**
   * Get permalink for a node
   */
  getPermalink: (idx: BigInteger) => string;
  isAdmin: boolean;
  /**
   * Post message with contents to channel
   */
  onSubmit: (contents: Content[]) => void;
  /**
   *
   * Users or group we haven't shared our contact with yet
   *
   * string[] - array of ships
   * string - path of group
   */
  promptShare?: string[] | string;
}

export function ChatPane(props: ChatPaneProps): ReactElement {
  const {
    graph,
    unreadCount,
    canWrite,
    id,
    getPermalink,
    isAdmin,
    dismissUnread,
    onSubmit,
    onDelete,
    promptShare = [],
    fetchMessages
  } = props;
  const graphTimesentMap = useGraphTimesent(id);
  const ourContact = useOurContact();
  const { restore, setMessage } = useChatStore(s => ({ setMessage: s.setMessage, restore: s.restore }));
  const { canUpload, drag } = useFileUpload({
    onSuccess: url => onSubmit([{ url }])
  });

  useEffect(() => {
    restore(id);
  }, [id]);

  const scrollTo = new URLSearchParams(location.search).get('msg');

  const [showBanner, setShowBanner] = useState(false);

{'fraction_non_alphanumeric': 0.13851817763460653, 'fraction_numerical': 0.0, 'mean_word_length': 3.4505119453924915, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7317997', 'n_tokens_mistral': 2052, 'n_tokens_neox': 2017, 'n_words': 585}