Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popup Manager #28

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
69 changes: 69 additions & 0 deletions src/app/_components/_sharedcomponents/Popup/Popup.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export const buttonStyle = {
borderRadius: "15px",
backgroundColor: "#1E2D32",
padding: "1rem 1.5rem",

border: "2px solid transparent",
background:
"linear-gradient(#1E2D32, #1E2D32) padding-box, linear-gradient(to top, #038FC3, #595A5B) border-box",
"&:hover": {
background: "hsl(195, 25%, 16%)",
},
};

export const overlayStyle = {
position: "fixed" as const,
inset: "0",
backgroundColor: "rgba(0, 0, 0, 0.5)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 50,
};

export const concedeButtonStyle = {
padding: "1rem 1.5rem",
borderRadius: "15px",
backgroundImage: "linear-gradient(#1E2D32, #1E2D32)",
border: "2px solid transparent",
background:
"linear-gradient(#380707, #380707) padding-box, linear-gradient(to top, #C30101, #7D0807) border-box",
};

export const contentStyle = {
padding: "2rem",
borderRadius: "15px",
width: "90%",
maxWidth: "500px",
height: "260px",
position: "relative" as const,

border: "2px solid transparent",
background:
"linear-gradient(#0F1F27, #030C13) padding-box, linear-gradient(to top, #30434B, #50717D) border-box",
};

export const containerStyle = {
alignItems: "center",
justifyContent: "center",
display: "flex",
flexDirection: "column",
textAlign: "center",
};

export const minimalButtonStyle = {
position: "absolute",
top: "0.5rem",
right: "0.5rem",
color: "white",
};

export const titleStyle = {
color: "white",
fontSize: "1.25rem",
fontWeight: "bold",
};

export const textStyle = {
color: "#C7C7C7",
};
46 changes: 46 additions & 0 deletions src/app/_components/_sharedcomponents/Popup/Popup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client";
import { PopupData, PopupType, usePopup } from "@/app/_contexts/Popup.context";
import { Box, IconButton } from "@mui/material";
import React from "react";
import { BiMinus } from "react-icons/bi";
import { contentStyle, minimalButtonStyle, overlayStyle } from "./Popup.styles";
import { ConcedePopup, DefaultPopup, MulliganPopup } from "./Popup.types";
import { ConcededPopupModal } from "./PopupVariant/ConcedePopup";
import { DefaultPopupModal } from "./PopupVariant/DefaultPopup";
import { MulliganPopupModal } from "./PopupVariant/MulliganPopup";

const PopupShell: React.FC = () => {
const { type, data, closePopup } = usePopup();

if (!type || !data) return null; // No popup to display

const renderPopup = (type: PopupType, data: PopupData) => {
switch (type) {
case "default":
return <DefaultPopupModal data={data as DefaultPopup} />;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this a fine direction for this as far as giving us the ability to render different kinds of popups. However, I would think less about rendering specific popup and more about popup "forms" For instance, here, the concede and mulligan popups are essentially identical to the default popup. In fact both of these could be created using the default popup.

It may be that you only included these as a demonstration of selecting the popup type, but certainly the default popup is on the right track for what we want. I think it will be helpful for you to get into a game and start looking at some of the prompts we're receiving fromt he back end. I think even just this default prompt would handle a lot of cases if we change it to render the buttons provided by the prompts instead of just a yes no. I would take a look at the BasicPrompt already in the frontend code as a reference for how the default might work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. About mulligan and concede, I saw a possible interest for a variant of design(red button because it's important action, or anything imaginable), but we can definitely make it part of the data object we pass to the openPopup method.

About the different prompts, I know that 80% of the prompts can be handled using the basic variant, it was mainly to show flexibility of coding. For the final versions, I see 4 variants:

case "mulligan":
return <MulliganPopupModal data={data as MulliganPopup} />;
case "concede":
return <ConcededPopupModal data={data as ConcedePopup} />;
default:
return null;
}
};

return (
<Box sx={overlayStyle}>
<Box sx={contentStyle}>
<IconButton
sx={minimalButtonStyle}
aria-label="minimize"
onClick={closePopup}
>
<BiMinus />
</IconButton>
{renderPopup(type, data)}
</Box>
</Box>
);
};

export default PopupShell;
19 changes: 19 additions & 0 deletions src/app/_components/_sharedcomponents/Popup/Popup.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type DefaultPopup = {
type: "default";
title: string;
description: string;
onConfirm: () => void;
onCancel: () => void;
};

export type MulliganPopup = {
type: "mulligan";
onDraw: () => void;
onKeep: () => void;
};

export type ConcedePopup = {
type: "concede";
onConcede: () => void;
onCancel: () => void;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Box, Button, Typography } from "@mui/material";
import {
buttonStyle,
concedeButtonStyle,
containerStyle,
textStyle,
titleStyle,
} from "../Popup.styles";
import { ConcedePopup } from "../Popup.types";

interface ButtonProps {
data: ConcedePopup;
}

