Fixed lint problems, fixed builds
This commit is contained in:
parent
39aa70a9a6
commit
f018dafa4f
7 changed files with 87 additions and 61 deletions
|
|
@ -36,7 +36,7 @@ function getSafeMessageHeight(height: number | undefined) {
|
|||
* @param props Parameter props.
|
||||
* @returns unknown.
|
||||
*/
|
||||
function MessageComponent(props: {
|
||||
function MessageComponent({ message, notEncrypted, measureRef }: {
|
||||
message: RawMessage & {
|
||||
failed?: boolean;
|
||||
};
|
||||
|
|
@ -50,16 +50,16 @@ function MessageComponent(props: {
|
|||
|
||||
const [decodedContent, setDecodedContent] = React.useState("");
|
||||
const [isReady, setIsReady] = React.useState(false);
|
||||
const safeMessageHeight = getSafeMessageHeight(props.message.height);
|
||||
const safeMessageHeight = getSafeMessageHeight(message.height);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.notEncrypted) {
|
||||
setDecodedContent(props.message.content);
|
||||
if (notEncrypted) {
|
||||
setDecodedContent(message.content);
|
||||
setIsReady(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const content = props.message.content;
|
||||
const content = message.content;
|
||||
const secret = sharedSecret;
|
||||
let active = true;
|
||||
|
||||
|
|
@ -94,27 +94,27 @@ function MessageComponent(props: {
|
|||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [decrypt, props.message.content, props.notEncrypted, sharedSecret]);
|
||||
}, [decrypt, message.content, notEncrypted, sharedSecret]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={props.measureRef}
|
||||
className={`w-full flex justify-start transition-opacity duration-150 ${(props.message.failed || props.message.message_state === "awaiting") && "opacity-50"}`}
|
||||
ref={measureRef}
|
||||
className={`w-full flex justify-start transition-opacity duration-150 ${(message.failed || message.message_state === "awaiting") && "opacity-50"}`}
|
||||
>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger
|
||||
render={
|
||||
<div
|
||||
className={`selection-foreground flex gap-1 items-center justify-center max-w-[80%] rounded-lg px-2 py-px whitespace-pre-wrap break-all ${
|
||||
props.message.sent_by_self
|
||||
? props.message.failed
|
||||
message.sent_by_self
|
||||
? message.failed
|
||||
? "bg-destructive/75 text-destructive-foreground"
|
||||
: "bg-primary text-primary-foreground"
|
||||
: "bg-muted"
|
||||
} ${!isReady && "animate-pulse"} ${isMobile && "select-none"}`}
|
||||
>
|
||||
{props.message.failed &&
|
||||
props.message.message_state === "awaiting" && (
|
||||
{message.failed &&
|
||||
message.message_state === "awaiting" && (
|
||||
<Tooltip>
|
||||
<TooltipContent>
|
||||
<p>Failed to send message</p>
|
||||
|
|
@ -122,8 +122,8 @@ function MessageComponent(props: {
|
|||
<TooltipTrigger render={<AlertTriangle size={17} />} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{!props.message.failed &&
|
||||
props.message.message_state === "awaiting" && (
|
||||
{!message.failed &&
|
||||
message.message_state === "awaiting" && (
|
||||
<Clock size={17} />
|
||||
)}
|
||||
{isReady ? (
|
||||
|
|
@ -133,7 +133,7 @@ function MessageComponent(props: {
|
|||
className="block rounded-sm"
|
||||
style={{
|
||||
width: generateFixedLoadingSize(
|
||||
props.message.content.length,
|
||||
message.content.length,
|
||||
safeMessageHeight,
|
||||
),
|
||||
minHeight: safeMessageHeight,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,13 @@ export default function Provider(props: { children: ReactNode }) {
|
|||
const { send, subscribePush } = useTTP();
|
||||
|
||||
const [liveMessagesState, setLiveMessagesState] = useState<LiveMessage[]>([]);
|
||||
const [currentSharedSecret, setCurrentSharedSecret] = useState("");
|
||||
const [currentSharedSecretState, setCurrentSharedSecretState] = useState<{
|
||||
userId: number;
|
||||
value: string;
|
||||
}>({
|
||||
userId: 0,
|
||||
value: "",
|
||||
});
|
||||
|
||||
const inputBoxRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
|
@ -75,16 +81,13 @@ export default function Provider(props: { children: ReactNode }) {
|
|||
return Number(rawId ?? 0);
|
||||
}, [locationSearch]);
|
||||
|
||||
const userIdValueFromLastRender = useRef(userIdValue);
|
||||
|
||||
useEffect(() => {
|
||||
if (userIdValue === userIdValueFromLastRender.current) {
|
||||
return;
|
||||
const currentSharedSecret = useMemo(() => {
|
||||
if (currentSharedSecretState.userId !== userIdValue) {
|
||||
return "";
|
||||
}
|
||||
|
||||
userIdValueFromLastRender.current = userIdValue;
|
||||
setCurrentSharedSecret("");
|
||||
}, [userIdValue]);
|
||||
return currentSharedSecretState.value;
|
||||
}, [currentSharedSecretState, userIdValue]);
|
||||
|
||||
// Load shared secret
|
||||
useEffect(() => {
|
||||
|
|
@ -105,11 +108,17 @@ export default function Provider(props: { children: ReactNode }) {
|
|||
);
|
||||
|
||||
if (active) {
|
||||
setCurrentSharedSecret(sharedSecret);
|
||||
setCurrentSharedSecretState({
|
||||
userId: userIdValue,
|
||||
value: sharedSecret,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
if (active) {
|
||||
setCurrentSharedSecret("");
|
||||
setCurrentSharedSecretState({
|
||||
userId: userIdValue,
|
||||
value: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ export default function Screen() {
|
|||
return messagesRef.current[index]?.timestamp ?? index;
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line react-hooks/incompatible-library
|
||||
const virtualizer = useVirtualizer({
|
||||
count: messages.length,
|
||||
getScrollElement: () => scrollRef.current,
|
||||
|
|
|
|||
|
|
@ -33,8 +33,28 @@ export const context = React.createContext<CryptoContextType | undefined>(
|
|||
export default function Provider(props: { children: React.ReactNode }) {
|
||||
const apiRef = React.useRef<ApiRef | null>(null);
|
||||
|
||||
const value = React.useMemo(
|
||||
() => createCryptoActions(() => apiRef.current),
|
||||
const value = React.useMemo<CryptoContextType>(
|
||||
() => ({
|
||||
encrypt: async (secret, plaintext) => {
|
||||
const api = apiRef.current;
|
||||
if (!api) throw new Error("API not initialized");
|
||||
return await api.encrypt(secret, plaintext);
|
||||
},
|
||||
decrypt: async (secret, ciphertext) => {
|
||||
const api = apiRef.current;
|
||||
if (!api) throw new Error("API not initialized");
|
||||
return await api.decrypt(secret, ciphertext);
|
||||
},
|
||||
getSharedSecret: async (ownPrivateKey, ownPublicKey, otherPublicKey) => {
|
||||
const api = apiRef.current;
|
||||
if (!api) throw new Error("API not initialized");
|
||||
return await api.getSharedSecret(
|
||||
ownPrivateKey,
|
||||
ownPublicKey,
|
||||
otherPublicKey,
|
||||
);
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
|
|
@ -56,11 +76,11 @@ export default function Provider(props: { children: React.ReactNode }) {
|
|||
|
||||
/**
|
||||
* Creates crypto action functions that safely delegate to the worker API.
|
||||
* @param getApiRef Function that returns worker API reference when initialized.
|
||||
* @param apiRef Worker API reference object.
|
||||
* @returns Typed crypto action functions.
|
||||
*/
|
||||
export function createCryptoActions(
|
||||
getApiRef: () => ApiRef | null,
|
||||
apiRef: React.RefObject<ApiRef | null>,
|
||||
): CryptoContextType {
|
||||
/**
|
||||
* Encrypts plaintext by delegating to the crypto worker API.
|
||||
|
|
@ -72,9 +92,9 @@ export function createCryptoActions(
|
|||
secret: string,
|
||||
plaintext: string,
|
||||
): Promise<string> => {
|
||||
const apiRef = getApiRef();
|
||||
if (!apiRef) throw new Error("API not initialized");
|
||||
return await apiRef.encrypt(secret, plaintext);
|
||||
const api = apiRef.current;
|
||||
if (!api) throw new Error("API not initialized");
|
||||
return await api.encrypt(secret, plaintext);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -87,9 +107,9 @@ export function createCryptoActions(
|
|||
secret: string,
|
||||
ciphertext: string,
|
||||
): Promise<string> => {
|
||||
const apiRef = getApiRef();
|
||||
if (!apiRef) throw new Error("API not initialized");
|
||||
return await apiRef.decrypt(secret, ciphertext);
|
||||
const api = apiRef.current;
|
||||
if (!api) throw new Error("API not initialized");
|
||||
return await api.decrypt(secret, ciphertext);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -104,9 +124,9 @@ export function createCryptoActions(
|
|||
ownPublicKey: string,
|
||||
otherPublicKey: string,
|
||||
): Promise<string> => {
|
||||
const apiRef = getApiRef();
|
||||
if (!apiRef) throw new Error("API not initialized");
|
||||
return await apiRef.getSharedSecret(
|
||||
const api = apiRef.current;
|
||||
if (!api) throw new Error("API not initialized");
|
||||
return await api.getSharedSecret(
|
||||
ownPrivateKey,
|
||||
ownPublicKey,
|
||||
otherPublicKey,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { useStorage } from "@tensamin/storage/context";
|
|||
|
||||
// Wrapper function to pass user data to some component
|
||||
export default function Wrapper(props: {
|
||||
userId?: number | "own";
|
||||
userId: number | "own";
|
||||
loading: React.ReactNode;
|
||||
component: (user: User) => React.ReactNode;
|
||||
}) {
|
||||
|
|
@ -15,40 +15,34 @@ export default function Wrapper(props: {
|
|||
const [user, setUser] = useState<User | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.userId == null) {
|
||||
setUser(failedUser);
|
||||
return;
|
||||
}
|
||||
if (props.userId == null) return;
|
||||
|
||||
let active = true;
|
||||
|
||||
if (props.userId === "own") {
|
||||
load("user_id")
|
||||
.then((id) => {
|
||||
get(id)
|
||||
.then((user) => (active ? setUser(user) : null))
|
||||
.catch(() => setUser(failedUser));
|
||||
})
|
||||
.catch(() => setUser(failedUser));
|
||||
return;
|
||||
}
|
||||
void (async () => {
|
||||
try {
|
||||
const userId =
|
||||
props.userId === "own" ? await load("user_id") : props.userId;
|
||||
const value = await get(userId);
|
||||
|
||||
get(props.userId)
|
||||
.then((value) => {
|
||||
if (active) {
|
||||
setUser(value);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
} catch {
|
||||
if (active) {
|
||||
setUser(failedUser);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [load, get, props.userId]);
|
||||
|
||||
if (props.userId == null) {
|
||||
return <>{props.component(failedUser)}</>;
|
||||
}
|
||||
|
||||
return <>{user ? props.component(user) : props.loading}</>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue