Skip to content

Commit

Permalink
Load messages on scroll
Browse files Browse the repository at this point in the history
  • Loading branch information
blaknite committed Nov 14, 2024
1 parent 6691c9a commit 3fe0333
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 54 deletions.
12 changes: 6 additions & 6 deletions src/components/PageComponents/Messages/ChannelChat.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
import {
type MessageWithState,
useDevice,
} from "@app/core/stores/deviceStore.ts";
import { useDevice } from "@app/core/stores/deviceStore.ts";
import { MessageStore } from "@app/core/stores/messageStore.ts";
import { Message } from "@components/PageComponents/Messages/Message.tsx";
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx";
import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.tsx";
import type { Protobuf, Types } from "@meshtastic/js";
import { InboxIcon } from "lucide-react";
import { useState, useEffect, useRef } from 'react';

export interface ChannelChatProps {
messages?: MessageWithState[];
messageStore: MessageStore;
channel: Types.ChannelNumber;
to: Types.Destination;
traceroutes?: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery>[];
}

export const ChannelChat = ({
messages,
messageStore,
channel,
to,
traceroutes,
}: ChannelChatProps): JSX.Element => {
const { nodes } = useDevice();
const messages = messageStore.messages;

return (
<div className="flex flex-grow flex-col">
Expand Down
2 changes: 2 additions & 0 deletions src/components/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const PageLayout = ({
noPadding,
actions,
children,
onScroll,
}: PageLayoutProps): JSX.Element => {
return (
<>
Expand Down Expand Up @@ -52,6 +53,7 @@ export const PageLayout = ({
"flex h-full w-full flex-col overflow-y-auto",
!noPadding && "pl-3 pr-3 ",
)}
onScroll={onScroll}
>
{children}
<Footer />
Expand Down
8 changes: 4 additions & 4 deletions src/core/stores/deviceStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface Device {
messageStores: {
direct: Map<number, MessageStore>;
broadcast: Map<Types.ChannelNumber, MessageStore>;
semaphore: number;
semaphore: boolean;
}
traceroutes: Map<
number,
Expand Down Expand Up @@ -131,7 +131,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
messageStores: {
direct: new Map(),
broadcast: new Map(),
semaphore: 0,
semaphore: false,
},
traceroutes: new Map(),
connection: undefined,
Expand Down Expand Up @@ -502,7 +502,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({

messageStore.addMessage(message);

device.messageStores.semaphore++;
device.messageStores.semaphore = !device.messageStores.semaphore;
}),
);
},
Expand Down Expand Up @@ -582,7 +582,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({

messageStore.setMessageState(messageId, state);

device.messageStores.semaphore++;
device.messageStores.semaphore = !device.messageStores.semaphore;
}),
);
},
Expand Down
100 changes: 58 additions & 42 deletions src/core/stores/messageStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,35 @@ export default class MessageStore {
constructor(deviceNumber: number, channelNumber: number) {
this.deviceNumber = deviceNumber;
this.channelNumber = channelNumber;
this.indexKey = `device/${this.deviceNumber}/group/${this.channelNumber}/index`;
this.messageIdsKey = `device/${this.deviceNumber}/group/${this.channelNumber}/message_ids`;
this.cursor = 0;
this.loaded = false;
this.messageIds = [];
this.messages = [];

this.loadMessageIds();
this.loadMessages();
}

get currentIndex(): number {
return parseInt(localStorage.getItem(this.indexKey)) || 0;
}

messageKey(messageIndex: number): string {
return `device/${this.deviceNumber}/group/${this.channelNumber}/message/${messageIndex}`;
}

nextIndex(): number {
const nextIndex = this.currentIndex + 1;

localStorage.setItem(this.indexKey, nextIndex);

return nextIndex;
messageKey(messageId: number): string {
return `device/${this.deviceNumber}/message/${messageId}`;
}

addMessage(message: MessageWithState): void {
const messageIndex = this.nextIndex();
const messageKey = this.messageKey(messageIndex);
this.setMessage(message);

localStorage.setItem(messageKey, JSON.stringify(message));
this.messageIds.unshift(message.id);
localStorage.setItem(this.messageIdsKey, JSON.stringify(this.messageIds));

this.messages.push(message);
}

getMessage(messageIndex: number): MessageWithState {
const messageKey = this.messageKey(messageIndex);

getMessage(messageId: number): MessageWithState {
const messageKey = this.messageKey(messageId);
const messageJSON = localStorage.getItem(messageKey);

if (messageJSON === null) {
return;
return null;
}

const message = JSON.parse(messageJSON);
Expand All @@ -57,45 +48,70 @@ export default class MessageStore {
return message;
}

setMessage(message: MessageWithState): void {
const messageKey = this.messageKey(message.id);

localStorage.setItem(messageKey, JSON.stringify(message));
}

setMessageState(messageId: number, state: MessageState): void {
this.messages.forEach((message, i) => {
if (message.id === messageId) {
const messageKey = this.messageKey(i + 1);
const message = this.getMessage(messageId);

message.state = state;

message.state = state;
localStorage.setItem(messageKey, JSON.stringify(message));
this.setMessage(message);

this.messages.forEach((m, i) => {
if (m.id === messageId) {
this.messages[i] = message
return;
}
});
}

deleteMessage(messageIndex: number): void {
const messageKey = this.messageKey(messageIndex);
deleteMessage(messageId: number): void {
const messageKey = this.messageKey(messageId);
localStorage.removeItem(messageKey);
}

loadMessages(): MessageWithState[] {
if (this.currentIndex === 0) {
loadMessageIds(): void {
const messageIdsJSON = localStorage.getItem(this.messageIdsKey);

if (messageIdsJSON === null) {
this.messageIds = [];
localStorage.setItem(this.messageIdsKey, JSON.stringify(this.messageIds));
return;
}

for (let i = 1; i <= this.currentIndex; i++) {
this.messages.push(this.getMessage(i));
}
this.messageIds = JSON.parse(messageIdsJSON);
}

clear() {
if (this.currentIndex === 0) {
return;
}
loadMessages(): void {
if (this.loaded) return;

for (let i = 1; i <= this.currentIndex; i++) {
this.deleteMessage(i);
}
let message;

for (let i = this.cursor; i <= 50 + this.cursor; i++) {
message = this.getMessage(this.messageIds[i]);

if (message === null) {
this.loaded = true;
return;
}

this.cursor = i;
this.messages.unshift(message);
};
}

clear() {
this.messageIds.forEach((messageId) => {
this.deleteMessage(messageId);
});

localStorage.removeItem(this.indexKey);
localStorage.removeItem(this.messageIdsKey);

this.messageIds = [];
this.messages = [];
}
}
19 changes: 17 additions & 2 deletions src/pages/Messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ export const MessagesPage = (): JSX.Element => {
const node = nodes.get(activeChat);
const nodeHex = node?.num ? numberToHexUnpadded(node.num) : "Unknown";

const [isLoading, setIsLoading] = useState(false);

const handleScroll = (event) => {
if (!isLoading && activeChat && event.currentTarget.scrollTop < 10) {
setIsLoading(true);

const messageStore = messageStores[chatType].get(activeChat);

messageStore.loadMessages();

setIsLoading(false);
}
};

return (
<>
<Sidebar>
Expand Down Expand Up @@ -72,6 +86,7 @@ export const MessagesPage = (): JSX.Element => {
</Sidebar>
<div className="flex flex-col flex-grow">
<PageLayout
onScroll={handleScroll}
label={`Messages: ${
chatType === "broadcast" && currentChannel
? getChannelName(currentChannel)
Expand Down Expand Up @@ -147,7 +162,7 @@ export const MessagesPage = (): JSX.Element => {
<ChannelChat
key={channel.index}
to="broadcast"
messages={messageStores.broadcast.get(channel.index).messages}
messageStore={messageStores.broadcast.get(channel.index)}
channel={channel.index}
/>
),
Expand All @@ -158,7 +173,7 @@ export const MessagesPage = (): JSX.Element => {
<ChannelChat
key={node.num}
to={activeChat}
messages={messageStores.direct.get(node.num).messages}
messageStore={messageStores.direct.get(node.num)}
channel={Types.ChannelNumber.Primary}
traceroutes={traceroutes.get(node.num)}
/>
Expand Down

0 comments on commit 3fe0333

Please sign in to comment.