export const ConcededPopupModal = ({ data }: ButtonProps) => {
return (
<Box sx={containerStyle}>
<Typography sx={titleStyle}>Would you like to concede?</Typography>
<Typography sx={textStyle}>
Concede will end the game and declare you as the loser.
</Typography>
<Box sx={{ display: "flex", gap: "1rem", marginTop: "2rem" }}>
<Button
onClick={data.onConcede}
sx={concedeButtonStyle}
variant="contained"
>
Concede
</Button>
<Button onClick={data.onCancel} sx={buttonStyle}>
Continue playing
</Button>
</Box>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Box, Button, Typography } from "@mui/material";
import {
buttonStyle,
containerStyle,
textStyle,
titleStyle,
} from "../Popup.styles";
import { DefaultPopup } from "../Popup.types";

interface ButtonProps {
data: DefaultPopup;
}

export const DefaultPopupModal = ({ data }: ButtonProps) => {
return (
<Box sx={containerStyle}>
<Typography sx={titleStyle}>{data.title}</Typography>
<Typography sx={textStyle}>{data.description}</Typography>
<Box sx={{ display: "flex", gap: "1rem", marginTop: "2rem" }}>
<Button onClick={data.onConfirm} sx={buttonStyle} variant="contained">
Yes
</Button>
<Button onClick={data.onCancel} sx={buttonStyle}>
No
</Button>
</Box>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Box, Button, Typography } from "@mui/material";
import {
buttonStyle,
containerStyle,
textStyle,
titleStyle,
} from "../Popup.styles";
import { MulliganPopup } from "../Popup.types";

interface ButtonProps {
data: MulliganPopup;
}

export const MulliganPopupModal = ({ data }: ButtonProps) => {
return (
<Box sx={containerStyle}>
<Typography sx={titleStyle}>Would you like to mulligan?</Typography>
<Typography sx={textStyle}>
Mulligan will replace your initial 6 cards with another 6 new\nones.
This action can only be done one time!
</Typography>
<Box sx={{ display: "flex", gap: "1rem", marginTop: "2rem" }}>
<Button onClick={data.onDraw} sx={buttonStyle} variant="contained">
Draw another six
</Button>
<Button onClick={data.onKeep} sx={buttonStyle}>
Keep this hand
</Button>
</Box>
</Box>
);
};
59 changes: 59 additions & 0 deletions src/app/_contexts/Popup.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use client";
import React, { createContext, useContext, useState } from "react";
import {
ConcedePopup,
DefaultPopup,
MulliganPopup,
} from "../_components/_sharedcomponents/Popup/Popup.types";

// TODO: SelectCardsPopup | LeaderAbilityPopup | DrawPopup | SelectCardsWithAspectPopup
export type PopupData = DefaultPopup | MulliganPopup | ConcedePopup;

export type PopupType = PopupData["type"];

export type PopupDataMap = {
default: Omit<DefaultPopup, "type">;
mulligan: Omit<MulliganPopup, "type">;
concede: Omit<ConcedePopup, "type">;
};

interface PopupContextProps {
type?: PopupType;
data?: PopupData;
openPopup: <T extends PopupType>(type: T, data: PopupDataMap[T]) => void;
closePopup: () => void;
}

const PopupContext = createContext<PopupContextProps | undefined>(undefined);

export const PopupProvider: React.FC<{ children: React.ReactNode }> = ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have support for multiple popups here or is that still WIP?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet. But refactoring the code to handle multiple popups will not be that struggling

children,
}) => {
const [type, setType] = useState<PopupType | undefined>(undefined);
const [data, setData] = useState<PopupData | undefined>(undefined);

const openPopup = <T extends PopupType>(type: T, data: PopupDataMap[T]) => {
setType(type);
setData({ type, ...data } as PopupData);
};

const closePopup = () => {
console.log("Closing popup");
setType(undefined);
setData(undefined);
};

return (
<PopupContext.Provider value={{ type, data, openPopup, closePopup }}>
{children}
</PopupContext.Provider>
);
};

export const usePopup = () => {
const context = useContext(PopupContext);
if (!context) {
throw new Error("usePopup must be used within a PopupProvider");
}
return context;
};
25 changes: 16 additions & 9 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
// layout.tsx
import type { Metadata } from "next";
import React from 'react';
import ClientLayout from './ClientLayout';
import { Barlow } from "next/font/google";
import React from "react";
import { PopupProvider } from "./_contexts/Popup.context";
import "./_utils/s3Utils";
import ClientLayout from "./ClientLayout";

const barlow = Barlow({
subsets: ["latin"],
weight: ["400", "600", "800"],
variable: "--font-barlow",
subsets: ["latin"],
weight: ["400", "600", "800"],
variable: "--font-barlow",
});

export const metadata: Metadata = {
title: "Karabast",
title: "Karabast",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={barlow.variable}>
<body>
<ClientLayout>{children}</ClientLayout>
<PopupProvider>
<ClientLayout>{children}</ClientLayout>
</PopupProvider>
</body>
</html>
);
}
}