(feat): add cache for conversations and communities
(feat): conversations now get moved to the top when engaged with (qol): update todo
This commit is contained in:
parent
006c222cc5
commit
fd15215f15
14 changed files with 178 additions and 67 deletions
|
|
@ -8,13 +8,13 @@ import { Select, SelectContent, SelectItem, SelectTrigger } from "@tensamin/ui";
|
||||||
import { displayCallId } from "@tensamin/call/utils";
|
import { displayCallId } from "@tensamin/call/utils";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { SidebarTrigger, useSidebar } from "@tensamin/ui";
|
import { SidebarTrigger, useSidebar } from "@tensamin/ui";
|
||||||
import { useTTP } from "@tensamin/ttp";
|
|
||||||
import { WindowControls as Controls } from "@tensamin/ui";
|
import { WindowControls as Controls } from "@tensamin/ui";
|
||||||
|
import { useSession } from "@tensamin/storage/session";
|
||||||
|
|
||||||
export default function Navbar({ forMobile }: { forMobile: boolean }) {
|
export default function Navbar({ forMobile }: { forMobile: boolean }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const callState = useCall((state) => state.state);
|
const callState = useCall((state) => state.state);
|
||||||
const { contacts } = useTTP();
|
const { contacts } = useSession();
|
||||||
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,18 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||||
import { useTTP } from "@tensamin/ttp";
|
|
||||||
|
|
||||||
import Switch from "./switch";
|
import Switch from "./switch";
|
||||||
import ConversationModal from "../modal/conversation";
|
import ConversationModal from "../modal/conversation";
|
||||||
import CommunityModal from "../modal/community";
|
import CommunityModal from "../modal/community";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
|
import { useSession } from "@tensamin/storage/session";
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes List.
|
|
||||||
* @param none This function has no parameters.
|
|
||||||
* @returns unknown.
|
|
||||||
*/
|
|
||||||
export default function List() {
|
export default function List() {
|
||||||
const [category, setCategory] = React.useState<
|
const [category, setCategory] = React.useState<
|
||||||
"conversations" | "communities"
|
"conversations" | "communities"
|
||||||
>("conversations");
|
>("conversations");
|
||||||
|
|
||||||
const { contacts, communities } = useTTP();
|
const { contacts, communities } = useSession();
|
||||||
const items = category === "conversations" ? contacts : communities;
|
const items = category === "conversations" ? contacts : communities;
|
||||||
|
|
||||||
const scrollRef = React.useRef<HTMLDivElement | null>(null);
|
const scrollRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import Login from "@/routes/screens/login";
|
||||||
|
|
||||||
import ChatContext from "@tensamin/chat/context";
|
import ChatContext from "@tensamin/chat/context";
|
||||||
import { useInitializeCall } from "@tensamin/call/store";
|
import { useInitializeCall } from "@tensamin/call/store";
|
||||||
import { Provider as SocketContext } from "@tensamin/ttp";
|
import { Provider as TTPProvider } from "@tensamin/ttp";
|
||||||
import UserContext from "@tensamin/user/context";
|
import UserContext from "@tensamin/user/context";
|
||||||
import DeeplinkContext from "@tensamin/tauri/deeplinkHandler";
|
import DeeplinkContext from "@tensamin/tauri/deeplinkHandler";
|
||||||
import NotificationsProvider from "@tensamin/notifications/context";
|
import NotificationsProvider from "@tensamin/notifications/context";
|
||||||
|
|
@ -35,6 +35,7 @@ import z from "zod";
|
||||||
import { useEffect, useState, type ReactNode } from "react";
|
import { useEffect, useState, type ReactNode } from "react";
|
||||||
|
|
||||||
import Storage from "@tensamin/storage/context";
|
import Storage from "@tensamin/storage/context";
|
||||||
|
import Session from "@tensamin/storage/session";
|
||||||
import Crypto from "@tensamin/crypto/context";
|
import Crypto from "@tensamin/crypto/context";
|
||||||
import Mobile from "@tensamin/tauri/context";
|
import Mobile from "@tensamin/tauri/context";
|
||||||
|
|
||||||
|
|
@ -141,20 +142,22 @@ function RootShell() {
|
||||||
|
|
||||||
function AppShell() {
|
function AppShell() {
|
||||||
return (
|
return (
|
||||||
<SocketContext>
|
<TTPProvider>
|
||||||
<UserContext>
|
<Session>
|
||||||
<CallInit />
|
<UserContext>
|
||||||
<TAuthWrapper>
|
<CallInit />
|
||||||
<AppLayout>
|
<TAuthWrapper>
|
||||||
<ChatContext>
|
<AppLayout>
|
||||||
<NotificationsProvider>
|
<ChatContext>
|
||||||
<Outlet />
|
<NotificationsProvider>
|
||||||
</NotificationsProvider>
|
<Outlet />
|
||||||
</ChatContext>
|
</NotificationsProvider>
|
||||||
</AppLayout>
|
</ChatContext>
|
||||||
</TAuthWrapper>
|
</AppLayout>
|
||||||
</UserContext>
|
</TAuthWrapper>
|
||||||
</SocketContext>
|
</UserContext>
|
||||||
|
</Session>
|
||||||
|
</TTPProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1
bun.lock
1
bun.lock
|
|
@ -197,6 +197,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tensamin/shared": "workspace:*",
|
"@tensamin/shared": "workspace:*",
|
||||||
|
"@tensamin/ttp": "workspace:*",
|
||||||
"@tensamin/ui": "*",
|
"@tensamin/ui": "*",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { useTTP } from "@tensamin/ttp";
|
|
||||||
import { Button, Popover, PopoverContent, PopoverTrigger } from "@tensamin/ui";
|
import { Button, Popover, PopoverContent, PopoverTrigger } from "@tensamin/ui";
|
||||||
import Wrapper from "@tensamin/user/wrapper";
|
import Wrapper from "@tensamin/user/wrapper";
|
||||||
import { Mail } from "lucide-react";
|
import { Mail } from "lucide-react";
|
||||||
import { sendCallInvite } from "../../store";
|
import { sendCallInvite } from "../../store";
|
||||||
import { log, toast } from "@tensamin/shared/log";
|
import { log, toast } from "@tensamin/shared/log";
|
||||||
|
import { useSession } from "@tensamin/storage/session";
|
||||||
|
|
||||||
export default function InviteButton({
|
export default function InviteButton({
|
||||||
className,
|
className,
|
||||||
|
|
@ -12,7 +12,7 @@ export default function InviteButton({
|
||||||
className?: string;
|
className?: string;
|
||||||
iconSize?: number;
|
iconSize?: number;
|
||||||
}) {
|
}) {
|
||||||
const { contacts } = useTTP();
|
const { contacts } = useSession();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { useTTP } from "@tensamin/ttp";
|
||||||
import { log, toast } from "@tensamin/shared/log";
|
import { log, toast } from "@tensamin/shared/log";
|
||||||
import { cn, useIsMobile } from "@tensamin/ui";
|
import { cn, useIsMobile } from "@tensamin/ui";
|
||||||
import { encryptText } from "@tensamin/crypto/worker";
|
import { encryptText } from "@tensamin/crypto/worker";
|
||||||
|
import { useSession } from "@tensamin/storage/session";
|
||||||
|
|
||||||
export default function InputComponent({
|
export default function InputComponent({
|
||||||
value,
|
value,
|
||||||
|
|
@ -23,6 +24,7 @@ export default function InputComponent({
|
||||||
const { send } = useTTP();
|
const { send } = useTTP();
|
||||||
const { addLiveMessage, sharedSecret, userId, inputBoxRef } = useChat();
|
const { addLiveMessage, sharedSecret, userId, inputBoxRef } = useChat();
|
||||||
const { load } = useStorage();
|
const { load } = useStorage();
|
||||||
|
const { moveUserIdToTop } = useSession();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
void load("settings.reverse_enter_behavior").then((shouldInvert) => {
|
void load("settings.reverse_enter_behavior").then((shouldInvert) => {
|
||||||
|
|
@ -78,6 +80,8 @@ export default function InputComponent({
|
||||||
toast("error", "Failed to send message");
|
toast("error", "Failed to send message");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
moveUserIdToTop(userId);
|
||||||
|
|
||||||
setValue("");
|
setValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { useUser } from "@tensamin/user/context";
|
||||||
import { useStorage } from "@tensamin/storage/context";
|
import { useStorage } from "@tensamin/storage/context";
|
||||||
import { useTTP } from "@tensamin/ttp";
|
import { useTTP } from "@tensamin/ttp";
|
||||||
import { log } from "@tensamin/shared/log";
|
import { log } from "@tensamin/shared/log";
|
||||||
|
import { useSession } from "@tensamin/storage/session";
|
||||||
|
|
||||||
export const context = createContext<contextType | undefined>(undefined);
|
export const context = createContext<contextType | undefined>(undefined);
|
||||||
|
|
||||||
|
|
@ -59,6 +60,7 @@ export default function Provider(props: { children: ReactNode }) {
|
||||||
const { get } = useUser();
|
const { get } = useUser();
|
||||||
const { load } = useStorage();
|
const { load } = useStorage();
|
||||||
const { send, subscribePush } = useTTP();
|
const { send, subscribePush } = useTTP();
|
||||||
|
const { moveUserIdToTop } = useSession();
|
||||||
|
|
||||||
const [liveMessagesState, setLiveMessagesState] = useState<LiveMessage[]>([]);
|
const [liveMessagesState, setLiveMessagesState] = useState<LiveMessage[]>([]);
|
||||||
const [currentSharedSecretState, setCurrentSharedSecretState] = useState<{
|
const [currentSharedSecretState, setCurrentSharedSecretState] = useState<{
|
||||||
|
|
@ -171,32 +173,39 @@ export default function Provider(props: { children: ReactNode }) {
|
||||||
[send, userIdValue, currentSharedSecret, decryptText],
|
[send, userIdValue, currentSharedSecret, decryptText],
|
||||||
);
|
);
|
||||||
|
|
||||||
const addLiveMessage = useCallback((message: RawMessage) => {
|
const addLiveMessage = useCallback(
|
||||||
const localId =
|
(message: RawMessage) => {
|
||||||
globalThis.crypto?.randomUUID?.() ??
|
const localId =
|
||||||
`${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
globalThis.crypto?.randomUUID?.() ??
|
||||||
|
`${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||||
|
|
||||||
setLiveMessagesState((prev) => [
|
if (!message.sent_by_self) {
|
||||||
...prev,
|
moveUserIdToTop(userIdValue);
|
||||||
{
|
}
|
||||||
...message,
|
|
||||||
localId,
|
|
||||||
failed: false,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
setLiveMessagesState((prev) => [
|
||||||
setFailed: (failed: boolean) => {
|
...prev,
|
||||||
setLiveMessagesState((prev) =>
|
{
|
||||||
prev.map((liveMessage) =>
|
...message,
|
||||||
liveMessage.localId === localId
|
localId,
|
||||||
? { ...liveMessage, failed }
|
failed: false,
|
||||||
: liveMessage,
|
},
|
||||||
),
|
]);
|
||||||
);
|
|
||||||
},
|
return {
|
||||||
};
|
setFailed: (failed: boolean) => {
|
||||||
}, []);
|
setLiveMessagesState((prev) =>
|
||||||
|
prev.map((liveMessage) =>
|
||||||
|
liveMessage.localId === localId
|
||||||
|
? { ...liveMessage, failed }
|
||||||
|
: liveMessage,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[userIdValue, moveUserIdToTop],
|
||||||
|
);
|
||||||
|
|
||||||
const clearLiveMessages = useCallback(() => {
|
const clearLiveMessages = useCallback(() => {
|
||||||
setLiveMessagesState([]);
|
setLiveMessagesState([]);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { toast as sonnerToast } from "sonner";
|
||||||
import { message as messageSchema } from "@tensamin/shared/data";
|
import { message as messageSchema } from "@tensamin/shared/data";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@tensamin/ui";
|
import { Avatar, AvatarFallback, AvatarImage } from "@tensamin/ui";
|
||||||
import { isTauri } from "@tauri-apps/api/core";
|
import { isTauri } from "@tauri-apps/api/core";
|
||||||
|
import { useSession } from "@tensamin/storage/session";
|
||||||
|
|
||||||
export const context = createContext<contextType | undefined>(undefined);
|
export const context = createContext<contextType | undefined>(undefined);
|
||||||
|
|
||||||
|
|
@ -27,6 +28,7 @@ export default function Provider(props: { children: React.ReactNode }) {
|
||||||
const { get } = useUser();
|
const { get } = useUser();
|
||||||
const { decryptText, getSharedSecret } = useCrypto();
|
const { decryptText, getSharedSecret } = useCrypto();
|
||||||
const { addLiveMessage, userId } = useChat();
|
const { addLiveMessage, userId } = useChat();
|
||||||
|
const { moveUserIdToTop } = useSession();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return subscribePush(async (ttpMessage) => {
|
return subscribePush(async (ttpMessage) => {
|
||||||
|
|
@ -60,6 +62,8 @@ export default function Provider(props: { children: React.ReactNode }) {
|
||||||
|
|
||||||
// add notification symbol to conversation cards
|
// add notification symbol to conversation cards
|
||||||
|
|
||||||
|
moveUserIdToTop(sender_id);
|
||||||
|
|
||||||
if (isTauri()) {
|
if (isTauri()) {
|
||||||
console.log("weewoo");
|
console.log("weewoo");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -86,6 +90,7 @@ export default function Provider(props: { children: React.ReactNode }) {
|
||||||
get,
|
get,
|
||||||
addLiveMessage,
|
addLiveMessage,
|
||||||
userId,
|
userId,
|
||||||
|
moveUserIdToTop,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,13 @@ export const failedUser = {
|
||||||
username: "unknown",
|
username: "unknown",
|
||||||
} as z.infer<typeof ttp.get_user_data.response>;
|
} as z.infer<typeof ttp.get_user_data.response>;
|
||||||
|
|
||||||
|
export type Contacts = z.infer<
|
||||||
|
typeof ttp.challenge_response.response.shape.contacts
|
||||||
|
>;
|
||||||
|
export type Communities = z.infer<
|
||||||
|
typeof ttp.challenge_response.response.shape.communities
|
||||||
|
>;
|
||||||
|
|
||||||
// TTP
|
// TTP
|
||||||
export const ttp = {
|
export const ttp = {
|
||||||
identification: {
|
identification: {
|
||||||
|
|
@ -230,6 +237,8 @@ export interface Storage extends SettingsStorageDefaults {
|
||||||
analytics_usage_data: boolean;
|
analytics_usage_data: boolean;
|
||||||
analytics_done: boolean;
|
analytics_done: boolean;
|
||||||
legal_docs: z.infer<typeof legalDocsSchema>;
|
legal_docs: z.infer<typeof legalDocsSchema>;
|
||||||
|
cached_contacts: Contacts;
|
||||||
|
cached_communities: Communities;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const storageDefaults: Storage = {
|
export const storageDefaults: Storage = {
|
||||||
|
|
@ -260,4 +269,6 @@ export const storageDefaults: Storage = {
|
||||||
unix: 0,
|
unix: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
cached_contacts: [],
|
||||||
|
cached_communities: [],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
"./session": "./src/session.tsx",
|
||||||
"./context": "./src/context.tsx",
|
"./context": "./src/context.tsx",
|
||||||
"./indexed-db": "./src/indexed-db.ts"
|
"./indexed-db": "./src/indexed-db.ts"
|
||||||
},
|
},
|
||||||
|
|
@ -14,6 +15,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tensamin/shared": "workspace:*",
|
"@tensamin/shared": "workspace:*",
|
||||||
|
"@tensamin/ttp": "workspace:*",
|
||||||
"@tensamin/ui": "*",
|
"@tensamin/ui": "*",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0"
|
"react-dom": "^19.2.0"
|
||||||
|
|
|
||||||
84
packages/storage/src/session.tsx
Normal file
84
packages/storage/src/session.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { useTTP } from "@tensamin/ttp";
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
type ReactNode,
|
||||||
|
useState,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
} from "react";
|
||||||
|
import { useStorage } from "./context";
|
||||||
|
import type { Contacts, Communities } from "@tensamin/shared/data";
|
||||||
|
|
||||||
|
interface SessionContextType {
|
||||||
|
contacts: Contacts;
|
||||||
|
communities: Communities;
|
||||||
|
moveUserIdToTop: (userId: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SessionContext = createContext<SessionContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export default function SessionProvider({ children }: { children: ReactNode }) {
|
||||||
|
const { freshContacts, freshCommunities } = useTTP();
|
||||||
|
const { load, save } = useStorage();
|
||||||
|
const [contacts, setContacts] = useState<Contacts>([]);
|
||||||
|
const [communities, setCommunities] = useState<Communities>([]);
|
||||||
|
|
||||||
|
// Get cached data and merge fresh data
|
||||||
|
useEffect(() => {
|
||||||
|
load("cached_contacts").then((cachedData) => {
|
||||||
|
setContacts([
|
||||||
|
...freshContacts,
|
||||||
|
...cachedData.filter(
|
||||||
|
(item) =>
|
||||||
|
!freshContacts.some((fresh) => fresh.user_id === item.user_id),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
load("cached_communities").then((cachedData) => {
|
||||||
|
if (cachedData && freshCommunities) {
|
||||||
|
setCommunities([
|
||||||
|
...freshCommunities,
|
||||||
|
...cachedData.filter(
|
||||||
|
(item) =>
|
||||||
|
!freshCommunities.some(
|
||||||
|
(fresh) => fresh.community_id === item.community_id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [load, freshContacts, freshCommunities]);
|
||||||
|
|
||||||
|
// Save data
|
||||||
|
useEffect(() => {
|
||||||
|
save("cached_contacts", contacts);
|
||||||
|
}, [contacts, save]);
|
||||||
|
useEffect(() => {
|
||||||
|
save("cached_communities", communities);
|
||||||
|
}, [communities, save]);
|
||||||
|
|
||||||
|
const moveUserIdToTop = (userId: number) => {
|
||||||
|
setContacts((prevContacts) => {
|
||||||
|
const userIndex = prevContacts.findIndex(
|
||||||
|
(contact) => contact.user_id === userId,
|
||||||
|
);
|
||||||
|
if (userIndex === -1) return prevContacts;
|
||||||
|
const [user] = prevContacts.splice(userIndex, 1);
|
||||||
|
return [user, ...prevContacts];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SessionContext.Provider value={{ contacts, communities, moveUserIdToTop }}>
|
||||||
|
{children}
|
||||||
|
</SessionContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSession(): SessionContextType {
|
||||||
|
const context = useContext(SessionContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error("useSession must be used within a SessionProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
1
packages/storage/todo.md
Normal file
1
packages/storage/todo.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
- Add `notifications: number` to contacts
|
||||||
|
|
@ -25,15 +25,15 @@ import {
|
||||||
TRANSPORT_URL,
|
TRANSPORT_URL,
|
||||||
} from "./values";
|
} from "./values";
|
||||||
import {
|
import {
|
||||||
|
type Communities,
|
||||||
|
type Contacts,
|
||||||
ttp as schemas,
|
ttp as schemas,
|
||||||
ttp,
|
|
||||||
type TTP as Schemas,
|
type TTP as Schemas,
|
||||||
} from "@tensamin/shared/data";
|
} from "@tensamin/shared/data";
|
||||||
import { LoadingScreen as Loading } from "@tensamin/ui";
|
import { LoadingScreen as Loading } from "@tensamin/ui";
|
||||||
import { ErrorScreen } from "@tensamin/ui";
|
import { ErrorScreen } from "@tensamin/ui";
|
||||||
|
|
||||||
import { version } from "../../../package.json";
|
import { version } from "../../../package.json";
|
||||||
import z from "zod";
|
|
||||||
import { decryptText } from "@tensamin/crypto/worker";
|
import { decryptText } from "@tensamin/crypto/worker";
|
||||||
|
|
||||||
const FATAL_IDENTIFICATION_ERROR_TYPES = new Set([
|
const FATAL_IDENTIFICATION_ERROR_TYPES = new Set([
|
||||||
|
|
@ -147,10 +147,8 @@ type ContextType = {
|
||||||
ownPing: number;
|
ownPing: number;
|
||||||
iotaPing: number;
|
iotaPing: number;
|
||||||
identified: boolean;
|
identified: boolean;
|
||||||
contacts: z.infer<typeof ttp.challenge_response.response.shape.contacts>;
|
freshContacts: Contacts;
|
||||||
communities: z.infer<
|
freshCommunities: Communities;
|
||||||
typeof ttp.challenge_response.response.shape.communities
|
|
||||||
>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TTPContext = createContext<ContextType | undefined>(undefined);
|
const TTPContext = createContext<ContextType | undefined>(undefined);
|
||||||
|
|
@ -178,12 +176,8 @@ export function Provider(props: {
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [errorDescription, setErrorDescription] = useState("");
|
const [errorDescription, setErrorDescription] = useState("");
|
||||||
|
|
||||||
const [communities, setCommunities] = useState<
|
const [freshCommunities, setFreshCommunities] = useState<Communities>([]);
|
||||||
z.infer<typeof ttp.challenge_response.response.shape.communities>
|
const [freshContacts, setFreshContacts] = useState<Contacts>([]);
|
||||||
>([]);
|
|
||||||
const [contacts, setContacts] = useState<
|
|
||||||
z.infer<typeof ttp.challenge_response.response.shape.contacts>
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const clientRef = useRef<ReturnType<
|
const clientRef = useRef<ReturnType<
|
||||||
typeof createTransportClient<Schemas>
|
typeof createTransportClient<Schemas>
|
||||||
|
|
@ -500,8 +494,8 @@ export function Provider(props: {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Data handling
|
// Data handling
|
||||||
setContacts(finalResponse.data.contacts);
|
setFreshContacts(finalResponse.data.contacts);
|
||||||
setCommunities(finalResponse.data.communities);
|
setFreshCommunities(finalResponse.data.communities);
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -613,8 +607,8 @@ export function Provider(props: {
|
||||||
ownPing,
|
ownPing,
|
||||||
iotaPing,
|
iotaPing,
|
||||||
identified,
|
identified,
|
||||||
contacts,
|
freshContacts,
|
||||||
communities,
|
freshCommunities,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,9 @@ function dedupePackageRecords(records: PackageRecord[]): PackageRecord[] {
|
||||||
|
|
||||||
unique.set(key, {
|
unique.set(key, {
|
||||||
...existing,
|
...existing,
|
||||||
copiedFiles: [...new Set([...existing.copiedFiles, ...record.copiedFiles])],
|
copiedFiles: [
|
||||||
|
...new Set([...existing.copiedFiles, ...record.copiedFiles]),
|
||||||
|
],
|
||||||
license: existing.license ?? record.license,
|
license: existing.license ?? record.license,
|
||||||
description: existing.description ?? record.description,
|
description: existing.description ?? record.description,
|
||||||
homepage: existing.homepage ?? record.homepage,
|
homepage: existing.homepage ?? record.homepage,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue