Jak zbudować czat między kupującymi a sprzedającymi dla marketplace w Next.js

Czat między kupującymi a sprzedającymi wygląda jak jedna funkcja, a w marketplace zbudowanym w Next.js to kilka funkcji naraz. Osoba kupująca pyta o przedmiot i dostaje odpowiedź, gdy jeszcze ma go przed oczami. Rozmowa należy tylko do tych dwóch osób, ze zdjęciami, których nikt inny nie otworzy. Czasem zdjęcie nie wystarcza, a krótka rozmowa wideo pokazuje przedmiot, zanim ktokolwiek wyruszy w drogę. Oferta musi dotrzeć do osoby sprzedającej na każdej stronie aplikacji, a odpowiedź musi wrócić tą samą drogą. A wszystkie osoby oglądające przedmiot powinny widzieć jego cenę, i to, czy jest wciąż na sprzedaż, gdy tylko coś się zmieni.
Ten artykuł omawia każdą część, której potrzebuje prawdziwa aplikacja, na pełnym, działającym przykładzie: marketplace z naszego przewodnika po Next.js, w którym sąsiedzi kupują i sprzedają swoje rzeczy. Możesz wypróbować go na żywo: bądź Omarem, z gramofonem na sprzedaż, a potem otwórz stronę w oknie prywatnym jako Ava, z ofertą 130 $, która czeka na odpowiedź. Zmień ofertę jako Ava, a Omar widzi to od razu, na każdej stronie.
Czego potrzebuje czat w marketplace
- Jedna rozmowa na przedmiot i osobę kupującą. Każda osoba, która pyta o przedmiot, dostaje własną rozmowę z osobą sprzedającą, i nikt inny jej nie czyta, ani inni kupujący, ani inni sprzedający.
- Wiadomości, które przychodzą, gdy patrzysz. Wiadomość przechodzi ze strony na stronę od razu po wysłaniu, ze wskaźnikiem pisania i potwierdzeniami odczytu, i zapisuje się w Twojej własnej bazie danych. Połączenia, których to wymaga, pozostają otwarte tak długo jak strona, a funkcja serverless nie może ich utrzymać (dlaczego i co z tym zrobić).
- Zdjęcia przechowywane u Ciebie. Osoba kupująca chce coś zobaczyć z bliska, a osoba sprzedająca wysyła zdjęcie. Zdjęcia zostają na Twoim serwerze, i tylko dwie osoby z rozmowy mogą je otworzyć.
- Rozmowa wideo, żeby zobaczyć przedmiot. Z czatu, zanim ktokolwiek przyjedzie.
- Oferty, których żadna strona nie podrobi. Oferta, i tak albo nie osoby sprzedającej, pochodzą z Twojego serwera, nigdy ze strony, i obie strony widzą je od razu.
- Marketplace taki, jaki jest teraz. Ile innych osób ogląda przedmiot, jego nowa cena, i czy jest zarezerwowany lub sprzedany, na każdej stronie, która go pokazuje.
Możesz zbudować to wszystko samodzielnie, na własnych websocketach i własnym WebRTC. Albo pozwolić chmurze czasu rzeczywistego przenosić wiadomości, rozmowy i liczniki, podczas gdy Twój serwer zachowuje to, co naprawdę Twoje: kto może rozmawiać z kim, rozmowy i oferty. Tak właśnie działa marketplace, przez WordPlus Cloud.
Przykład: marketplace
Sąsiedzi wystawiają swoje rzeczy ze zdjęciami i ceną. Osoba, która chce kupić, otwiera przedmiot, klika Message Omar, i otwiera się rozmowa o nim, z przedmiotem i ofertą nad czatem. To aplikacja Next.js z App Routerem, Better Auth i Postgresem, a jej część czasu rzeczywistego to kilka plików. Pięć kroków tworzy jej rozmowy.
1. Jedna rozmowa na przedmiot i osobę kupującą
Każda rozmowa to pokój nazwany swoim numerem w bazie danych, deal:7, i obejmuje jeden przedmiot, osobę sprzedającą i jedną osobę kupującą. Strona deklaruje te pokoje raz, według tego wzorca, z tym, co strona może w nich wysyłać, i z regułą:
realtime.room(
'deal:{id}',
{ history: 50, emit: ['message', 'typing', 'reaction', 'read'], webhook: ['message', 'reaction'] },
async (user, { id }) => mayTalk(user, id),
);export async function mayTalk(user: string | number | null, deal: string): Promise<boolean> {
return user !== null && (await partiesOf(deal)).includes(String(user));
}mayTalk pyta bazę danych, kim są dwie osoby rozmowy, i nie wpuszcza nikogo innego. Strona prosi o wejście przez własną trasę serwisu, /api/realtime/auth, która uruchamia regułę z własną sesją serwisu i odpowiada przepustką podpisaną sekretem aplikacji albo powodem odmowy. Wiadomości, wskaźniki pisania, reakcje i potwierdzenia odczytu przechodzą potem ze strony na stronę przez WordPlus Cloud, a webhook wysyła każdą wiadomość i reakcję także do serwisu, który zapisuje je w Postgresie:
realtime.on('message', async ({ room, from, data, event_id, at }) => {
const deal = idIn(room, 'deal');
if (!from || !deal) return;
await keepAndTell({ id: event_id, deal, from: String(from), text: String(data.text ?? ''), files: data.files as unknown[], at });
});keepAndTell zapisuje wiadomość tylko raz, według identyfikatora jej zdarzenia, i powiadamia drugą osobę z rozmowy przez realtime.toUser(), które dociera do każdej strony otwartej przez tę osobę: powiadomienie, które otwiera rozmowę.
2. Czat na stronie, ze zdjęciami u Ciebie
Rozmowa to gotowy czat, ten z Better Messages, w Twoim własnym drzewie:
<Chat
rt={useRealtime()}
room={room}
to={to}
name={name}
avatar={avatar}
call
messages={`/api/messages?room=${room}`}
upload="/api/files"
accept="image/jpeg,image/png,image/webp,image/gif"
maxFileSize={15 * 1024 * 1024}
/>Pokazuje wiadomości zapisane przez Twój serwer i dociąga starsze przy przewijaniu w górę (messages), ze wskaźnikiem pisania, reakcjami, potwierdzeniami odczytu i polem do pisania. to, name i avatar umieszczają drugą osobę na jego górnym pasku. upload przyjmuje zdjęcia: każde trafia na trasę serwisu zaraz po wybraniu, a wiadomość niesie tylko miejsce, w którym się znajduje, więc WordPlus Cloud nigdy nie widzi samego zdjęcia. Trasa pyta o tę samą regułę co pokój:
if (!me || !deal || !(file instanceof File) || !(await realtime.mayJoin(room, me))) {
return Response.json({ error: 'This photo is not for this conversation.' }, { status: 403 });
}realtime.mayJoin uruchamia regułę deal:{id} dla zalogowanej osoby, więc rozmowa i jej zdjęcia nigdy nie różnią się co do tego, kto może z nich korzystać. Trasa, która podaje zdjęcie, znowu pyta mayTalk, a każdy inny dostaje 404, nawet z adresem zdjęcia.
3. Rozmowa wideo, żeby zobaczyć przedmiot
realtime.calls('deal:{id}', async (user, other, { id }) => (await mayTalk(user, id)) && (await mayTalk(other, id)));Rozmowa wideo ma nazwę czatu, i dwie osoby z czatu mogą do siebie w niej dzwonić. call na czacie umieszcza przyciski audio i wideo na jego górnym pasku i pokazuje rozmowę w samym czacie, obok wiadomości, więc osoba kupująca może zapytać o rysę, gdy osoba sprzedająca obraca gramofon do kamery. Na innej stronie serwisu rozmowa trwa dalej w okienku w rogu. Rozmowa między dwiema osobami idzie bezpośrednio między ich przeglądarkami, gdy to możliwe, przez przekaźnik, gdy sieć to blokuje, i jest szyfrowana end-to-end na każdej drodze (jak wdrożyć rozmowy wideo w Next.js).
4. Oferty, które wypowiada Twój serwer
Oferta to obietnica pieniędzy, więc żadna strona nie może jej wysłać. emit pokoju pomija oferty, i tylko server actions serwisu tworzą ofertę, zapisują ją i powiadamiają obie osoby:
export async function offer(deal: string, amount: number): Promise<{ error: string } | null> {
const me = await currentUserId();
if (!me) return { error: 'Sign in first.' };
const made = await makeOffer(deal, me, amount);
if (typeof made === 'string') return { error: made };
const found = (await dealOf(deal))!;
await realtime.to(`deal:${deal}`).emit('changed');
await realtime.toUser(found.listing.seller).emit('offer', { deal, name: found.buyerName, amount, title: found.listing.title });
return null;
}realtime.to() mówi changed w pokoju rozmowy, a obie jej strony znowu pytają serwer o swoją zawartość, bez przeładowania. realtime.toUser() dociera do osoby sprzedającej na każdej stronie, jako powiadomienie: „Ava offers $135 for Wooden turntable, works perfectly”. Accept albo Decline wraca tą samą drogą do osoby kupującej. Gdy oferta zostaje przyjęta, przedmiot jest zarezerwowany w tej cenie dla osoby, która ją złożyła, pozostałe otwarte oferty na niego są odrzucane, a każda osoba, która je złożyła, też się o tym dowiaduje:
for (const other of answered.declined) {
await realtime.toUser(other.buyer).emit('answer', {
deal: other.deal, name: seller, accepted: false, amount: other.amount, title: answered.listing.title,
});
}5. Kto ogląda, i marketplace taki, jaki jest
Strona przedmiotu wchodzi do własnego pokoju, listing:7, publicznego i liczonego przez WordPlus Cloud, bez żadnej Twojej tabeli:
realtime.room('listing:{id}', { public: true, count: true });const { count } = useRoom(`listing:${id}`, { changed: () => router.refresh() });
const others = count === null ? 0 : count - 1;Strona pokazuje „2 other people are looking at this”, a liczba zmienia się, gdy ludzie przychodzą i odchodzą. Ten sam pokój słyszy changed, gdy cena spada albo przedmiot zostaje zarezerwowany lub sprzedany, i strona pokazuje to od razu. Każda strona zalogowanej osoby wchodzi do jeszcze jednego pokoju, market, w którym mówi tylko serwer, więc sam marketplace pokazuje nowy przedmiot, nową cenę albo sprzedany przedmiot, bez przeładowania.
Co dostajesz w zestawie
- Wskaźnik pisania i potwierdzenia odczytu, w obie strony, jak w komunikatorze.
- Skrzynka odbiorcza: każda rozmowa, przy kupowaniu i sprzedawaniu, najnowsza na górze, i powiadomienie o nowej wiadomości na każdej stronie.
- Czy osoba sprzedająca jest online, albo kiedy była ostatnio, na stronie przedmiotu, z
useOnline(). - Imiona i twarze z Twojej aplikacji. Czat i rozmowa pokazują każdą osobę z imieniem i zdjęciem, które podaje Twój serwis, bez żadnej listy użytkowników w WordPlus Cloud.
- Zapieczętowane w drodze. To, co przechodzi przez pokój prywatny, przechodzi przez WordPlus Cloud zapieczętowane kluczem, który wyprowadza Twoja aplikacja, a serwery WordPlus Cloud nigdy nie mają tego klucza.
Wypróbuj, a potem zbuduj własny
W demie na żywo sąsiedzi i ich rzeczy należą tylko do Ciebie przez jeden dzień, więc tego, co tam napiszesz albo zaproponujesz, nikt inny nie widzi. Pod każdą stroną How this page works pokazuje kod, który ją tworzy, krok po kroku, z wartościami strony, którą masz otwartą.
Przewodnik po marketplace buduje cały serwis, z każdym plikiem jego części czasu rzeczywistego, a o spotkaniach wielu osób w tym samym rodzaju aplikacji przeczytasz w artykule jak wdrożyć wideokonferencje w Next.js.
WordPlus Cloud for Apps ma cenę według jednoczesnych połączeń, od 1000 wzwyż, rozliczaną miesięcznie lub rocznie. Wiadomości, oferty, licznik oglądających i rozmowy między dwiema osobami nie zużywają kredytów. Poznaj WordPlus Cloud dla aplikacji albo cennik dla aplikacji. Chcesz zapytać o swoją integrację? Napisz do nas.