diff --git a/CHANGELOG.MD b/CHANGELOG.MD
index d22ba70e..f2d01df4 100644
--- a/CHANGELOG.MD
+++ b/CHANGELOG.MD
@@ -14,6 +14,7 @@
- Removed "Ask the Oracle" oracles from the oracle list, since they are pinned up top
- Added a test mobile view for moves and oracles
- Redesigned stats and tracks sections on mobile
+- Redesigned moves and oracles to make each section collapsible
### Accessibility
@@ -25,6 +26,7 @@
- Fixed an issue where deleting a custom move caused the app to crash
- Made some performance improvements on the character view
- Fixed shared tracks not showing up in the move rollers
+- Prevented oracle inputs from reordering themselves
---
diff --git a/src/components/features/assets/AssetCard/AssetCard.tsx b/src/components/features/assets/AssetCard/AssetCard.tsx
index d4b210ee..e7c21394 100644
--- a/src/components/features/assets/AssetCard/AssetCard.tsx
+++ b/src/components/features/assets/AssetCard/AssetCard.tsx
@@ -179,38 +179,48 @@ export function AssetCard(props: AssetCardProps) {
{asset.Requirement && (
)}
- {Object.values(asset.Inputs ?? {}).map((field) => (
- {
- if (handleInputChange) {
- return handleInputChange(encodeDataswornId(field.$id), value);
- }
- return new Promise((res, reject) =>
- reject("HandleInputChange is undefined")
- );
- }}
- disabled={readOnly || !handleInputChange}
- />
- ))}
- {abilityInputs.map((field) => (
- {
- if (handleInputChange) {
- return handleInputChange(encodeDataswornId(field.$id), value);
- }
- return new Promise((res, reject) =>
- reject("handleInputChange is undefined")
- );
- }}
- disabled={readOnly || !handleInputChange}
- />
- ))}
+ {Object.values(asset.Inputs ?? {})
+ .sort((i1, i2) => i1.Label.localeCompare(i2.Label))
+ .map((field) => (
+ {
+ if (handleInputChange) {
+ return handleInputChange(
+ encodeDataswornId(field.$id),
+ value
+ );
+ }
+ return new Promise((res, reject) =>
+ reject("HandleInputChange is undefined")
+ );
+ }}
+ disabled={readOnly || !handleInputChange}
+ />
+ ))}
+ {abilityInputs
+ .sort((i1, i2) => i1.Label.localeCompare(i2.Label))
+ .map((field) => (
+ {
+ if (handleInputChange) {
+ return handleInputChange(
+ encodeDataswornId(field.$id),
+ value
+ );
+ }
+ return new Promise((res, reject) =>
+ reject("handleInputChange is undefined")
+ );
+ }}
+ disabled={readOnly || !handleInputChange}
+ />
+ ))}
{asset.Abilities.map((ability, index) => (
0 && (
- {conditionMeter.Conditions.map((condition) => (
+ {conditionMeter.Conditions.sort((c1, c2) =>
+ c1.localeCompare(c2)
+ ).map((condition) => (
void;
+}
+
+export function CollapsibleSectionHeader(props: CollapsibleSectionHeaderProps) {
+ const { text, open, toggleOpen } = props;
+
+ return (
+ ({
+ mt: open ? 0.5 : 0,
+ backgroundColor: theme.palette.background.paperInlayDarker,
+ color: theme.palette.grey[theme.palette.mode === "light" ? 700 : 200],
+ ...theme.typography.body1,
+ fontFamily: theme.fontFamilyTitle,
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ transition: theme.transitions.create(["margin"], {
+ duration: theme.transitions.duration.shorter,
+ }),
+ })}
+ >
+
+ {text}
+
+ ({
+ transform: `rotate(${open ? "-" : ""}90deg)`,
+ transition: theme.transitions.create(["transform"], {
+ duration: theme.transitions.duration.shorter,
+ }),
+ })}
+ />
+
+
+
+ );
+}
diff --git a/src/components/features/charactersAndCampaigns/MoveDrawer.tsx b/src/components/features/charactersAndCampaigns/MoveDrawer.tsx
index 6f6d4311..c6f203dd 100644
--- a/src/components/features/charactersAndCampaigns/MoveDrawer.tsx
+++ b/src/components/features/charactersAndCampaigns/MoveDrawer.tsx
@@ -15,6 +15,7 @@ function MoveDrawerUnMemoized(props: MoveDrawerProps) {
void;
+ forceOpen?: boolean;
}
export function MoveCategory(props: MoveCategoryProps) {
- const { category, openMove } = props;
+ const { category, openMove, forceOpen } = props;
+
+ const showNewView = useNewMoveOracleView();
+ const [isExpanded, setIsExpanded] = useState(showNewView ? false : true);
+
+ useEffect(() => {
+ setIsExpanded(showNewView ? false : true);
+ }, [showNewView]);
+
+ const isExpandedOrForced = isExpanded || forceOpen;
return (
<>
- ({
- backgroundColor:
- theme.palette.darkGrey[
- theme.palette.mode === "light" ? "light" : "dark"
- ],
- color: theme.palette.darkGrey.contrastText,
- ...theme.typography.body1,
- fontFamily: theme.fontFamilyTitle,
- })}
- >
- {category.Title.Standard}
-
- {Object.values(category.Moves).map((move, index) => (
- !forceOpen && setIsExpanded((prev) => !prev)}
+ text={category.Title.Standard}
+ />
+ ) : (
+ ({
- "&:nth-of-type(odd)": {
- backgroundColor: theme.palette.action.hover,
- },
+ backgroundColor:
+ theme.palette.darkGrey[
+ theme.palette.mode === "light" ? "light" : "dark"
+ ],
+ color: theme.palette.darkGrey.contrastText,
+ ...theme.typography.body1,
+ fontFamily: theme.fontFamilyTitle,
})}
- disablePadding
>
- openMove(move)}
- sx={{
- display: "flex",
- justifyContent: "space-between",
- alignItems: "center",
- }}
- >
-
+ )}
+
+
+ {Object.values(category.Moves).map((move, index) => (
+ ({
- ...theme.typography.body2,
- color: theme.palette.text.primary,
+ "&:nth-of-type(even)": {
+ backgroundColor: theme.palette.background.paperInlay,
+ },
})}
+ disablePadding
>
- {move.Title.Standard}
-
-
-
-
-
-
- ))}
+ openMove(move)}
+ sx={{
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ }}
+ >
+ ({
+ ...theme.typography.body2,
+ color: theme.palette.text.primary,
+ })}
+ >
+ {move.Title.Standard}
+
+
+
+
+
+
+ ))}
+
+
>
);
}
diff --git a/src/components/features/charactersAndCampaigns/MovesSection/MovesSection.tsx b/src/components/features/charactersAndCampaigns/MovesSection/MovesSection.tsx
index c7d68635..bc8faaf2 100644
--- a/src/components/features/charactersAndCampaigns/MovesSection/MovesSection.tsx
+++ b/src/components/features/charactersAndCampaigns/MovesSection/MovesSection.tsx
@@ -5,34 +5,44 @@ import { useFilterMoves } from "./useFilterMoves";
import { useStore } from "stores/store";
export function MovesSection() {
- const { setSearch, filteredMoves } = useFilterMoves();
+ const { setSearch, filteredMoves, isSearchActive } = useFilterMoves();
const openDialog = useStore((store) => store.appState.openDialog);
return (
<>
-
- ({ color: theme.palette.grey[300] })} />
-
- }
- aria-label={"Filter Moves"}
- placeholder={"Filter Moves"}
- onChange={(evt) => setSearch(evt.currentTarget.value)}
- color={"primary"}
- sx={(theme) => ({
- backgroundColor: theme.palette.darkGrey.main,
- color: "#fff",
- px: 2,
- "&::hover": {
+ theme.palette.darkGrey.contrastText}
+ bgcolor={(theme) => theme.palette.darkGrey.dark}
+ borderBottom={(theme) => `1px solid ${theme.palette.darkGrey.dark}`}
+ >
+
+ ({ color: theme.palette.grey[300] })}
+ />
+
+ }
+ aria-label={"Filter Moves"}
+ placeholder={"Filter Moves"}
+ onChange={(evt) => setSearch(evt.currentTarget.value)}
+ color={"primary"}
+ sx={(theme) => ({
+ backgroundColor: theme.palette.darkGrey.main,
+ color: "#fff",
+ px: 2,
borderBottomColor: theme.palette.darkGrey.light,
- },
- })}
- />
-
-
+ })}
+ />
+
+
{filteredMoves.map((category, index) => (
{
openDialog(move.$id);
}}
+ forceOpen={isSearchActive}
/>
))}
diff --git a/src/components/features/charactersAndCampaigns/MovesSection/useFilterMoves.ts b/src/components/features/charactersAndCampaigns/MovesSection/useFilterMoves.ts
index f8aaf99c..47c6d4d3 100644
--- a/src/components/features/charactersAndCampaigns/MovesSection/useFilterMoves.ts
+++ b/src/components/features/charactersAndCampaigns/MovesSection/useFilterMoves.ts
@@ -6,7 +6,7 @@ import { useCustomMoves } from "./useCustomMoves";
import { useStore } from "stores/store";
export function useFilterMoves() {
- const { setSearch, debouncedSearch } = useSearch();
+ const { search, setSearch, debouncedSearch } = useSearch();
const [filteredMoves, setFilteredMoves] = useState(orderedCategories);
const { customMoveCategories } = useCustomMoves();
@@ -58,5 +58,5 @@ export function useFilterMoves() {
setFilteredMoves(results);
}, [debouncedSearch, customMoveCategories, showDelveMoves]);
- return { setSearch, filteredMoves };
+ return { setSearch, filteredMoves, isSearchActive: !!search };
}
diff --git a/src/components/features/charactersAndCampaigns/OracleDrawer.tsx b/src/components/features/charactersAndCampaigns/OracleDrawer.tsx
index 010cdc45..d6b02656 100644
--- a/src/components/features/charactersAndCampaigns/OracleDrawer.tsx
+++ b/src/components/features/charactersAndCampaigns/OracleDrawer.tsx
@@ -15,6 +15,7 @@ function OracleDrawerUnMemoized(props: OracleDrawerProps) {
store.appState.openDialog);
@@ -22,6 +26,14 @@ export function OracleCategory(props: OracleCategoryProps) {
const sampleNames = category["Sample Names" as "Sample names"];
+ const showNewView = useNewMoveOracleView();
+ const [isExpanded, setIsExpanded] = useState(showNewView ? false : true);
+ useEffect(() => {
+ setIsExpanded(showNewView ? false : true);
+ }, [showNewView]);
+
+ const isExpandedOrForced = isExpanded || forceOpen || false;
+
if (hiddenOracleCategoryIds[category.$id]) {
return null;
}
@@ -29,52 +41,67 @@ export function OracleCategory(props: OracleCategoryProps) {
return (
<>
- {Object.keys(category.Tables ?? {}).length > 0 && (
- ({
- backgroundColor:
- theme.palette.darkGrey[
- theme.palette.mode === "light" ? "light" : "dark"
- ],
- color: theme.palette.darkGrey.contrastText,
- ...theme.typography.body1,
- fontFamily: theme.fontFamilyTitle,
- })}
- >
- {title}
-
- )}
- {Array.isArray(sampleNames) &&
- sampleNames.length > 0 &&
- Object.keys(category.Tables ?? {}).length > 0 && (
-
- rollOracleTable(category.$id + "/sample_names", true, true)
- }
- onOpenClick={() => openDialog(category.$id + "/sample_names")}
- />
- )}
- {Object.keys(category.Tables ?? {}).map((oracleId, index) => {
- const oracle = category.Tables?.[oracleId];
- if (!oracle) return null;
- return (
- rollOracleTable(oracle.$id, true, true)}
- onOpenClick={() => {
- openDialog(oracle.$id);
- }}
+ {Object.keys(category.Tables ?? {}).length > 0 &&
+ (showNewView ? (
+ !forceOpen && setIsExpanded((prev) => !prev)}
+ text={title}
/>
- );
- })}
+ ) : (
+ ({
+ backgroundColor:
+ theme.palette.darkGrey[
+ theme.palette.mode === "light" ? "light" : "dark"
+ ],
+ color: theme.palette.darkGrey.contrastText,
+ ...theme.typography.body1,
+ fontFamily: theme.fontFamilyTitle,
+ })}
+ >
+ {title}
+
+ ))}
+
+
+ {Array.isArray(sampleNames) &&
+ sampleNames.length > 0 &&
+ Object.keys(category.Tables ?? {}).length > 0 && (
+
+ rollOracleTable(category.$id + "/sample_names", true, true)
+ }
+ onOpenClick={() => openDialog(category.$id + "/sample_names")}
+ />
+ )}
+ {Object.keys(category.Tables ?? {}).map((oracleId, index) => {
+ const oracle = category.Tables?.[oracleId];
+ if (!oracle) return null;
+ return (
+ rollOracleTable(oracle.$id, true, true)}
+ onOpenClick={() => {
+ openDialog(oracle.$id);
+ }}
+ />
+ );
+ })}
+
+
{Object.keys(category.Sets ?? {}).map((oracleSetId) => {
const set = category.Sets?.[oracleSetId];
if (!set) return null;
diff --git a/src/components/features/charactersAndCampaigns/OracleSection/OracleListItem.tsx b/src/components/features/charactersAndCampaigns/OracleSection/OracleListItem.tsx
index 338b8e10..a20f190d 100644
--- a/src/components/features/charactersAndCampaigns/OracleSection/OracleListItem.tsx
+++ b/src/components/features/charactersAndCampaigns/OracleSection/OracleListItem.tsx
@@ -24,8 +24,8 @@ export function OracleListItem(props: OracleListItemProps) {
id={id}
disablePadding
sx={(theme) => ({
- "&:nth-of-type(odd)": {
- backgroundColor: theme.palette.action.hover,
+ "&:nth-of-type(even)": {
+ backgroundColor: theme.palette.background.paperInlay,
},
"& #open-table": {
display: isTouchDevice ? "inline-flex" : "none",
diff --git a/src/components/features/charactersAndCampaigns/OracleSection/OracleSection.tsx b/src/components/features/charactersAndCampaigns/OracleSection/OracleSection.tsx
index 95896a56..4954b4fc 100644
--- a/src/components/features/charactersAndCampaigns/OracleSection/OracleSection.tsx
+++ b/src/components/features/charactersAndCampaigns/OracleSection/OracleSection.tsx
@@ -5,7 +5,7 @@ import { useFilterOracles } from "./useFilterOracles";
import { AskTheOracleButtons } from "./AskTheOracleButtons";
export function OracleSection() {
- const { search, filteredOracles, setSearch } = useFilterOracles();
+ const { isSearchActive, filteredOracles, setSearch } = useFilterOracles();
return (
<>
@@ -34,7 +34,6 @@ export function OracleSection() {
}
aria-label={"Filter Oracles"}
placeholder={"Filter Oracles"}
- value={search}
onChange={(evt) => setSearch(evt.target.value)}
color={"primary"}
sx={(theme) => ({
@@ -46,7 +45,11 @@ export function OracleSection() {
/>
{filteredOracles.map((category, index) => (
-
+
))}
>
diff --git a/src/components/features/charactersAndCampaigns/OracleSection/useFilterOracles.ts b/src/components/features/charactersAndCampaigns/OracleSection/useFilterOracles.ts
index cf2cf197..359a1d17 100644
--- a/src/components/features/charactersAndCampaigns/OracleSection/useFilterOracles.ts
+++ b/src/components/features/charactersAndCampaigns/OracleSection/useFilterOracles.ts
@@ -127,5 +127,5 @@ export function useFilterOracles() {
showDelveOracles,
]);
- return { search, setSearch, filteredOracles };
+ return { isSearchActive: !!search, setSearch, filteredOracles };
}
diff --git a/src/components/shared/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx b/src/components/shared/Layout/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx
similarity index 94%
rename from src/components/shared/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx
rename to src/components/shared/Layout/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx
index e64a0986..e52c2764 100644
--- a/src/components/shared/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx
+++ b/src/components/shared/Layout/AccessibilitySettingsDialog/AccessibilitySettingsDialog.tsx
@@ -7,7 +7,7 @@ import {
DialogContent,
FormControlLabel,
} from "@mui/material";
-import { DialogTitleWithCloseButton } from "../DialogTitleWithCloseButton";
+import { DialogTitleWithCloseButton } from "components/shared/DialogTitleWithCloseButton";
import { useStore } from "stores/store";
export interface AccessibilitySettingsDialogProps {
diff --git a/src/components/shared/Layout/AccessibilitySettingsDialog/index.ts b/src/components/shared/Layout/AccessibilitySettingsDialog/index.ts
new file mode 100644
index 00000000..6bd66360
--- /dev/null
+++ b/src/components/shared/Layout/AccessibilitySettingsDialog/index.ts
@@ -0,0 +1 @@
+export * from "./AccessibilitySettingsDialog";
diff --git a/src/components/shared/Layout/BetaTestsDialog.tsx b/src/components/shared/Layout/BetaTestsDialog.tsx
new file mode 100644
index 00000000..538c03d7
--- /dev/null
+++ b/src/components/shared/Layout/BetaTestsDialog.tsx
@@ -0,0 +1,93 @@
+import {
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ FormControlLabel,
+ LinearProgress,
+ Stack,
+ Switch,
+} from "@mui/material";
+import { DialogTitleWithCloseButton } from "../DialogTitleWithCloseButton";
+import { activeFeatureFlags } from "hooks/featureFlags/activeFeatureFlags";
+import { EmptyState } from "../EmptyState";
+import { useEffect, useRef, useState } from "react";
+import { useStore } from "stores/store";
+
+export interface BetaTestsDialogProps {
+ open: boolean;
+ onClose: () => void;
+}
+
+export function BetaTestsDialog(props: BetaTestsDialogProps) {
+ const { open, onClose } = props;
+
+ const updateBetaTests = useStore((store) => store.appState.updateBetaTests);
+
+ const [testStates, setTestStates] = useState>();
+ const initialTestStates = useRef>();
+
+ useEffect(() => {
+ const forcedGroups: { [groupName: string]: boolean } = JSON.parse(
+ localStorage.getItem("forcedGroups") ?? "{}"
+ );
+ setTestStates(forcedGroups);
+ initialTestStates.current = forcedGroups;
+ }, []);
+
+ const handleTestChange = (testId: string, value: boolean) => {
+ setTestStates((prev) => ({
+ ...prev,
+ [testId]: value,
+ }));
+ };
+
+ const handleSave = () => {
+ if (testStates !== initialTestStates.current && testStates !== undefined) {
+ updateBetaTests(testStates);
+ onClose();
+ } else {
+ onClose();
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/shared/Layout/HeaderMenu.tsx b/src/components/shared/Layout/HeaderMenu.tsx
index c5c18f05..c8ae164b 100644
--- a/src/components/shared/Layout/HeaderMenu.tsx
+++ b/src/components/shared/Layout/HeaderMenu.tsx
@@ -1,5 +1,5 @@
import {
- ButtonBase,
+ IconButton,
ListItemIcon,
ListItemText,
Menu,
@@ -8,8 +8,6 @@ import {
import { useRef, useState } from "react";
import { logout } from "lib/auth.lib";
import LogoutIcon from "@mui/icons-material/Logout";
-import { useStore } from "stores/store";
-import { UserAvatar } from "components/shared/UserAvatar";
import { useToggleTheme } from "providers/ThemeProvider";
import LightThemeIcon from "@mui/icons-material/LightMode";
import DarkThemeIcon from "@mui/icons-material/DarkMode";
@@ -23,11 +21,12 @@ import {
constructCharacterPath,
} from "pages/Character/routes";
import AccessibilityIcon from "@mui/icons-material/AccessibilityNew";
-import { AccessibilitySettingsDialog } from "../AccessibilitySettingsDialog/AccessibilitySettingsDialog";
+import { AccessibilitySettingsDialog } from "./AccessibilitySettingsDialog";
+import SettingsIcon from "@mui/icons-material/Settings";
+import { BetaTestsDialog } from "./BetaTestsDialog";
+import TestsIcon from "@mui/icons-material/AutoAwesome";
export function HeaderMenu() {
- const userId = useStore((store) => store.auth.uid);
-
const [menuOpen, setMenuOpen] = useState(false);
const anchorRef = useRef(null);
@@ -39,18 +38,27 @@ export function HeaderMenu() {
const [accessibilitySettingsOpen, setAccessibilitySettingsOpen] =
useState(false);
+ const [betaTestsOpen, setBetaTestsOpen] = useState(false);
+
return (
<>
- setMenuOpen(true)}
>
-
-
+ ({
+ transform: `rotate(${menuOpen ? "90deg" : "0deg"})`,
+ transition: theme.transitions.create(["transform"], {
+ duration: theme.transitions.duration.shorter,
+ }),
+ })}
+ />
+