Skip to content

Commit

Permalink
feat: optimize chat state
Browse files Browse the repository at this point in the history
  • Loading branch information
kaladivo committed Nov 30, 2023
1 parent ea901f8 commit c7c5dc9
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function ChatTextInput(): JSX.Element | null {
setReplyToMessage(undefined)
setSelectedImage(undefined)

void sendMessage(message)()
void sendMessage(message)
}, [
value,
selectedImage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ function TextMessage({
void sendMessage({
...messageItem.message.message,
time: unixMillisecondsNow(),
})()
})
}
}, [sendMessage, messageItem])

Expand Down
15 changes: 7 additions & 8 deletions apps/mobile/src/state/chat/atoms/fetchNewMessagesActionAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,12 @@ export const fetchAndStoreMessagesForInboxAtom = atom<
return T.of(inbox)
}

reportError(
'warn',
'Api Error fetching messages for inbox. Trying to create the inbox again.',
error
)

if (error._tag === 'inboxDoesNotExist')
if (error._tag === 'inboxDoesNotExist') {
reportError(
'warn',
'Api Error fetching messages for inbox. Trying to create the inbox again.',
error
)
return pipe(
getNotificationToken(),
TE.fromTask,
Expand All @@ -236,7 +235,7 @@ export const fetchAndStoreMessagesForInboxAtom = atom<
),
T.map(() => inbox)
)

}
return T.of(inbox)
},
({newMessages, updatedInbox}) => {
Expand Down
88 changes: 45 additions & 43 deletions apps/mobile/src/state/chat/atoms/sendMessageActionAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import {type FocusAtomType} from '../../../utils/atomUtils/FocusAtomType'
import {type ActionAtomType} from '../../../utils/atomUtils/ActionAtomType'
import {now} from '@vexl-next/domain/dist/utility/UnixMilliseconds.brand'
import replaceImageFileUrisWithBase64 from '../utils/replaceImageFileUrisWithBase64'
import {InteractionManager} from 'react-native'

type SendMessageAtom = ActionAtomType<
[ChatMessage],
T.Task<ChatMessageWithState>
ReturnType<typeof InteractionManager.runAfterInteractions>
>

export default function sendMessageActionAtom(
Expand All @@ -37,51 +38,52 @@ export default function sendMessageActionAtom(
const chatWithMessages = get(chatWithMessagesAtom)
const {chat} = chatWithMessages

return pipe(
TE.Do,
T.delay(2000), // TODO check for value maybe try `InteractionManager.runAfterTransaction`?
TE.chainTaskK(() => replaceImageFileUrisWithBase64(message)),
TE.chainW((m) =>
sendMessage({
message: m,
api: api.chat,
senderKeypair: chat.inbox.privateKey,
receiverPublicKey: chat.otherSide.publicKey,
})
),
TE.match(
(e): ChatMessageWithState => {
if (
e._tag === 'inboxDoesNotExist' ||
e._tag === 'notPermittedToSendMessageToTargetInbox'
) {
return {
state: 'received',
message: {
messageType: 'INBOX_DELETED',
time: now(),
senderPublicKey: chatWithMessages.chat.otherSide.publicKey,
uuid: generateChatMessageId(),
text: 'Inbox deleted',
},
return InteractionManager.runAfterInteractions(() => {
void pipe(
TE.Do,
TE.chainTaskK(() => replaceImageFileUrisWithBase64(message)),
TE.chainW((m) =>
sendMessage({
message: m,
api: api.chat,
senderKeypair: chat.inbox.privateKey,
receiverPublicKey: chat.otherSide.publicKey,
})
),
TE.match(
(e): ChatMessageWithState => {
if (
e._tag === 'inboxDoesNotExist' ||
e._tag === 'notPermittedToSendMessageToTargetInbox'
) {
return {
state: 'received',
message: {
messageType: 'INBOX_DELETED',
time: now(),
senderPublicKey: chatWithMessages.chat.otherSide.publicKey,
uuid: generateChatMessageId(),
text: 'Inbox deleted',
},
}
}
}

return {
state: 'sendingError',
error: e,
return {
state: 'sendingError',
error: e,
message,
}
},
(): ChatMessageWithState => ({
state: 'sent',
message,
}
},
(): ChatMessageWithState => ({
state: 'sent',
message,
})
),
T.map((message) => {
set(chatWithMessagesAtom, addMessageToChat(message))
return message
})
),
T.map((message) => {
set(chatWithMessagesAtom, addMessageToChat(message))
return message
})
)
)()
})
})
}
75 changes: 39 additions & 36 deletions apps/mobile/src/utils/atomUtils/atomWithParsedMmkvStorage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {atom, type PrimitiveAtom} from 'jotai'
import {InteractionManager} from 'react-native'
import {type z} from 'zod'
import {pipe} from 'fp-ts/function'
import * as E from 'fp-ts/Either'
Expand All @@ -24,23 +25,21 @@ function toShadowStorageAtom<Value extends z.ZodObject<any>>(
const newValue = getValueFromSetStateActionOfAtom(update)(() =>
get(baseAtom)
)
set(baseAtom, newValue)

pipe(
{...newValue, [AUTHOR_ID_KEY]: baseAtom.toString()},
storage.setJSON(key),
E.match(
(l) => {
void InteractionManager.runAfterInteractions(() => {
pipe(
{...newValue, [AUTHOR_ID_KEY]: baseAtom.toString()},
storage.setJSON(key),
E.getOrElseW((l) => {
reportError(
'warn',
`Error while saving value to storage. Key: ${key}`,
l
)
},
(r) => {
set(baseAtom, newValue)
}
})
)
)
})
}
)
}
Expand Down Expand Up @@ -96,33 +95,37 @@ export function atomWithParsedMmkvStorage<Value extends z.ZodObject<any>>(
(changedKey) => {
if (changedKey !== key) return

pipe(
storage.getJSON(key),
E.filterOrElseW(
(value) => value[AUTHOR_ID_KEY] !== mmkvAtom.toString(),
() =>
({
_tag: 'authoredByThisAtom',
}) as const
),
E.map(({[AUTHOR_ID_KEY]: _, ...rest}) => rest),
E.chainW(safeParse(zodType)),
E.match((e) => {
if (e._tag === 'authoredByThisAtom') return
if (e._tag === 'ValueNotSet') {
console.info(
`MMKV value for key '${key}' was deleted. Setting atom to default value`
void InteractionManager.runAfterInteractions(() => {
pipe(
storage.getJSON(key),
E.filterOrElseW(
(value) => value[AUTHOR_ID_KEY] !== coreAtom.toString(),
() =>
({
_tag: 'authoredByThisAtom',
}) as const
),
E.map(({[AUTHOR_ID_KEY]: _, ...rest}) => rest),
E.chainW(safeParse(zodType)),
E.match((e) => {
if (e._tag === 'authoredByThisAtom') {
return
}
if (e._tag === 'ValueNotSet') {
console.info(
`MMKV value for key '${key}' was deleted. Setting atom to default value`
)
setAtom(defaultValue)
return
}
reportError(
'warn',
`Error while parsing stored mmkv value in onChange function. Key: '${key}'`,
e
)
setAtom(defaultValue)
return
}
reportError(
'warn',
`Error while parsing stored mmkv value in onChange function. Key: '${key}'`,
e
)
}, setAtom)
)
}, setAtom)
)
})
}
)

Expand Down
14 changes: 10 additions & 4 deletions apps/mobile/src/utils/reportTime.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
const numberFormatIntl = new Intl.NumberFormat('cs', {})

export function startMeasure(name: string): () => string {
export function startMeasure(name: string): (millisec?: boolean) => string {
const start = Date.now()

return () => {
return (millisec) => {
const end = Date.now()
// TODO log to server?

const prettyDuration = numberFormatIntl.format((end - start) / 1000)
const prettyDuration = numberFormatIntl.format(
millisec ? end - start : (end - start) / 1000
)

console.log(`⌛️ Measuring: ${name}. Took: ${prettyDuration}sec`)
console.log(
`⌛️ Measuring: ${name}. Took: ${prettyDuration} ${
millisec ? 'milisec' : 'sec'
}`
)

return prettyDuration
}
Expand Down

0 comments on commit c7c5dc9

Please sign in to comment.