Skip to content

Commit

Permalink
feat(www): new participant in exchange notification (#1444)
Browse files Browse the repository at this point in the history
  • Loading branch information
douglasduteil authored Jun 16, 2024
1 parent 612f0d1 commit 51f1d3c
Show file tree
Hide file tree
Showing 27 changed files with 505 additions and 252 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ export default function Infinite_Exchange_List() {
{pages
.map((page) => page.data)
.flat()
.map((item) => (
<li key={item.id}>
<Item exchange_id={item.id} active={item.id === exchange_id} />
.map(({ exchange, is_unread }) => (
<li key={exchange.id}>
<Item
exchange_id={exchange.id}
active={exchange.id === exchange_id}
unread={is_unread}
/>
</li>
))}
<li className="col-span-full mx-auto">
Expand All @@ -83,9 +87,11 @@ export default function Infinite_Exchange_List() {
function Item({
active,
exchange_id,
unread,
}: {
active: boolean;
exchange_id: string;
unread: boolean;
}) {
const info = TRPC_React.exchanges.by_id.useQuery(exchange_id);
const { data: session } = useSession();
Expand All @@ -107,7 +113,7 @@ function Item({
title: title_style,
} = styles({
active,
unread: false,
unread,
with_return: Boolean(exchange.return),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import {
Denied,
Idle,
} from "@1.ui/react/icons";
import { useUpdateEffect } from "@react-hookz/web";
import type { UseQueryResult } from "@tanstack/react-query";
import { isAfter } from "date-fns";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
import type { ComponentPropsWithoutRef } from "react";
import { type ComponentPropsWithoutRef } from "react";
import { match } from "ts-pattern";

//
Expand All @@ -38,18 +39,24 @@ export default function Infinite_Thread_List(params: Params) {

return (
<Thread_InfiniteList info={query_info}>
{({ id, thread_id }) => {
{({ id, thread: { id: thread_id, updated_at } }) => {
return (
<li key={id}>
<UserThread_Item thread_id={thread_id} />
<UserThread_Item thread_id={thread_id} updated_at={updated_at} />
</li>
);
}}
</Thread_InfiniteList>
);
}

function UserThread_Item({ thread_id }: { thread_id: string }) {
function UserThread_Item({
thread_id,
updated_at,
}: {
thread_id: string;
updated_at: Date;
}) {
const { data: session } = useSession();
const pathname = usePathname();
const searchParams = useSearchParams();
Expand All @@ -58,6 +65,10 @@ function UserThread_Item({ thread_id }: { thread_id: string }) {
thread_id,
) as UseQueryResult<Inbox & { deal: { parent_id: string } }>;

useUpdateEffect(() => {
info.refetch();
}, [thread_id, updated_at]);

return (
<Thread_AsyncItem info={info}>
{({ inbox }) => {
Expand All @@ -77,6 +88,11 @@ function UserThread_Item({ thread_id }: { thread_id: string }) {
profile_id,
});
const href = `/@~/exchanges/inbox/${deal.parent_id}/${thread.id}`;
const is_active = pathname === href;
const unread = !is_active
? isAfter(thread.updated_at, last_seen_date)
: false;

return (
<Link
href={{
Expand All @@ -87,8 +103,8 @@ function UserThread_Item({ thread_id }: { thread_id: string }) {
<Thread_Item
last_update={last_message.updated_at}
variants={{
active: pathname === href,
unread: isAfter(last_message.updated_at, last_seen_date),
active: is_active,
unread,
}}
>
<Thread_Item.Avatar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export default function Infinite_Thread_Timeline({
}

utils.notification.count_unread.invalidate();
last_seen_thread_update.mutate({ thread_id, type: "EXCHANGE_NEW_MESSAGE" });

scroll_target_ref.current.scrollIntoView({
behavior: "smooth",
block: "end",
Expand All @@ -76,20 +78,22 @@ export default function Infinite_Thread_Timeline({
scroll_target_ref,
]);

useLayoutEffect(() => {
last_seen_thread_update.mutate({ thread_id, type: "INBOX_NEW_MESSAGE" });
}, [scroll_target_ref]);

const { data: session, status } = useSession();
const invalidate = useCallback(async () => {
await last_seen_thread_update.mutateAsync({
thread_id,
type: "EXCHANGE_NEW_MESSAGE",
});

await Promise.all([
utils.inbox.thread.messages.invalidate({ thread_id }),
utils.exchanges.by_id.invalidate(exchange.id),
utils.exchanges.me.inbox.by_thread_id.invalidate(thread_id),
utils.exchanges.me.inbox.next_actions.invalidate(),
utils.exchanges.by_id.invalidate(exchange.id),
utils.inbox.thread.messages.invalidate({ thread_id }),
utils.notification.count_unread.invalidate(),
]);
reset();
}, [utils, thread_id]);
}, [utils, thread_id, query_info.dataUpdatedAt]);

TRPC_React.inbox.thread.on_new_message.useSubscription(
{ token: session?.header.NEXTAUTH_TOKEN!, thread_id },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,18 @@ function Infinite_Thread_List() {
const search = search_params.get("q") ?? undefined;
const query_info = TRPC_React.inbox.find.useInfiniteQuery(
{ search },
{ getNextPageParam: ({ next_cursor }) => next_cursor },
{
getNextPageParam: ({ next_cursor }) => next_cursor,
// TODO(douglasduteil): allow loading from the selected thread
// ```
// import type { Params } from ":pipes/thread_by_id";
// const params = useParams<Partial<Params>>();
// ```
// Will required adding a previous cursor on the thread query
// Pretty difficult with the current implementation
// see https://github.com/prisma/prisma/discussions/12173
// initialCursor: params.thread_id,
},
);

return (
Expand Down Expand Up @@ -142,12 +153,12 @@ function UserThread_Item({ thread_id }: { thread_id: string }) {

export const item = tv({
slots: {
indicator: "float-right",
indicator: "float-right opacity-100 transition-opacity",
},
variants: {
unread: {
false: {
indicator: "hidden",
indicator: "opacity-0",
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ export default function Infinite_Thread_Timeline({
) as UseInfiniteQueryResult<{ data: Message }>;

const thread_seen = useCallback(async () => {
await utils.inbox.find.refetch();
await utils.inbox.by_thread_id.invalidate(thread_id);
await utils.inbox.thread.by_id.invalidate(thread_id);
await utils.notification.invalidate();
}, [last_seen_thread_update, utils]);

useLayoutEffect(() => {
last_seen_thread_update.mutate({ thread_id, type: "INBOX_NEW_MESSAGE" });
}, [scroll_target_ref]);
await last_seen_thread_update.mutateAsync({
thread_id,
type: "INBOX_NEW_MESSAGE",
});
await Promise.all([
utils.inbox.find.refetch(),
utils.inbox.by_thread_id.invalidate(thread_id),
utils.inbox.thread.by_id.invalidate(thread_id),
utils.notification.invalidate(),
]);
}, [last_seen_thread_update, utils, query_info.dataUpdatedAt]);

useLayoutEffect(() => {
if (!scroll_target_ref.current) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//

import { card } from "@1.ui/react/card/atom";
import { format } from "date-fns";
import { fr } from "date-fns/locale";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { select_exchange_message } from "./select_exchange_message";
import type { Notification } from "./type";

//

export function ExchangeNewMessage({
notification,
}: {
notification: Notification;
}) {
const session = useSession();
if (!session.data) return null;
const {
data: {
profile: { id: my_profile_id },
},
} = session;
const { base, body } = card();
const { id, created_at, read_at } = notification;
const exchange_message = select_exchange_message(notification);

if (!exchange_message) return null;

const {
exchange_id,
exchange: {
owner: { profile_id },
title,
},
message: {
author: { name },
thread_id,
},
} = exchange_message;
return (
<Link id={id} href={`/@~/exchanges/inbox/${exchange_id}/${thread_id}`}>
<div
className={base({ className: read_at ? "bg-transparent" : "bg-white" })}
>
<div className={body()}>
<time
className="float-right text-xs text-gray-500"
dateTime={created_at.toUTCString()}
title={created_at.toUTCString()}
>
{format(created_at, "Pp", { locale: fr })}
</time>
<b>{name}</b> vous a envoyé un nouveau message sur{" "}
{my_profile_id === profile_id ? "votre " : "l'"}échange{" "}
<i>{title}</i>
</div>
</div>
</Link>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//

import { card } from "@1.ui/react/card/atom";
import { format } from "date-fns";
import { fr } from "date-fns/locale";
import Link from "next/link";
import { select_exchange_message } from "./select_exchange_message";
import type { Notification } from "./type";

//

export function ExchangeNewParticipant({
notification,
}: {
notification: Notification;
}) {
const { base, body } = card();
const { id, created_at, read_at } = notification;
const exchange_message = select_exchange_message(notification);
if (!exchange_message) return null;

const {
exchange_id,
exchange: { title },
message: {
author: { name },
thread_id,
},
} = exchange_message;
return (
<Link id={id} href={`/@~/exchanges/inbox/${exchange_id}/${thread_id}`}>
<div
className={base({ className: read_at ? "bg-transparent" : "bg-white" })}
>
<div className={body()}>
<time
className="float-right text-xs text-gray-500"
dateTime={created_at.toUTCString()}
title={created_at.toUTCString()}
>
{format(created_at, "Pp", { locale: fr })}
</time>
<b>{name}</b> vous a envoyé une nouvelle demade à votre échange{" "}
<i>{title}</i>
</div>
</div>
</Link>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//

import { card } from "@1.ui/react/card/atom";
import { format } from "date-fns";
import { fr } from "date-fns/locale";
import Link from "next/link";
import type { Notification } from "./type";

//

export function InboxNewMessage({
notification,
}: {
notification: Notification;
}) {
const { base, body } = card();
const { id, created_at, read_at, inbox_message } = notification;

if (!inbox_message) return null;
if (!inbox_message.message) return null;

const {
message: {
author: { name },
thread_id,
},
} = inbox_message;
return (
<Link id={id} href={`/@~/inbox/${thread_id}`}>
<div
className={base({ className: read_at ? "bg-transparent" : "bg-white" })}
>
<div className={body()}>
<time
className="float-right text-xs text-gray-500"
dateTime={created_at.toUTCString()}
title={created_at.toUTCString()}
>
{format(created_at, "Pp", { locale: fr })}
</time>
<b>{name}</b> vous a envoyé un nouveau message
</div>
</div>
</Link>
);
}
Loading

0 comments on commit 51f1d3c

Please sign in to comment.