Skip to content
Merged

wip #2362

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions __tests__/hooks/useMarkWaveNotificationsRead.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const createWrapper =
const setActiveIdentity = ({
address,
jwt,
connectedProfileId,
activeProfileProxyId,
activeProfileProxyCreatorId,
jwtAddress,
Expand All @@ -133,6 +134,7 @@ const setActiveIdentity = ({
}: {
readonly address?: string | undefined;
readonly jwt?: string | null | undefined;
readonly connectedProfileId?: string | null | undefined;
readonly activeProfileProxyId?: string | null | undefined;
readonly activeProfileProxyCreatorId?: string | null | undefined;
readonly jwtAddress?: string | undefined;
Expand All @@ -156,6 +158,7 @@ const setActiveIdentity = ({

useSeizeConnectContextMock.mockReturnValue({ address } as any);
useAuthMock.mockReturnValue({
connectedProfile: connectedProfileId ? { id: connectedProfileId } : null,
activeProfileProxy: activeProfileProxyId
? { id: activeProfileProxyId, created_by: { id: proxyCreatorId } }
: null,
Expand Down Expand Up @@ -233,6 +236,63 @@ describe("useMarkWaveNotificationsRead", () => {
});
});

it("treats the connected profile's own JWT role as primary auth when no proxy is active", async () => {
const invalidateNotifications = jest.fn();

setActiveIdentity({
address: "0xAAA",
jwt: "jwt-own-role",
connectedProfileId: "profile-1",
jwtRole: "profile-1",
});

const { result } = renderHook(() => useWaveNotificationsReadMarkerState(), {
wrapper: createWrapper(invalidateNotifications),
});

expect(result.current.proxyRoleIdentityKey).toBeNull();

await expect(
result.current.markWaveNotificationsRead("wave-own-role")
).resolves.toBe("sent");

expect(apiPostMock).toHaveBeenCalledTimes(1);
expect(apiPostMock).toHaveBeenCalledWith({
endpoint: "notifications/wave/wave-own-role/read",
headers: { Authorization: "Bearer jwt-own-role" },
});
});

it("does not treat a different JWT role as primary auth when no proxy is active", async () => {
const invalidateNotifications = jest.fn();

setActiveIdentity({
address: "0xAAA",
jwt: "jwt-other-role",
connectedProfileId: "profile-1",
jwtRole: "creator-1",
});

const { result } = renderHook(() => useWaveNotificationsReadMarkerState(), {
wrapper: createWrapper(invalidateNotifications),
});

expect(result.current.proxyRoleIdentityKey).toBe(
getWaveReadProxyRoleIdentityKey({
addressKey: "0xaaa",
proxyCreatorId: "creator-1",
})
);

await expect(
result.current.markWaveNotificationsRead("wave-other-role", {
queueIfBlocked: false,
})
).resolves.toBe("skipped");

expect(apiPostMock).not.toHaveBeenCalled();
});

it("does not queue a read before the wallet address is known", async () => {
const invalidateNotifications = jest.fn();

Expand Down
2 changes: 2 additions & 0 deletions hooks/useMarkWaveNotificationsRead.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ export type {

export const useWaveNotificationsReadMarkerState = ({
address,
connectedProfileId,
activeProfileProxyId,
activeProfileProxyCreatorId,
walletAuth,
invalidateNotifications,
}: WaveNotificationsReadMarkerConfig): WaveNotificationsReadMarkerState => {
const identityState = useWaveReadIdentityState({
address,
connectedProfileId,
activeProfileProxyId,
activeProfileProxyCreatorId,
walletAuth,
Expand Down
44 changes: 37 additions & 7 deletions hooks/useMarkWaveNotificationsRead.identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface WaveReadTemporaryProxyRoleIdentity {

export interface WaveReadIdentityConfig {
readonly address: string | undefined;
readonly connectedProfileId: string | null;
readonly activeProfileProxyId: string | null;
readonly activeProfileProxyCreatorId: string | null;
readonly walletAuth: string | null;
Expand Down Expand Up @@ -95,8 +96,30 @@ const getAuthHeaders = (walletAuth: string): AuthHeaders => ({
export const isWaveReadJwtExpired = (jwtExpiresAt: number): boolean =>
jwtExpiresAt <= Math.floor(Date.now() / 1000);

const normalizeWaveReadJwtRole = ({
role,
connectedProfileId,
activeProfileProxyId,
}: {
readonly role: string | null | undefined;
readonly connectedProfileId: string | null;
readonly activeProfileProxyId: string | null;
}): string | null => {
if (typeof role !== "string" || role.length === 0) {
return null;
}

if (activeProfileProxyId === null && role === connectedProfileId) {
return null;
}

return role;
};

const decodeWaveReadJwtIdentity = (
walletAuth: string | null
walletAuth: string | null,
connectedProfileId: string | null,
activeProfileProxyId: string | null
): WaveReadJwtIdentity | undefined => {
if (!walletAuth) {
return undefined;
Expand All @@ -120,10 +143,11 @@ const decodeWaveReadJwtIdentity = (

return {
addressKey,
proxyCreatorId:
typeof decodedJwt.role === "string" && decodedJwt.role.length > 0
? decodedJwt.role
: null,
proxyCreatorId: normalizeWaveReadJwtRole({
role: decodedJwt.role,
connectedProfileId,
activeProfileProxyId,
}),
jwtExpiresAt: expiresAt,
};
} catch {
Expand Down Expand Up @@ -247,14 +271,20 @@ const getProxyRoleIdentityKey = ({

export const useWaveReadIdentityState = ({
address,
connectedProfileId,
activeProfileProxyId,
activeProfileProxyCreatorId,
walletAuth,
}: WaveReadIdentityConfig): WaveReadIdentityState => {
const addressKey = getAddressKey(address);
const jwtIdentity = useMemo(
() => decodeWaveReadJwtIdentity(walletAuth),
[walletAuth]
() =>
decodeWaveReadJwtIdentity(
walletAuth,
connectedProfileId,
activeProfileProxyId
),
[activeProfileProxyId, connectedProfileId, walletAuth]
);
const verifiedAuthHeaders = useMemo(
() =>
Expand Down
4 changes: 3 additions & 1 deletion hooks/useMarkWaveNotificationsRead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import { useContext } from "react";
export function useWaveNotificationsReadMarkerState(): WaveNotificationsReadMarkerState {
const { invalidateNotifications } = useContext(ReactQueryWrapperContext);
const { address } = useSeizeConnectContext();
const { activeProfileProxy } = useAuth();
const { activeProfileProxy, connectedProfile } = useAuth();
const activeProfileProxyId = activeProfileProxy?.id ?? null;
const activeProfileProxyCreatorId = activeProfileProxy
? activeProfileProxy.created_by.id
: null;
const connectedProfileId = connectedProfile?.id ?? null;

return useWaveNotificationsReadMarkerStateFromConfig({
address,
connectedProfileId,
activeProfileProxyId,
activeProfileProxyCreatorId,
walletAuth: getAuthJwt(),
Expand Down