diff --git a/firestore.rules b/firestore.rules index d1379da1..e796294d 100644 --- a/firestore.rules +++ b/firestore.rules @@ -111,8 +111,8 @@ service cloud.firestore { match /collections/{homebrewId} { allow read: if true; allow create: if request.auth.uid != null; - allow write: if request.auth.uid in resource.data.editors && !request.resource.data.diff(resource.data).affectedKeys().hasAny(["creator", "editors"]); - + allow update: if (request.auth.uid in resource.data.editors && !request.resource.data.diff(resource.data).affectedKeys().hasAny(["creator", "editors"])) || (request.auth.uid != null && request.resource.data.diff(resource.data).affectedKeys().hasOnly(["viewers"])); + allow delete: if (request.auth.uid == resource.data.creator); } match /stats/{statId} { diff --git a/functions/src/index.ts b/functions/src/index.ts index 541021d8..34e50c34 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -139,3 +139,22 @@ export const addCurrentUserAsHomebrewCampaignEditor = onCall< return true; }); + +export const removeCurrentUserAsHomebrewCampaignEditor = onCall< + { homebrewCollectionId: string }, + Promise +>(async (request) => { + const homebrewCollectionId = request.data.homebrewCollectionId; + const uid = request.auth?.uid; + + if (!uid) { + logger.warn("User was not authenticated"); + return false; + } + + await getFirestore() + .doc(`/homebrew/homebrew/collections/${homebrewCollectionId}`) + .update({ editors: FieldValue.arrayRemove(uid) }); + + return true; +}); diff --git a/src/Router.tsx b/src/Router.tsx index fc55ec29..e536d925 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -92,6 +92,10 @@ const router = createBrowserRouter( path={homebrewPaths[HOMEBREW_ROUTES.EDITOR]} lazy={() => import("pages/Homebrew/HomebrewEditorPage")} /> + import("pages/Homebrew/HomebrewEditorInvitationPage")} + /> {/* Unauthenticated Pages */} diff --git a/src/api-calls/homebrew/editorFunction/getHomebrewCollectionFromInviteUrl.ts b/src/api-calls/homebrew/editorFunction/getHomebrewCollectionFromInviteUrl.ts index 08e568b6..7edb044b 100644 --- a/src/api-calls/homebrew/editorFunction/getHomebrewCollectionFromInviteUrl.ts +++ b/src/api-calls/homebrew/editorFunction/getHomebrewCollectionFromInviteUrl.ts @@ -1,12 +1,10 @@ import { functions } from "config/firebase.config"; import { httpsCallable } from "firebase/functions"; -export function getHomebrewCollectionFromInviteUrl(): Promise { +export function getHomebrewCollectionFromInviteUrl( + inviteKey: string +): Promise { return new Promise((resolve, reject) => { - const inviteKey = location.pathname.substring( - location.pathname.lastIndexOf("/") + 1 - ); - const getHomebrewId = httpsCallable( functions, "getHomebrewIdFromInviteKey" @@ -14,7 +12,7 @@ export function getHomebrewCollectionFromInviteUrl(): Promise { getHomebrewId({ inviteKey }) .then((homebrewId) => { - return homebrewId.data; + resolve(homebrewId.data as string | null); }) .catch((e) => { console.error(e); diff --git a/src/api-calls/homebrew/editorFunction/removeSelfAsEditor.ts b/src/api-calls/homebrew/editorFunction/removeSelfAsEditor.ts new file mode 100644 index 00000000..8d724aaa --- /dev/null +++ b/src/api-calls/homebrew/editorFunction/removeSelfAsEditor.ts @@ -0,0 +1,22 @@ +import { functions } from "config/firebase.config"; +import { httpsCallable } from "firebase/functions"; + +export function removeSelfAsEditor( + homebrewCollectionId: string +): Promise { + return new Promise((resolve, reject) => { + const removeEditor = httpsCallable( + functions, + "removeCurrentUserAsHomebrewCampaignEditor" + ); + + removeEditor({ homebrewCollectionId }) + .then((wasSuccessful) => { + resolve(wasSuccessful.data as boolean); + }) + .catch((e) => { + console.error(e); + reject(e); + }); + }); +} diff --git a/src/api-calls/homebrew/listenToHomebrewCollections.ts b/src/api-calls/homebrew/listenToHomebrewCollections.ts index 004c610e..c54b3fa3 100644 --- a/src/api-calls/homebrew/listenToHomebrewCollections.ts +++ b/src/api-calls/homebrew/listenToHomebrewCollections.ts @@ -1,4 +1,4 @@ -import { onSnapshot, query, where } from "firebase/firestore"; +import { onSnapshot, or, query, where } from "firebase/firestore"; import { HomebrewCollectionDocument } from "types/homebrew/HomebrewCollection.type"; import { getHomebrewCollection } from "./_getRef"; @@ -14,7 +14,10 @@ export function listenToHomebrewCollections( ) { const homebrewQuery = query( getHomebrewCollection(), - where("editors", "array-contains", uid) + or( + where("editors", "array-contains", uid), + where("viewers", "array-contains", uid) + ) ); return onSnapshot( homebrewQuery, diff --git a/src/api-calls/homebrew/updateHomebrewExpansion.ts b/src/api-calls/homebrew/updateHomebrewExpansion.ts index 8cfc9a83..cd1bbb11 100644 --- a/src/api-calls/homebrew/updateHomebrewExpansion.ts +++ b/src/api-calls/homebrew/updateHomebrewExpansion.ts @@ -1,10 +1,10 @@ -import { updateDoc } from "firebase/firestore"; +import { PartialWithFieldValue, updateDoc } from "firebase/firestore"; import { getHomebrewCollectionDoc } from "./_getRef"; import { ExpansionDocument } from "types/homebrew/HomebrewCollection.type"; import { createApiFunction } from "api-calls/createApiFunction"; export const updateHomebrewExpansion = createApiFunction< - { id: string; expansion: Partial }, + { id: string; expansion: PartialWithFieldValue }, void >((params) => { const { id, expansion } = params; diff --git a/src/components/features/charactersAndCampaigns/ExpansionSelector/ExpansionSelector.tsx b/src/components/features/charactersAndCampaigns/ExpansionSelector/ExpansionSelector.tsx index b7602151..7f5111d4 100644 --- a/src/components/features/charactersAndCampaigns/ExpansionSelector/ExpansionSelector.tsx +++ b/src/components/features/charactersAndCampaigns/ExpansionSelector/ExpansionSelector.tsx @@ -33,17 +33,18 @@ export function ExpansionSelector(props: ExpansionSelectorProps) { const officialExpansions = useGameSystemValue(defaultExpansions); const homebrewExpansionMap = useStore((store) => store.homebrew.collections); + const sortedExpansionIds = useStore( + (store) => store.homebrew.sortedHomebrewCollectionIds + ); - const expansionIds = Object.keys(homebrewExpansionMap) - .filter( - (expansionId) => - homebrewExpansionMap[expansionId]?.base?.rulesetId === baseRuleset - ) - .sort((k1, k2) => - homebrewExpansionMap[k1]?.base?.title?.localeCompare( - homebrewExpansionMap[k2]?.base?.title - ) - ); + const expansionIds = sortedExpansionIds.filter( + (expansionId) => + homebrewExpansionMap[expansionId]?.base?.rulesetId === baseRuleset + ); + + const notFoundExpansionIds = Object.keys(enabledExpansionMap).filter( + (key) => !expansionIds.includes(key) + ); return ( @@ -88,6 +89,23 @@ export function ExpansionSelector(props: ExpansionSelectorProps) { } /> ))} + {notFoundExpansionIds.map((expansionId) => ( + + toggleEnableExpansion(expansionId, checked) + } + /> + } + label={ + homebrewExpansionMap[expansionId].base?.title ?? + "Deleted Homebrew Expansion" + } + /> + ))} ) : ( diff --git a/src/components/shared/Layout/Layout.tsx b/src/components/shared/Layout/Layout.tsx index 3f5991b2..52224105 100644 --- a/src/components/shared/Layout/Layout.tsx +++ b/src/components/shared/Layout/Layout.tsx @@ -12,8 +12,10 @@ import { BottomNav } from "./nav/BottomNav"; import { NavRail } from "./nav/NavRail"; import { TopNav } from "./nav/TopNav"; import { LayoutPathListener } from "./LayoutPathListener"; -import { useNewCrewLinkTheme } from "hooks/featureFlags/useNewCrewLinkTheme"; import { StarforgedStarBackground } from "./StarforgedStarBackground"; +import { useGameSystem } from "hooks/useGameSystem"; +import { GAME_SYSTEMS } from "types/GameSystems.type"; +import { useNewSunderedIslesTheme } from "hooks/featureFlags/useNewSunderedIslesTheme"; export function Layout() { useSyncFeatureFlags(); @@ -25,7 +27,10 @@ export function Layout() { (store) => store.auth.closeUserNameDialog ); - const showNewCrewLinkTheme = useNewCrewLinkTheme(); + const { gameSystem } = useGameSystem(); + const showNewSunderedIslesTheme = useNewSunderedIslesTheme(); + const showStarforgedTheming = + gameSystem === GAME_SYSTEMS.STARFORGED && !showNewSunderedIslesTheme; if (state === AUTH_STATE.LOADING) { return ; @@ -37,12 +42,12 @@ export function Layout() { display={"flex"} flexDirection={"column"} sx={(theme) => ({ - backgroundColor: showNewCrewLinkTheme + backgroundColor: showStarforgedTheming ? undefined : theme.palette.background.default, })} > - {showNewCrewLinkTheme && } + {showStarforgedTheming && } ({ backgroundImage: - isLightTheme && !showNewStarforgedTheme + isLightTheme && (isIronsworn || showNewSunderedIslesTheme) ? `url("${borderUrl}")` : undefined, - backgroundColor: showNewSunderedIslesTheme - ? "darkGrey.main" - : undefined, + backgroundColor: + showNewSunderedIslesTheme && theme.palette.mode === "light" + ? "darkGrey.main" + : undefined, height: theme.spacing(showNewSunderedIslesTheme ? 2 : 8), backgroundRepeat: "repeat-x", backgroundSize: "contain", diff --git a/src/components/shared/Layout/nav/NavRailFlyouts/HomebrewMenu.tsx b/src/components/shared/Layout/nav/NavRailFlyouts/HomebrewMenu.tsx index a8e9f596..ff84f60c 100644 --- a/src/components/shared/Layout/nav/NavRailFlyouts/HomebrewMenu.tsx +++ b/src/components/shared/Layout/nav/NavRailFlyouts/HomebrewMenu.tsx @@ -6,10 +6,13 @@ import { FlyoutMenuList } from "./FlyoutMenuList"; export function HomebrewMenu() { const homebrewCollections = useStore((store) => store.homebrew.collections); + const sortedHomebrewCollections = useStore( + (store) => store.homebrew.sortedHomebrewCollectionIds + ); return ( ( - Select a Campaign - - } - /> + <> + + + + Select a Campaign + + } + /> + + ); } if (!uid || !campaign.gmIds?.includes(uid)) { diff --git a/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx b/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx index 546fd266..68e2742d 100644 --- a/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx +++ b/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx @@ -4,7 +4,7 @@ import { useSearchParams } from "react-router-dom"; import { CampaignSheetHeader } from "./components/CampaignSheetHeader"; import { CharacterSection } from "./components/CharacterSection"; import { WorldSection } from "./components/WorldSection"; -import { PageContent } from "components/shared/Layout"; +import { PageContent, PageHeader } from "components/shared/Layout"; import { BreakContainer } from "components/shared/BreakContainer"; import { TracksSection } from "./components/TracksSection"; import { StyledTabs, StyledTab } from "components/shared/StyledTabs"; @@ -68,21 +68,26 @@ export function CampaignSheetPage() { if (!campaignId || !campaign) { return ( - - Select a Campaign - - } - /> + <> + + + + Select a Campaign + + } + /> + + ); } @@ -98,7 +103,7 @@ export function CampaignSheetPage() { handleTabChange(value)} - indicatorColor='primary' + indicatorColor="primary" centered variant={"standard"} sx={(theme) => ({ diff --git a/src/pages/Character/CharacterSheetPage/CharacterSheetPage.tsx b/src/pages/Character/CharacterSheetPage/CharacterSheetPage.tsx index 9f04e54b..d1773112 100644 --- a/src/pages/Character/CharacterSheetPage/CharacterSheetPage.tsx +++ b/src/pages/Character/CharacterSheetPage/CharacterSheetPage.tsx @@ -45,21 +45,26 @@ export function CharacterSheetPage() { if (!isCharacterLoaded) { return ( - - Character Select - - } - /> + <> + + + + Character Select + + } + /> + + ); } diff --git a/src/pages/Homebrew/HomebrewEditorInvitationPage/HomebrewEditorInvitationPage.tsx b/src/pages/Homebrew/HomebrewEditorInvitationPage/HomebrewEditorInvitationPage.tsx new file mode 100644 index 00000000..c1b86b3a --- /dev/null +++ b/src/pages/Homebrew/HomebrewEditorInvitationPage/HomebrewEditorInvitationPage.tsx @@ -0,0 +1,97 @@ +import { LoadingButton } from "@mui/lab"; +import { Box, LinearProgress } from "@mui/material"; +import { acceptEditorInvite } from "api-calls/homebrew/editorFunction/acceptEditorInvite"; +import { getHomebrewCollectionFromInviteUrl } from "api-calls/homebrew/editorFunction/getHomebrewCollectionFromInviteUrl"; +import { EmptyState } from "components/shared/EmptyState"; +import { PageContent, PageHeader } from "components/shared/Layout"; +import { useSnackbar } from "providers/SnackbarProvider"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { useListenToHomebrewContent } from "stores/homebrew/useListenToHomebrewContent"; +import { useStore } from "stores/store"; +import { constructHomebrewEditorPath } from "../routes"; + +export function HomebrewEditorInvitationPage() { + const { editorInviteKey } = useParams<{ editorInviteKey: string }>(); + const [homebrewId, setHomebrewId] = useState(); + + useEffect(() => { + if (editorInviteKey) { + getHomebrewCollectionFromInviteUrl(editorInviteKey) + .then((parsedHomebrewId) => { + console.debug(parsedHomebrewId); + setHomebrewId(parsedHomebrewId ?? undefined); + }) + .catch((e) => console.error(e)); + } else { + setHomebrewId(undefined); + } + }, [editorInviteKey]); + + const homebrewIds = useMemo(() => { + if (homebrewId) { + return [homebrewId]; + } + return []; + }, [homebrewId]); + + useListenToHomebrewContent(homebrewIds); + + const homebrewDetails = useStore( + (store) => + homebrewId ? store.homebrew.collections[homebrewId]?.base : undefined, + (a, b) => JSON.stringify(a) === JSON.stringify(b) + ); + + const navigate = useNavigate(); + const { success, error } = useSnackbar(); + const [acceptLoading, setAcceptLoading] = useState(false); + const handleAccept = useCallback(() => { + if (homebrewId && editorInviteKey) { + setAcceptLoading(true); + acceptEditorInvite(homebrewId, editorInviteKey) + .then((result) => { + if (result) { + success("You have been added as an editor"); + navigate(constructHomebrewEditorPath(homebrewId)); + } else { + error("Failed to add you as an editor."); + } + }) + .catch(() => { + error("Failed to add you as an editor."); + }) + .finally(() => { + setAcceptLoading(false); + }); + } + }, [homebrewId, editorInviteKey, success, error, navigate]); + + if (!homebrewDetails) { + return ; + } + + return ( + <> + + + + + Accept Invitation + + } + /> + + + + ); +} diff --git a/src/pages/Homebrew/HomebrewEditorInvitationPage/index.ts b/src/pages/Homebrew/HomebrewEditorInvitationPage/index.ts new file mode 100644 index 00000000..c97bd5da --- /dev/null +++ b/src/pages/Homebrew/HomebrewEditorInvitationPage/index.ts @@ -0,0 +1 @@ +export { HomebrewEditorInvitationPage as Component } from "./HomebrewEditorInvitationPage"; diff --git a/src/pages/Homebrew/HomebrewEditorPage/AboutSection/AboutSection.tsx b/src/pages/Homebrew/HomebrewEditorPage/AboutSection/AboutSection.tsx index 026812fc..7092d5f8 100644 --- a/src/pages/Homebrew/HomebrewEditorPage/AboutSection/AboutSection.tsx +++ b/src/pages/Homebrew/HomebrewEditorPage/AboutSection/AboutSection.tsx @@ -18,14 +18,21 @@ import { InviteEditorDialog } from "./InviteEditorDialog"; import { useSnackbar } from "providers/SnackbarProvider"; import { constructHomebrewEditorPath } from "pages/Homebrew/routes"; import { UserList } from "./UserList"; +import { useConfirm } from "material-ui-confirm"; +import { arrayRemove } from "firebase/firestore"; +import { removeSelfAsEditor } from "api-calls/homebrew/editorFunction/removeSelfAsEditor"; +import { useNavigate } from "react-router-dom"; +import { BASE_ROUTES, basePaths } from "routes"; export interface AboutSectionProps { id: string; isEditor: boolean; + isViewer: boolean; + isOwner: boolean; } export function AboutSection(props: AboutSectionProps) { - const { id, isEditor } = props; + const { id, isEditor, isViewer, isOwner } = props; const { success } = useSnackbar(); @@ -52,6 +59,53 @@ export function AboutSection(props: AboutSectionProps) { }); }; + const deleteCollection = useStore((store) => store.homebrew.deleteExpansion); + const updateExpansion = useStore((store) => store.homebrew.updateExpansion); + + const uid = useStore((store) => store.auth.uid); + + const navigate = useNavigate(); + + const confirm = useConfirm(); + + const removeSelf = () => { + const promises: Promise[] = []; + if (isViewer) { + promises.push(updateExpansion(id, { viewers: arrayRemove(uid) })); + } + if (isEditor) { + promises.push(removeSelfAsEditor(id)); + } + Promise.all(promises) + .then(() => { + location.reload(); + }) + .catch(() => { + location.reload(); + }); + }; + + const handleDeleteCollection = () => { + confirm({ + title: "Delete Homebrew Collection", + description: + "Are you sure you want to delete this homebrew expansion? Characters and campaigns that use this expansion will no longer be able to use it.", + confirmationText: "Delete", + confirmationButtonProps: { + variant: "contained", + color: "error", + }, + }) + .then(() => { + deleteCollection(id) + .then(() => { + navigate(basePaths[BASE_ROUTES.HOMEBREW]); + }) + .catch(() => {}); + }) + .catch(() => {}); + }; + return ( Players and GMs in the campaigns you run will automatically be able to view enabled homebrew content within their character - sheets and GM screens, but will not be allowed to edit it unless - you add them as an editor below. + sheets and GM screens, but will not be able to edit content unless + you invite them as an editor. )} @@ -137,8 +191,9 @@ export function AboutSection(props: AboutSectionProps) { Viewers - Viewers will be able to use this content on their own characters - and campaigns, but will not be allowed to make any changes. + Viewers will be able to add this content to their own homebrew + page to use for characters and campaigns, but will not be + allowed to edit anything. + )} + {((!isOwner && isEditor) || isViewer) && ( + + )} + + + )} {isEditor && ( - {inviteLink ?? } + {inviteLink ? ( + inviteLink + ) : ( + + )} - } - /> + <> + + + + Your Homebrew + + } + /> + + ); } @@ -109,34 +122,27 @@ export function HomebrewEditorPage() { const isViewer = uid ? homebrewDetails?.viewers?.includes(uid) ?? false : false; + const isOwner = uid ? homebrewDetails?.creator === uid ?? false : false; const getPageHeaderProps = (): Partial => { if (isEditor) { + return {}; + } else if (isViewer) { + return {}; + } else if (isLoggedIn) { return { + subLabel: ( + + You can use this homebrew in your characters and campaigns by adding + it to your collection + + ), actions: ( - ), - }; - } else if (isViewer) { - return {}; - } else if (isLoggedIn) { - return { - subLabel: - "You can use this homebrew in your characters and campaigns by adding it to your collection", - actions: ( - ), @@ -185,7 +191,12 @@ export function HomebrewEditorPage() { {selectedTab === TABS.ABOUT && ( - + )} {selectedTab === TABS.RULES && ( diff --git a/src/pages/Homebrew/HomebrewEditorPage/OracleSection/Sections/OracleCollectionsSection/MoveOracleCollectionDialog.tsx b/src/pages/Homebrew/HomebrewEditorPage/OracleSection/Sections/OracleCollectionsSection/MoveOracleCollectionDialog.tsx index 85e18a2d..03dbc42e 100644 --- a/src/pages/Homebrew/HomebrewEditorPage/OracleSection/Sections/OracleCollectionsSection/MoveOracleCollectionDialog.tsx +++ b/src/pages/Homebrew/HomebrewEditorPage/OracleSection/Sections/OracleCollectionsSection/MoveOracleCollectionDialog.tsx @@ -41,8 +41,6 @@ export function MoveOracleCollectionDialog( setCollectionId(parentOracleCollectionId); }, [parentOracleCollectionId]); - console.debug(parentOracleCollectionId, collectionId, oracleCollections); - const updateOracleCollection = useStore( (store) => store.homebrew.updateOracleCollection ); diff --git a/src/pages/Homebrew/HomebrewSelectPage/HomebrewSelectPage.tsx b/src/pages/Homebrew/HomebrewSelectPage/HomebrewSelectPage.tsx index 5551a6bc..50042520 100644 --- a/src/pages/Homebrew/HomebrewSelectPage/HomebrewSelectPage.tsx +++ b/src/pages/Homebrew/HomebrewSelectPage/HomebrewSelectPage.tsx @@ -23,6 +23,9 @@ import { FooterFab } from "components/shared/Layout/FooterFab"; export function HomebrewSelectPage() { const showPage = useNewCustomContentPage(); + const sortedHomebrewIds = useStore( + (store) => store.homebrew.sortedHomebrewCollectionIds + ); const homebrewCollections = useStore((store) => store.homebrew.collections); const homebrewLoading = useStore((store) => store.homebrew.loading); const errorMessage = useStore((store) => store.homebrew.error); @@ -34,14 +37,6 @@ export function HomebrewSelectPage() { return null; } - const collectionKeys = Object.keys(homebrewCollections).sort((k1, k2) => - ( - homebrewCollections[k1]?.base?.title || "Unnamed Collection" - )?.localeCompare( - homebrewCollections[k2]?.base?.title || "Unnamed Collection" - ) - ); - const collectionIds = Object.values(homebrewCollections).map( (collection) => collection.base?.id ); @@ -115,7 +110,7 @@ export function HomebrewSelectPage() { my={0} sx={{ listStyle: "none" }} > - {collectionKeys.map((collectionKey) => ( + {sortedHomebrewIds.map((collectionKey) => ( - Select a World - - } - /> + <> + + + + Select a World + + } + /> + + ); } diff --git a/src/providers/ThemeProvider/ThemeProvider.tsx b/src/providers/ThemeProvider/ThemeProvider.tsx index 927702fa..19ea2799 100644 --- a/src/providers/ThemeProvider/ThemeProvider.tsx +++ b/src/providers/ThemeProvider/ThemeProvider.tsx @@ -8,33 +8,23 @@ import { THEME_TYPE } from "./themes"; import { ThemeContext } from "./ThemeContext"; import { useGameSystemValue } from "hooks/useGameSystemValue"; import { GAME_SYSTEMS, GameSystemChooser } from "types/GameSystems.type"; -import { useNewCrewLinkTheme } from "hooks/featureFlags/useNewCrewLinkTheme"; import { useNewSunderedIslesTheme } from "hooks/featureFlags/useNewSunderedIslesTheme"; -import { starforgedLightTheme } from "./themes/starforged-light"; import { ironswornLightTheme } from "./themes/ironsworn-light"; import { ironswornDarkTheme } from "./themes/ironsworn-dark"; +import { starforgedLightTheme } from "./themes/starforged-light"; import { starforgedDarkTheme } from "./themes/starforged-dark"; -import { newStarforgedLightTheme } from "./themes/new-starforged-light"; -import { newStarforgedDarkTheme } from "./themes/new-starforged-dark"; import { sunderedIslesLightTheme } from "./themes/sundered-isles-light"; import { sunderedIslesDarkTheme } from "./themes/sundered-isles-dark"; export function ThemeProvider(props: PropsWithChildren) { const { children } = props; - const showNewStarforgedTheme = useNewCrewLinkTheme(); const showSunderedIslesTheme = useNewSunderedIslesTheme(); let starforgedTheme: { [key in THEME_TYPE]: Theme } = { [THEME_TYPE.LIGHT]: starforgedLightTheme, [THEME_TYPE.DARK]: starforgedDarkTheme, }; - if (showNewStarforgedTheme) { - starforgedTheme = { - [THEME_TYPE.LIGHT]: newStarforgedLightTheme, - [THEME_TYPE.DARK]: newStarforgedDarkTheme, - }; - } if (showSunderedIslesTheme) { starforgedTheme = { [THEME_TYPE.LIGHT]: sunderedIslesLightTheme, diff --git a/src/providers/ThemeProvider/themes/new-starforged-dark.tsx b/src/providers/ThemeProvider/themes/new-starforged-dark.tsx deleted file mode 100644 index f6a7fb18..00000000 --- a/src/providers/ThemeProvider/themes/new-starforged-dark.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { createTheme } from "@mui/material"; -import { green } from "@mui/material/colors"; -import { baseFontFamilies, sharedStatusColors } from "./constants-shared"; -import { starforgedGold, starforgedGrey } from "./constants-starforged"; -import { HexboxUnchecked } from "assets/HexboxUnchecked"; -import { HexboxChecked } from "assets/HexboxChecked"; - -export const newStarforgedDarkTheme = createTheme({ - palette: { - mode: "dark", - primary: starforgedGold, - secondary: green, - darkGrey: { - light: starforgedGrey[900], - main: starforgedGrey[950], - dark: "#030712", - contrastText: "#fff", - }, - ...sharedStatusColors, - background: { - paper: starforgedGrey[900], - paperInlay: starforgedGrey[950], - paperInlayDarker: starforgedGrey[800], - default: starforgedGrey[950], - }, - grey: starforgedGrey, - divider: starforgedGrey[600], - }, - typography: { - fontFamily: ["RubikVariable", ...baseFontFamilies].join(","), - }, - fontFamilyTitle: ["'Bebas Neue'", ...baseFontFamilies].join(","), - shape: { - borderRadius: 8, - }, - components: { - MuiList: { - styleOverrides: { - root: { - "&& .Mui-selected, && .Mui-selected:hover": { - backgroundColor: starforgedGrey[700], - }, - }, - }, - }, - MuiCard: { - styleOverrides: { - root: { - backgroundImage: "unset!important", // Remove the annoying elevation background filter - }, - }, - }, - MuiPaper: { - styleOverrides: { - root: { - backgroundImage: "unset!important", // Remove the annoying elevation background filter - }, - }, - }, - MuiCheckbox: { - defaultProps: { - icon: , - checkedIcon: , - }, - styleOverrides: { - root: { - "&&.Mui-disabled": { - color: "#595e68", - }, - }, - }, - }, - MuiButton: { - styleOverrides: { - root: ({ theme }) => { - return { - "&.Mui-focusVisible": { - boxShadow: `inset 0 0 0 2px ${theme.palette.info.light}, 0 0 0 2px ${theme.palette.info.light}`, - }, - }; - }, - }, - }, - MuiButtonBase: { - styleOverrides: { - root: ({ theme }) => ({ - "&.Mui-focusVisible": { - boxShadow: `inset 0 0 0 2px ${theme.palette.info.light}, 0 0 0 2px ${theme.palette.info.light}`, - }, - }), - }, - }, - }, -}); diff --git a/src/providers/ThemeProvider/themes/new-starforged-light.tsx b/src/providers/ThemeProvider/themes/new-starforged-light.tsx deleted file mode 100644 index 60fcd0b1..00000000 --- a/src/providers/ThemeProvider/themes/new-starforged-light.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { createTheme } from "@mui/material"; -import { green } from "@mui/material/colors"; -import { baseFontFamilies, sharedStatusColors } from "./constants-shared"; -import { starforgedGold, starforgedGrey } from "./constants-starforged"; -import { HexboxUnchecked } from "assets/HexboxUnchecked"; -import { HexboxChecked } from "assets/HexboxChecked"; - -export const newStarforgedLightTheme = createTheme({ - palette: { - primary: starforgedGold, - secondary: green, - darkGrey: { - light: starforgedGrey[800], - main: starforgedGrey[900], - dark: starforgedGrey[950], - contrastText: "#fff", - }, - divider: starforgedGrey[300], - ...sharedStatusColors, - background: { - paper: "#fff", - paperInlay: starforgedGrey[100], - paperInlayDarker: starforgedGrey[200], - default: starforgedGrey[200], - }, - grey: starforgedGrey, - }, - typography: { - fontFamily: ["RubikVariable", ...baseFontFamilies].join(","), - }, - fontFamilyTitle: ["'Bebas Neue'", ...baseFontFamilies].join(","), - shape: { - borderRadius: 8, - }, - components: { - MuiList: { - styleOverrides: { - root: { - "&& .Mui-selected, && .Mui-selected:hover": { - backgroundColor: starforgedGrey[200], - }, - }, - }, - }, - MuiCheckbox: { - defaultProps: { - icon: , - checkedIcon: , - }, - styleOverrides: { - root: { - "&&.Mui-disabled": { - color: "#bdbdbd", - }, - }, - }, - }, - MuiButton: { - styleOverrides: { - root: ({ theme }) => { - return { - "&.Mui-focusVisible": { - boxShadow: `inset 0 0 0 2px ${theme.palette.info.main}, 0 0 0 2px ${theme.palette.info.main}`, - "&.dark-focus-outline": { - boxShadow: `inset 0 0 0 2px ${theme.palette.info.light}, 0 0 0 2px ${theme.palette.info.light}`, - }, - }, - }; - }, - }, - }, - MuiButtonBase: { - styleOverrides: { - root: ({ theme }) => ({ - "&.Mui-focusVisible": { - boxShadow: `inset 0 0 0 2px ${theme.palette.info.main}, 0 0 0 2px ${theme.palette.info.main}`, - "&.dark-focus-outline": { - boxShadow: `inset 0 0 0 2px ${theme.palette.info.light}, 0 0 0 2px ${theme.palette.info.light}`, - }, - }, - }), - }, - }, - }, -}); diff --git a/src/providers/ThemeProvider/themes/starforged-dark.tsx b/src/providers/ThemeProvider/themes/starforged-dark.tsx index b0af68ed..1335bda2 100644 --- a/src/providers/ThemeProvider/themes/starforged-dark.tsx +++ b/src/providers/ThemeProvider/themes/starforged-dark.tsx @@ -11,8 +11,8 @@ export const starforgedDarkTheme = createTheme({ primary: starforgedGold, secondary: green, darkGrey: { - light: starforgedGrey[800], - main: starforgedGrey[900], + light: starforgedGrey[900], + main: starforgedGrey[950], dark: "#030712", contrastText: "#fff", }, diff --git a/src/providers/ThemeProvider/themes/starforged-light.tsx b/src/providers/ThemeProvider/themes/starforged-light.tsx index 60f18975..ea6a6331 100644 --- a/src/providers/ThemeProvider/themes/starforged-light.tsx +++ b/src/providers/ThemeProvider/themes/starforged-light.tsx @@ -10,9 +10,9 @@ export const starforgedLightTheme = createTheme({ primary: starforgedGold, secondary: green, darkGrey: { - light: starforgedGrey[600], - main: starforgedGrey[700], - dark: starforgedGrey[800], + light: starforgedGrey[800], + main: starforgedGrey[900], + dark: starforgedGrey[950], contrastText: "#fff", }, divider: starforgedGrey[300], diff --git a/src/stores/homebrew/homebrew.slice.default.ts b/src/stores/homebrew/homebrew.slice.default.ts index b5a5d3c9..c0d22d60 100644 --- a/src/stores/homebrew/homebrew.slice.default.ts +++ b/src/stores/homebrew/homebrew.slice.default.ts @@ -1,6 +1,7 @@ import { HomebrewSliceData } from "./homebrew.slice.type"; export const defaultHomebrewSlice: HomebrewSliceData = { + sortedHomebrewCollectionIds: [], collections: {}, loading: true, }; diff --git a/src/stores/homebrew/homebrew.slice.ts b/src/stores/homebrew/homebrew.slice.ts index 5450ca3d..fbb5ad01 100644 --- a/src/stores/homebrew/homebrew.slice.ts +++ b/src/stores/homebrew/homebrew.slice.ts @@ -92,6 +92,22 @@ export const createHomebrewSlice: CreateSliceType = ( ...(store.homebrew.collections[collectionId] ?? {}), base: collection, }; + + store.homebrew.sortedHomebrewCollectionIds = Object.keys( + store.homebrew.collections + ) + .filter((key) => { + return ( + store.homebrew.collections[key]?.base?.editors.includes(uid) || + store.homebrew.collections[key]?.base.viewers?.includes(uid) + ); + }) + .sort((k1, k2) => + (store.homebrew.collections[k1]?.base?.title ?? "").localeCompare( + store.homebrew.collections[k2]?.base?.title ?? "" + ) + ); + store.homebrew.loading = false; store.homebrew.error = undefined; }); @@ -101,6 +117,24 @@ export const createHomebrewSlice: CreateSliceType = ( delete store.homebrew.collections[collectionId]; store.homebrew.loading = false; store.homebrew.error = undefined; + store.homebrew.sortedHomebrewCollectionIds = Object.keys( + store.homebrew.collections + ) + .filter((key) => { + const shouldKeep = + store.homebrew.collections[key]?.base?.editors.includes( + store.auth.uid + ) || + store.homebrew.collections[key]?.base.viewers?.includes( + store.auth.uid + ); + return shouldKeep; + }) + .sort((k1, k2) => + (store.homebrew.collections[k1]?.base?.title ?? "").localeCompare( + store.homebrew.collections[k2]?.base?.title ?? "" + ) + ); }); }, (error) => { @@ -222,6 +256,26 @@ export const createHomebrewSlice: CreateSliceType = ( ...store.homebrew.collections[homebrewId], base: data, }; + store.homebrew.sortedHomebrewCollectionIds = Object.keys( + store.homebrew.collections + ) + .filter((key) => { + const shouldKeep = + store.homebrew.collections[key]?.base?.editors.includes( + store.auth.uid + ) || + store.homebrew.collections[key]?.base.viewers?.includes( + store.auth.uid + ); + return shouldKeep; + }) + .sort((k1, k2) => + ( + store.homebrew.collections[k1]?.base?.title ?? "" + ).localeCompare( + store.homebrew.collections[k2]?.base?.title ?? "" + ) + ); }); }, (error) => { @@ -241,7 +295,6 @@ export const createHomebrewSlice: CreateSliceType = ( } ) ); - console.debug("STARTING LISTENERS"); listenerConfigs.forEach((config) => { unsubscribes.push( config.listenerFunction( @@ -264,7 +317,6 @@ export const createHomebrewSlice: CreateSliceType = ( getState().homebrew.updateDataswornMoves(homebrewId); break; case ListenerRefreshes.Stats: - console.debug("GOT STATS"); getState().rules.rebuildStats(); break; case ListenerRefreshes.ConditionMeters: diff --git a/src/stores/homebrew/homebrew.slice.type.ts b/src/stores/homebrew/homebrew.slice.type.ts index d5c8125b..a522ff07 100644 --- a/src/stores/homebrew/homebrew.slice.type.ts +++ b/src/stores/homebrew/homebrew.slice.type.ts @@ -54,6 +54,7 @@ export interface HomebrewEntry { } export interface HomebrewSliceData { + sortedHomebrewCollectionIds: string[]; collections: Record; loading: boolean; error?: string; @@ -66,7 +67,7 @@ export interface HomebrewSliceActions { createExpansion: (expansion: ExpansionDocument) => Promise; updateExpansion: ( expansionId: string, - expansion: Partial + expansion: PartialWithFieldValue ) => Promise; deleteExpansion: (expansionId: string) => Promise; diff --git a/src/stores/rules/rules.slice.ts b/src/stores/rules/rules.slice.ts index 96c5730d..573cb3cb 100644 --- a/src/stores/rules/rules.slice.ts +++ b/src/stores/rules/rules.slice.ts @@ -209,9 +209,8 @@ export const createRulesSlice: CreateSliceType = ( {}; nonLinearMeters = { ...nonLinearMeters, ...expansionNonLinearMeters }; } - - store.rules.nonLinearMeters = nonLinearMeters; }); + store.rules.nonLinearMeters = nonLinearMeters; }); }, rebuildSpecialTracks: () => {