Skip to content
Open
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
4 changes: 3 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ NEXT_PUBLIC_FIREBASE_PROJECT_ID=ensemble-square-dev
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=ensemble-square-dev.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="135380920875"
NEXT_PUBLIC_FIREBASE_APP_ID="1:135380920875:web:300170c3c9a3f100ce186e"
NEXT_PUBLIC_CAPTCHA_SITE_KEY=6LePMaoqAAAAALq5rY-tApn0j_7CJL0HDjMMuqMC
CAPTCHA_SECRET_KEY=6LePMaoqAAAAALD9YTugizQkfu0eoFXf5ynd1hhi
COOKIE_SECRET_CURRENT=50cee4fa6b5406f5636d605659c44b79c2c6c7d452f20292ae4d30b6f0e30b96
COOKIE_SECRET_PREVIOUS=61666d418e8407274bc29a6c01a1dc13e083d71f60774e892a14b5bbc0635e5c
COOKIE_SECRET_PREVIOUS=61666d418e8407274bc29a6c01a1dc13e083d71f60774e892a14b5bbc0635e5c
2 changes: 1 addition & 1 deletion components/Homepage/CurrentEventCountdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ function CurrentEventCountdowns({ events }: { events: Event[] }) {
</Text>
</Group>
{shownEvents.map((event) => (
<CurrentEventCountdown shownEvent={event} />
<CurrentEventCountdown key={event.event_id} shownEvent={event} />
))}
</>
);
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@tanstack/react-query": "^5.56.2",
"@types/seedrandom": "^3.0.5",
"@vercel/analytics": "^0.1.6",
"axios": "^1.7.9",
"cookies-next": "^2.1.1",
"country-flag-icons": "^1.5.5",
"dayjs": "^1.11.7",
Expand Down Expand Up @@ -58,14 +59,16 @@
"react-firebase-hooks": "^5.1.1",
"react-frame-component": "^5.2.3",
"react-gesture-responder": "^2.1.0",
"react-google-recaptcha": "^3.1.0",
"react-grid-drag": "^1.0.0",
"react-image-crop": "^10.0.9",
"react-infinite-scroll-component": "^6.1.0",
"react-scroll-parallax": "^3.4.5",
"seedrandom": "^3.0.5",
"strapi-sdk-js": "^2.2.0",
"swr": "^2.0.0",
"use-debounce": "^9.0.2"
"use-debounce": "^9.0.2",
"zod": "^3.24.1"
},
"scripts": {
"dev": "next dev",
Expand Down Expand Up @@ -98,6 +101,7 @@
"@types/react-beautiful-dnd": "^13.1.3",
"@types/react-collapse": "^5.0.1",
"@types/react-dom": "^18.0.9",
"@types/react-google-recaptcha": "^2.1.9",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.29.0",
"eslint-config-next": "^13.0.7",
Expand Down
3 changes: 1 addition & 2 deletions pages/[user]/components/ProfileButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
Text,
useMantineTheme,
} from "@mantine/core";
import { showNotification, updateNotification } from "@mantine/notifications";
import {
IconPencil,
IconCopy,
Expand Down Expand Up @@ -114,7 +113,7 @@ function ProfileButtons({
</CopyButton>
{!isOwnProfile && (
<>
{!user && !isFriend && !isOutgoingReq && !isIncomingReq && (
{user && !isFriend && !isOutgoingReq && !isIncomingReq && (
<Tooltip label={t("sendFriendReq")}>
<ActionIcon
onClick={() => sendFriendReq()}
Expand Down
25 changes: 25 additions & 0 deletions pages/api/email/validate.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
collection,
getDocs,
getFirestore,
query,
where,
} from "firebase/firestore";
import { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const email = req.body.email;
const db = getFirestore();
const q = query(collection(db, "users"), where("email", "==", email));
const querySnap = await getDocs(q);
const emailValid = !!querySnap.size;

if (emailValid) {
res.status(200).send({ valid: true });
} else {
res.status(404).send({ valid: false });
}
}
3 changes: 2 additions & 1 deletion pages/api/login.page.ts → pages/api/login/index.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
import { FieldValue } from "firebase-admin/firestore";
import { NextApiRequest, NextApiResponse } from "next";

import { migrateCollection } from "./collections/migrate.page";
import { migrateCollection } from "../collections/migrate.page";

import { initAuthentication } from "services/firebase/authentication";
import { getAuth } from "firebase/auth";

try {
initAuthentication();
Expand Down
85 changes: 85 additions & 0 deletions pages/api/login/token.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
collection,
Firestore,
getDocs,
getFirestore,
query,
where,
} from "firebase/firestore";
import { NextApiRequest, NextApiResponse } from "next";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import { getFirebaseAdmin } from "next-firebase-auth";

enum Errors {
NOT_FOUND = "Could not find user with this username",
INCOMPLETE = "Missing credentials",
UNAUTHED = "Invalid credentials",
}

async function validateUsernameDb(
username: string | undefined
): Promise<boolean | undefined> {
try {
const db = getFirestore();
const q = query(
collection(db as unknown as Firestore, "users"),
where("username", "==", username)
);
const querySnap = await getDocs(q);
const usernameValid = !querySnap.empty;
return usernameValid;
} catch {
return false;
}
}

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const auth = getAuth();
const admin = getFirebaseAdmin();
const username = req.body.username;
const password = req.body.password;
if (!username || !password) {
throw new TypeError(Errors.INCOMPLETE);
}
const isUsernameValid = await validateUsernameDb(username);
if (!isUsernameValid) throw new TypeError(Errors.NOT_FOUND);
const db = getFirestore();
const usernameQuery = query(
collection(db, "users"),
where("username", "==", username)
);
const querySnap = await getDocs(usernameQuery);
const userData = querySnap.docs[0];
const email = userData.data().email;
const emailUserData = await admin.auth().getUserByEmail(email);
if (!!!emailUserData) {
throw new TypeError(Errors.NOT_FOUND);
}
const uid = userData.id;
await signInWithEmailAndPassword(auth, email, password);
const customToken = await admin.auth().createCustomToken(uid);
res.status(200).send({ customToken });
} catch (error) {
if (error.message) {
switch (error.message) {
case Errors.NOT_FOUND:
res.status(404).send(error);
break;
case Errors.INCOMPLETE:
res.status(400).send(error);
break;
case Errors.UNAUTHED:
res.status(403).send(error);
break;
default:
res.status(500).send(error);
}
} else {
res.status(500).send(error);
}
}
}
52 changes: 52 additions & 0 deletions pages/api/username/email.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
collection,
getDocs,
getFirestore,
query,
where,
} from "firebase/firestore";
import { NextApiRequest, NextApiResponse } from "next";
import z from "zod";

export default async function handlers(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const username = req.body.username;
const shouldBeCensored = req.body.censored;
const db = getFirestore();
const q = query(collection(db, "users"), where("username", "==", username));
const querySnap = await getDocs(q);
if (!!!querySnap.size) {
res
.status(404)
.send({ message: "A user with this username does not exist." });
}

const user = querySnap.docs[0];
const email: string = user.data().email as string;

z.string().email().parse(email);

if (shouldBeCensored) {
const splitEmail = email.split("@");
const username = splitEmail[0];
const domainName = splitEmail[1];
const censoredEmailUsername: string = username
.split("")
.map((chara, index) => (index < 2 ? chara : "*"))
.join("");
const censoredDomainName: string = domainName
.split("")
.map((chara, index) => (index < 1 || chara === "." ? chara : "*"))
.join("");
const censoredEmail = `${censoredEmailUsername}@${censoredDomainName}`;
res.status(200).send({ email: censoredEmail });
} else {
res.status(200).send({ email });
}
} catch {
res.status(500).send({ message: "An unknown error occurred" });
}
}
24 changes: 24 additions & 0 deletions pages/api/username/validate.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
collection,
getDocs,
getFirestore,
query,
where,
} from "firebase/firestore";
import { NextApiRequest, NextApiResponse } from "next";

export default async function handlers(
req: NextApiRequest,
res: NextApiResponse
) {
const username = req.body.username;
const db = getFirestore();
const q = query(collection(db, "users"), where("username", "==", username));
const querySnap = await getDocs(q);
const usernameValid = !!!querySnap.size;
if (usernameValid) {
res.status(200).send({ valid: true });
} else {
res.status(400).send({ valid: false });
}
}
52 changes: 49 additions & 3 deletions pages/calendar/components/CalendarHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import {
ActionIcon,
Box,
Button,
createStyles,
Group,
MediaQuery,
Menu,
NativeSelect,
Stack,
Title,
} from "@mantine/core";
import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react";
import { Calendar } from "@mantine/dates";
import {
IconArrowLeft,
IconArrowRight,
IconSelector,
} from "@tabler/icons-react";
import useTranslation from "next-translate/useTranslation";
import { ReactElement, useCallback } from "react";
import { ReactElement, useCallback, useState } from "react";

import { useDayjs } from "services/libraries/dayjs";
import { Birthday, Event, Scout } from "types/game";

const useStyles = createStyles((theme, _params, getRef) => ({
header: {
Expand All @@ -30,10 +40,12 @@ const useStyles = createStyles((theme, _params, getRef) => ({
function CalendarHeader({
calendarTime,
setCalendarTime,
events,
children,
}: {
calendarTime: string;
setCalendarTime: (a: string) => void;
events: Array<Event | Scout | Birthday>;
children?: ReactElement;
}) {
const { t } = useTranslation("calendar");
Expand All @@ -47,6 +59,15 @@ function CalendarHeader({
[setCalendarTime, calendarTime, dayjs]
);

const sortedEvents = events
.filter((event) => !(event as Birthday).character_id)
.sort((a, b) => dayjs(a.end.en).diff(dayjs(b.end.en)));

const earliestEvent = sortedEvents[0];
const latestEvent = sortedEvents[sortedEvents.length - 1];

const [openMonths, setOpenMonths] = useState(false);

return (
<Box className={classes.header}>
<Button onClick={() => shiftCalendarTime(-1)} px="xs" variant="subtle">
Expand All @@ -66,7 +87,32 @@ function CalendarHeader({
<Title order={2} my={0} color="dimmed" size="lg">
{t("calendarHeader")}
</Title>
<Title order={3}>{dayjs(calendarTime).format("MMMM YYYY")}</Title>
<Group align="center">
<Title order={3}>{dayjs(calendarTime).format("MMMM YYYY")}</Title>{" "}
<Menu shadow="md" opened={openMonths} onChange={setOpenMonths}>
<Menu.Target>
<ActionIcon>
<IconSelector />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Calendar
firstDayOfWeek="sunday"
allowLevelChange={false}
initialLevel="month"
value={dayjs(calendarTime).startOf("month").toDate()}
onMonthChange={(date) => {
if (date) setCalendarTime(date?.toString());
setOpenMonths(false);
}}
minDate={dayjs(earliestEvent.start.en)
.startOf("month")
.toDate()}
maxDate={dayjs(latestEvent.end.en).endOf("month").toDate()}
/>
</Menu.Dropdown>
</Menu>
</Group>
</Stack>

{children}
Expand Down
12 changes: 6 additions & 6 deletions pages/calendar/index.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,11 @@ function Page({
</MediaQuery>
<Box className={classes.calendar}>
<CalendarHeader
calendarTime={calendarTime}
setCalendarTime={setCalendarTime}
{...{
calendarTime,
setCalendarTime,
events,
}}
>
<MediaQuery
smallerThan="sm"
Expand Down Expand Up @@ -159,10 +162,7 @@ function Page({
<CalendarListView events={events} calendarTime={calendarTime} />
)}
{view === "list" && (
<CalendarHeader
calendarTime={calendarTime}
setCalendarTime={setCalendarTime}
/>
<CalendarHeader {...{ calendarTime, setCalendarTime, events }} />
)}
</Box>
</>
Expand Down
Loading