Merge pull request #34 from atridadl/dev
1.2.7 ✨ Shiny new stats on the front page 🚧 Small cleanup of react FC references...
This commit is contained in:
commit
ad0be32e52
15 changed files with 163 additions and 95 deletions
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sprintpadawan",
|
"name": "sprintpadawan",
|
||||||
"version": "1.2.6",
|
"version": "1.2.7",
|
||||||
"description": "Plan. Sprint. Repeat.",
|
"description": "Plan. Sprint. Repeat.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { GiTechnoHeart } from "react-icons/gi";
|
import { GiTechnoHeart } from "react-icons/gi";
|
||||||
import packagejson from "../../package.json";
|
import packagejson from "../../package.json";
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
const Footer = () => {
|
||||||
return (
|
return (
|
||||||
<footer className="footer footer-center h-12 p-2 bg-base-100 text-base-content">
|
<footer className="footer footer-center h-12 p-2 bg-base-100 text-base-content">
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Made with{ " " }
|
Made with{" "}
|
||||||
<GiTechnoHeart className="inline-block text-primary text-lg animate-pulse" />{ " " }
|
<GiTechnoHeart className="inline-block text-primary text-lg animate-pulse" />{" "}
|
||||||
by{ " " }
|
by{" "}
|
||||||
<a
|
<a
|
||||||
className="link link-primary link-hover"
|
className="link link-primary link-hover"
|
||||||
href="https://atri.dad"
|
href="https://atri.dad"
|
||||||
|
@ -16,15 +16,15 @@ const Footer: React.FC = () => {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Atridad Lahiji
|
Atridad Lahiji
|
||||||
</a>{ " " }
|
</a>{" "}
|
||||||
-{ " " }
|
-{" "}
|
||||||
<a
|
<a
|
||||||
className="link link-primary link-hover"
|
className="link link-primary link-hover"
|
||||||
href={ `https://github.com/atridadl/sprintpadawan/releases/tag/${packagejson.version}` }
|
href={`https://github.com/atridadl/sprintpadawan/releases/tag/${packagejson.version}`}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
v{ packagejson.version }
|
v{packagejson.version}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface NavbarProps {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Navbar: React.FC<NavbarProps> = ({ title }) => {
|
const Navbar = ({ title }: NavbarProps) => {
|
||||||
const { data: sessionData, status: sessionStatus } = useSession();
|
const { data: sessionData, status: sessionStatus } = useSession();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { IoEnterOutline, IoTrashBinOutline } from "react-icons/io5";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
const RoomList: React.FC = () => {
|
const RoomList = () => {
|
||||||
const { data: sessionData } = useSession();
|
const { data: sessionData } = useSession();
|
||||||
|
|
||||||
configureAbly({
|
configureAbly({
|
||||||
|
@ -50,7 +50,7 @@ const RoomList: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center gap-8">
|
<div className="flex flex-col items-center justify-center gap-8">
|
||||||
{/* Modal for Adding Rooms */ }
|
{/* Modal for Adding Rooms */}
|
||||||
<input type="checkbox" id="new-room-modal" className="modal-toggle" />
|
<input type="checkbox" id="new-room-modal" className="modal-toggle" />
|
||||||
<div className="modal modal-bottom sm:modal-middle">
|
<div className="modal modal-bottom sm:modal-middle">
|
||||||
<div className="modal-box flex-col flex text-center justify-center items-center">
|
<div className="modal-box flex-col flex text-center justify-center items-center">
|
||||||
|
@ -72,30 +72,30 @@ const RoomList: React.FC = () => {
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Type here"
|
placeholder="Type here"
|
||||||
className="input input-bordered w-full max-w-xs"
|
className="input input-bordered w-full max-w-xs"
|
||||||
onChange={ (event) => {
|
onChange={(event) => {
|
||||||
setRoomName(event.target.value);
|
setRoomName(event.target.value);
|
||||||
} }
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
{ roomName.length > 0 && (
|
{roomName.length > 0 && (
|
||||||
<label
|
<label
|
||||||
htmlFor="new-room-modal"
|
htmlFor="new-room-modal"
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={ () => createRoomHandler() }
|
onClick={() => createRoomHandler()}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</label>
|
</label>
|
||||||
) }
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ roomsFromDb && roomsFromDb.length > 0 && (
|
{roomsFromDb && roomsFromDb.length > 0 && (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="table text-center">
|
<table className="table text-center">
|
||||||
{/* head */ }
|
{/* head */}
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-white">
|
<tr className="border-white">
|
||||||
<th>Room Name</th>
|
<th>Room Name</th>
|
||||||
|
@ -103,43 +103,43 @@ const RoomList: React.FC = () => {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="">
|
<tbody className="">
|
||||||
{ roomsFromDb?.map((room) => {
|
{roomsFromDb?.map((room) => {
|
||||||
return (
|
return (
|
||||||
<tr key={ room.id } className="hover border-white">
|
<tr key={room.id} className="hover border-white">
|
||||||
<td className="break-all max-w-[200px] md:max-w-[400px]">
|
<td className="break-all max-w-[200px] md:max-w-[400px]">
|
||||||
{ room.roomName }
|
{room.roomName}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Link
|
<Link
|
||||||
className="m-2 no-underline"
|
className="m-2 no-underline"
|
||||||
href={ `/room/${room.id}` }
|
href={`/room/${room.id}`}
|
||||||
>
|
>
|
||||||
<IoEnterOutline className="text-xl inline-block hover:text-secondary" />
|
<IoEnterOutline className="text-xl inline-block hover:text-secondary" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="m-2"
|
className="m-2"
|
||||||
onClick={ () => deleteRoomHandler(room.id) }
|
onClick={() => deleteRoomHandler(room.id)}
|
||||||
>
|
>
|
||||||
<IoTrashBinOutline className="text-xl inline-block hover:text-error" />
|
<IoTrashBinOutline className="text-xl inline-block hover:text-error" />
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}) }
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
) }
|
)}
|
||||||
<label htmlFor="new-room-modal" className="btn btn-secondary">
|
<label htmlFor="new-room-modal" className="btn btn-secondary">
|
||||||
New Room
|
New Room
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{ roomsFromDb === undefined && (
|
{roomsFromDb === undefined && (
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<span className="loading loading-dots loading-lg"></span>
|
<span className="loading loading-dots loading-lg"></span>
|
||||||
</div>
|
</div>
|
||||||
) }
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
83
src/components/Stats.tsx
Normal file
83
src/components/Stats.tsx
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import { configureAbly, useChannel } from "@ably-labs/react-hooks";
|
||||||
|
import { env } from "~/env.mjs";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
|
const Stats = () => {
|
||||||
|
configureAbly({
|
||||||
|
key: env.NEXT_PUBLIC_ABLY_PUBLIC_KEY,
|
||||||
|
recover: (_, cb) => {
|
||||||
|
cb(true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [] = useChannel(
|
||||||
|
`${env.NEXT_PUBLIC_APP_ENV}-stats`,
|
||||||
|
() => void refetchData()
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: usersCount,
|
||||||
|
isLoading: usersCountLoading,
|
||||||
|
isFetching: usersCountFetching,
|
||||||
|
refetch: refetchUsersCount,
|
||||||
|
} = api.rest.userCount.useQuery();
|
||||||
|
const {
|
||||||
|
data: roomsCount,
|
||||||
|
isLoading: roomsCountLoading,
|
||||||
|
isFetching: roomsCountFetching,
|
||||||
|
refetch: refetchRoomsCount,
|
||||||
|
} = api.rest.roomCount.useQuery();
|
||||||
|
const {
|
||||||
|
data: votesCount,
|
||||||
|
isLoading: votesCountLoading,
|
||||||
|
isFetching: votesCountFetching,
|
||||||
|
refetch: refetchVotesCount,
|
||||||
|
} = api.rest.voteCount.useQuery();
|
||||||
|
|
||||||
|
const refetchData = async () => {
|
||||||
|
await Promise.all([
|
||||||
|
refetchUsersCount(),
|
||||||
|
refetchRoomsCount(),
|
||||||
|
refetchVotesCount(),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="stats stats-horizontal shadow bg-neutral m-4">
|
||||||
|
<div className="stat">
|
||||||
|
<div className="stat-title">Users</div>
|
||||||
|
<div className="stat-value">
|
||||||
|
{usersCountLoading || usersCountFetching ? (
|
||||||
|
<span className="loading loading-infinity loading-lg"></span>
|
||||||
|
) : (
|
||||||
|
<>{usersCount ? usersCount : "0"}</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="stat">
|
||||||
|
<div className="stat-title">Rooms</div>
|
||||||
|
<div className="stat-value">
|
||||||
|
{roomsCountLoading || roomsCountFetching ? (
|
||||||
|
<span className="loading loading-infinity loading-lg"></span>
|
||||||
|
) : (
|
||||||
|
<>{roomsCount ? roomsCount : "0"}</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="stat">
|
||||||
|
<div className="stat-title">Votes</div>
|
||||||
|
<div className="stat-value">
|
||||||
|
{votesCountLoading || votesCountFetching ? (
|
||||||
|
<span className="loading loading-infinity loading-lg"></span>
|
||||||
|
) : (
|
||||||
|
<>{votesCount ? votesCount : "0"}</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Stats;
|
|
@ -8,6 +8,7 @@ import { SiGithub, SiGoogle } from "react-icons/si";
|
||||||
import { GiStarFormation } from "react-icons/gi";
|
import { GiStarFormation } from "react-icons/gi";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { getServerAuthSession } from "../../server/auth";
|
import { getServerAuthSession } from "../../server/auth";
|
||||||
|
import Stats from "~/components/Stats";
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
const session = await getServerAuthSession(ctx);
|
const session = await getServerAuthSession(ctx);
|
||||||
|
@ -55,31 +56,13 @@ const Admin: NextPage = () => {
|
||||||
|
|
||||||
export default Admin;
|
export default Admin;
|
||||||
|
|
||||||
const AdminBody: React.FC = () => {
|
const AdminBody = () => {
|
||||||
const {
|
|
||||||
data: usersCount,
|
|
||||||
isLoading: usersCountLoading,
|
|
||||||
isFetching: usersCountFetching,
|
|
||||||
refetch: refetchUsersCount,
|
|
||||||
} = api.rest.userCount.useQuery();
|
|
||||||
const {
|
const {
|
||||||
data: users,
|
data: users,
|
||||||
isLoading: usersLoading,
|
isLoading: usersLoading,
|
||||||
isFetching: usersFetching,
|
isFetching: usersFetching,
|
||||||
refetch: refetchUsers,
|
refetch: refetchUsers,
|
||||||
} = api.user.getAll.useQuery();
|
} = api.user.getAll.useQuery();
|
||||||
const {
|
|
||||||
data: roomsCount,
|
|
||||||
isLoading: roomsCountLoading,
|
|
||||||
isFetching: roomsCountFetching,
|
|
||||||
refetch: refetchRoomsCount,
|
|
||||||
} = api.rest.roomCount.useQuery();
|
|
||||||
const {
|
|
||||||
data: votesCount,
|
|
||||||
isLoading: votesCountLoading,
|
|
||||||
isFetching: votesCountFetching,
|
|
||||||
refetch: refetchVotesCount,
|
|
||||||
} = api.rest.voteCount.useQuery();
|
|
||||||
|
|
||||||
const getProviders = (user: {
|
const getProviders = (user: {
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
@ -153,57 +136,16 @@ const AdminBody: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const refetchData = async () => {
|
const refetchData = async () => {
|
||||||
await Promise.all([
|
await Promise.all([refetchUsers()]);
|
||||||
refetchUsers(),
|
|
||||||
refetchUsersCount(),
|
|
||||||
refetchRoomsCount(),
|
|
||||||
refetchVotesCount(),
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-4xl font-bold">Admin Panel</h1>
|
<h1 className="text-4xl font-bold">Admin Panel</h1>
|
||||||
|
|
||||||
<div className="stats stats-horizontal shadow bg-neutral m-4">
|
<Stats />
|
||||||
<div className="stat">
|
|
||||||
<div className="stat-title">Users</div>
|
|
||||||
<div className="stat-value">
|
|
||||||
{usersCountLoading || usersCountFetching ? (
|
|
||||||
<span className="loading loading-dots loading-lg"></span>
|
|
||||||
) : (
|
|
||||||
<>{usersCount ? usersCount : "0"}</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="stat">
|
{usersFetching ? (
|
||||||
<div className="stat-title">Rooms</div>
|
|
||||||
<div className="stat-value">
|
|
||||||
{roomsCountLoading || roomsCountFetching ? (
|
|
||||||
<span className="loading loading-dots loading-lg"></span>
|
|
||||||
) : (
|
|
||||||
<>{roomsCount ? roomsCount : "0"}</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="stat">
|
|
||||||
<div className="stat-title">Votes</div>
|
|
||||||
<div className="stat-value">
|
|
||||||
{votesCountLoading || votesCountFetching ? (
|
|
||||||
<span className="loading loading-dots loading-lg"></span>
|
|
||||||
) : (
|
|
||||||
<>{votesCount ? votesCount : "0"}</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{usersCountFetching ||
|
|
||||||
usersFetching ||
|
|
||||||
roomsCountFetching ||
|
|
||||||
votesCountFetching ? (
|
|
||||||
<span className="loading loading-dots loading-lg"></span>
|
<span className="loading loading-dots loading-lg"></span>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-row flex-wrap text-center items-center justify-center gap-2">
|
<div className="flex flex-row flex-wrap text-center items-center justify-center gap-2">
|
||||||
|
|
|
@ -45,7 +45,7 @@ const Home: NextPage = () => {
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|
||||||
const HomePageBody: React.FC = () => {
|
const HomePageBody = () => {
|
||||||
const { data: sessionData } = useSession();
|
const { data: sessionData } = useSession();
|
||||||
const [joinRoomTextBox, setJoinRoomTextBox] = useState<string>("");
|
const [joinRoomTextBox, setJoinRoomTextBox] = useState<string>("");
|
||||||
const [tabIndex, setTabIndex] = useState<number>();
|
const [tabIndex, setTabIndex] = useState<number>();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { type NextPage } from "next";
|
import { type NextPage } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import Stats from "~/components/Stats";
|
||||||
|
|
||||||
const Home: NextPage = () => {
|
const Home: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -17,7 +18,7 @@ const Home: NextPage = () => {
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|
||||||
const HomePageBody: React.FC = () => {
|
const HomePageBody = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-3xl sm:text-6xl font-bold">
|
<h1 className="text-3xl sm:text-6xl font-bold">
|
||||||
|
@ -54,6 +55,13 @@ const HomePageBody: React.FC = () => {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="card card-compact bg-secondary text-black font-bold text-left">
|
||||||
|
<div className="card-body">
|
||||||
|
<h2 className="card-title">Stats:</h2>
|
||||||
|
<Stats />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,7 +48,7 @@ const Profile: NextPage = () => {
|
||||||
|
|
||||||
export default Profile;
|
export default Profile;
|
||||||
|
|
||||||
const ProfileBody: React.FC = () => {
|
const ProfileBody = () => {
|
||||||
const { data: sessionData } = useSession();
|
const { data: sessionData } = useSession();
|
||||||
const [nameText, setNameText] = useState<string>("");
|
const [nameText, setNameText] = useState<string>("");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
|
@ -65,7 +65,7 @@ const Room: NextPage = () => {
|
||||||
|
|
||||||
export default Room;
|
export default Room;
|
||||||
|
|
||||||
const RoomBody: React.FC = ({}) => {
|
const RoomBody = ({}) => {
|
||||||
const { data: sessionData } = useSession();
|
const { data: sessionData } = useSession();
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
const roomId = z.string().parse(query.id);
|
const roomId = z.string().parse(query.id);
|
||||||
|
|
|
@ -33,6 +33,12 @@ export const roomRouter = createTRPCRouter({
|
||||||
EventTypes.ROOM_LIST_UPDATE,
|
EventTypes.ROOM_LIST_UPDATE,
|
||||||
JSON.stringify(room)
|
JSON.stringify(room)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await publishToChannel(
|
||||||
|
`stats`,
|
||||||
|
EventTypes.STATS_UPDATE,
|
||||||
|
JSON.stringify(room)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// happy path
|
// happy path
|
||||||
return !!room;
|
return !!room;
|
||||||
|
@ -224,6 +230,12 @@ export const roomRouter = createTRPCRouter({
|
||||||
EventTypes.ROOM_UPDATE,
|
EventTypes.ROOM_UPDATE,
|
||||||
JSON.stringify(deletedRoom)
|
JSON.stringify(deletedRoom)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await publishToChannel(
|
||||||
|
`stats`,
|
||||||
|
EventTypes.STATS_UPDATE,
|
||||||
|
JSON.stringify(deletedRoom)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!deletedRoom;
|
return !!deletedRoom;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Resend } from "resend";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { Goodbye } from "~/components/templates/Goodbye";
|
import { Goodbye } from "~/components/templates/Goodbye";
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
|
import { publishToChannel } from "~/server/ably";
|
||||||
import {
|
import {
|
||||||
adminProcedure,
|
adminProcedure,
|
||||||
createTRPCRouter,
|
createTRPCRouter,
|
||||||
|
@ -10,6 +11,7 @@ import {
|
||||||
} from "~/server/api/trpc";
|
} from "~/server/api/trpc";
|
||||||
|
|
||||||
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
||||||
|
import { EventTypes } from "~/utils/types";
|
||||||
|
|
||||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||||
|
|
||||||
|
@ -118,6 +120,12 @@ export const userRouter = createTRPCRouter({
|
||||||
|
|
||||||
await invalidateCache(`kv_usercount_admin`);
|
await invalidateCache(`kv_usercount_admin`);
|
||||||
await invalidateCache(`kv_userlist_admin`);
|
await invalidateCache(`kv_userlist_admin`);
|
||||||
|
|
||||||
|
await publishToChannel(
|
||||||
|
`stats`,
|
||||||
|
EventTypes.STATS_UPDATE,
|
||||||
|
JSON.stringify(user)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!user;
|
return !!user;
|
||||||
|
|
|
@ -93,6 +93,12 @@ export const voteRouter = createTRPCRouter({
|
||||||
EventTypes.VOTE_UPDATE,
|
EventTypes.VOTE_UPDATE,
|
||||||
input.value
|
input.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await publishToChannel(
|
||||||
|
`stats`,
|
||||||
|
EventTypes.STATS_UPDATE,
|
||||||
|
JSON.stringify(vote)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!vote;
|
return !!vote;
|
||||||
|
|
|
@ -12,6 +12,8 @@ import { env } from "~/env.mjs";
|
||||||
import { prisma } from "~/server/db";
|
import { prisma } from "~/server/db";
|
||||||
import { Welcome } from "../components/templates/Welcome";
|
import { Welcome } from "../components/templates/Welcome";
|
||||||
import { invalidateCache } from "./redis";
|
import { invalidateCache } from "./redis";
|
||||||
|
import { publishToChannel } from "./ably";
|
||||||
|
import { EventTypes } from "~/utils/types";
|
||||||
|
|
||||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||||
|
|
||||||
|
@ -65,6 +67,12 @@ export const authOptions: NextAuthOptions = {
|
||||||
});
|
});
|
||||||
await invalidateCache(`kv_userlist_admin`);
|
await invalidateCache(`kv_userlist_admin`);
|
||||||
await invalidateCache(`kv_usercount_admin`);
|
await invalidateCache(`kv_usercount_admin`);
|
||||||
|
|
||||||
|
await publishToChannel(
|
||||||
|
`stats`,
|
||||||
|
EventTypes.STATS_UPDATE,
|
||||||
|
JSON.stringify(user)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async signIn({}) {
|
async signIn({}) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ export const EventTypes = {
|
||||||
ROOM_LIST_UPDATE: "room.list.update",
|
ROOM_LIST_UPDATE: "room.list.update",
|
||||||
ROOM_UPDATE: "room.update",
|
ROOM_UPDATE: "room.update",
|
||||||
VOTE_UPDATE: "vote.update",
|
VOTE_UPDATE: "vote.update",
|
||||||
|
STATS_UPDATE: "stats.update",
|
||||||
} as const;
|
} as const;
|
||||||
export type EventType = BetterEnum<typeof EventTypes>;
|
export type EventType = BetterEnum<typeof EventTypes>;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue