Skip to content

Commit

Permalink
Merge pull request #350 from scottbenton/feat/add-rules-definition
Browse files Browse the repository at this point in the history
Feat/add rules definition
  • Loading branch information
scottbenton authored Dec 18, 2023
2 parents a8dfbaf + d0acf00 commit 3ef4c8d
Show file tree
Hide file tree
Showing 55 changed files with 2,620 additions and 290 deletions.
10 changes: 10 additions & 0 deletions firestore.rules
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ service cloud.firestore {
allow read: if request.auth.uid != null;
allow create: if request.auth.uid != null;
allow write: if request.auth.uid in resource.data.uids;

function checkIsHomebrewOwner() {
let world = get(/databases/$(database)/documents/homebrew/$(homebrewId)).data;
return request.auth.uid in world.ownerIds;
}

match /rules/rules {
allow read: if request.auth.uid != null;
allow write: if checkIsHomebrewOwner();
}
}
match /users/{userId} {
allow read: if true;
Expand Down
525 changes: 288 additions & 237 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
},
"dependencies": {
"@datasworn/core": "^0.0.5",
"@datasworn/ironsworn-classic": "^0.0.5",
"@datasworn/starforged": "^0.0.5",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource/bebas-neue": "^5.0.12",
Expand All @@ -20,14 +22,16 @@
"@mui/icons-material": "^5.14.15",
"@mui/lab": "^5.0.0-alpha.150",
"@mui/material": "^5.15.0",
"@mui/x-date-pickers": "^6.18.4",
"@tiptap/extension-collaboration": "^2.0.3",
"@tiptap/extension-collaboration-cursor": "^2.0.3",
"@tiptap/extension-placeholder": "^2.0.0-beta.218",
"@tiptap/pm": "^2.0.0-beta.218",
"@tiptap/react": "^2.0.0-beta.218",
"@tiptap/starter-kit": "^2.0.0-beta.218",
"dataforged": "^2.0.0-2",
"firebase": "^9.17.1",
"dayjs": "^1.11.5",
"firebase": "^10.7.1",
"formik": "^2.2.9",
"immer": "^9.0.17",
"material-ui-confirm": "^3.0.8",
Expand All @@ -38,11 +42,13 @@
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-hook-form": "^7.49.2",
"react-markdown": "^8.0.4",
"react-router-dom": "^6.6.1",
"react-transition-group": "^4.4.5",
"react-virtuoso": "^4.5.0",
"remark-gfm": "^3.0.1",
"tiptap-markdown": "^0.8.8",
"vite-plugin-svgr": "^2.4.0",
"vite-tsconfig-paths": "^4.0.5",
"y-prosemirror": "1.0.20",
Expand All @@ -62,7 +68,7 @@
"eslint": "^8.54.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"typescript": "^4.9.3",
"typescript": "^5.3.3",
"vite": "^4.0.0"
}
}
1 change: 0 additions & 1 deletion src/api-calls/assets/updateAssetCheckbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const updateAssetCheckbox = createApiFunction<
characterId
? getCharacterAssetDoc(characterId, assetId)
: getCampaignAssetDoc(campaignId as string, assetId),
//@ts-expect-error - typescript doesn't like this notation
{
[`enabledAbilities.${abilityIndex}`]: checked,
}
Expand Down
1 change: 0 additions & 1 deletion src/api-calls/assets/updateAssetCondition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const updateAssetCondition = createApiFunction<
characterId
? getCharacterAssetDoc(characterId, assetId)
: getCampaignAssetDoc(campaignId as string, assetId),
// @ts-expect-error - typescript doesn't like this notation
{
[`conditions.${condition}`]: checked,
}
Expand Down
1 change: 0 additions & 1 deletion src/api-calls/assets/updateAssetInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const updateAssetInput = createApiFunction<
characterId
? getCharacterAssetDoc(characterId, assetId)
: getCampaignAssetDoc(campaignId as string, assetId),
// @ts-expect-error Typescript doesn't like this notation, but it works
{
[`inputs.${inputLabel}`]: inputValue,
}
Expand Down
12 changes: 6 additions & 6 deletions src/api-calls/game-log/updateLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export const updateLog = createApiFunction<
reject(new Error("Either campaign or character ID must be defined."));
}

updateDoc(
campaignId
? getCampaignGameLogDocument(campaignId, logId)
: getCharacterGameLogDocument(characterId as string, logId),
log
)
const docRef = campaignId
? getCampaignGameLogDocument(campaignId, logId)
: getCharacterGameLogDocument(characterId as string, logId);

// @ts-expect-error not sure why log is incorrect here
updateDoc(docRef, log)
.then(() => {
resolve();
})
Expand Down
22 changes: 22 additions & 0 deletions src/api-calls/homebrew/deleteHomebrewExpansion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { deleteDoc } from "firebase/firestore";
import { getHomebrewCollectionDoc } from "./_getRef";
import { createApiFunction } from "api-calls/createApiFunction";

export const deleteHomebrewExpansion = createApiFunction<{ id: string }, void>(
(params) => {
const { id } = params;

return new Promise((resolve, reject) => {
const promises: Promise<unknown>[] = [];

promises.push(deleteDoc(getHomebrewCollectionDoc(id)));

Promise.all(promises)
.then(() => {
resolve();
})
.catch(reject);
});
},
"Failed to delete expansion."
);
15 changes: 15 additions & 0 deletions src/api-calls/homebrew/rules/_getRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DocumentReference, doc } from "firebase/firestore";
import { constructHomebrewCollectionDocPath } from "../_getRef";
import { firestore } from "config/firebase.config";
import { StoredRules } from "types/HomebrewCollection.type";

export function constructHomebrewRulesDocPath(homebrewId: string) {
return `${constructHomebrewCollectionDocPath(homebrewId)}/rules/rules`;
}

export function getHomebrewRulesDoc(homebrewId: string) {
return doc(
firestore,
constructHomebrewRulesDocPath(homebrewId)
) as DocumentReference<Partial<StoredRules>>;
}
21 changes: 21 additions & 0 deletions src/api-calls/homebrew/rules/createHomebrewRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createApiFunction } from "api-calls/createApiFunction";
import { PartialWithFieldValue, updateDoc } from "firebase/firestore";
import { getHomebrewRulesDoc } from "./_getRef";
import { StoredRules } from "types/HomebrewCollection.type";

export const updateHomebrewRules = createApiFunction<
{
homebrewId: string;
rules: PartialWithFieldValue<Partial<StoredRules>>;
},
void
>((params) => {
const { homebrewId, rules } = params;
return new Promise((resolve, reject) => {
updateDoc(getHomebrewRulesDoc(homebrewId), rules)
.then(() => {
resolve();
})
.catch(reject);
});
}, "Failed to update rules.");
26 changes: 26 additions & 0 deletions src/api-calls/homebrew/rules/listenToHomebrewRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { onSnapshot } from "firebase/firestore";
import { StoredRules } from "types/HomebrewCollection.type";
import { getHomebrewRulesDoc } from "./_getRef";

export function listenToHomebrewRules(
homebrewId: string,
updateRules: (collectionId: string, rules: Partial<StoredRules>) => void,
onError: (error: unknown) => void,
onLoaded: () => void
) {
return onSnapshot(
getHomebrewRulesDoc(homebrewId),
(snapshot) => {
const doc = snapshot.data();
if (doc) {
updateRules(homebrewId, doc);
} else {
onLoaded();
}
},
(error) => {
console.error(error);
onError(error);
}
);
}
21 changes: 21 additions & 0 deletions src/api-calls/homebrew/rules/updateExpansionRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createApiFunction } from "api-calls/createApiFunction";
import { PartialWithFieldValue, setDoc } from "firebase/firestore";
import { getHomebrewRulesDoc } from "./_getRef";
import { StoredRules } from "types/HomebrewCollection.type";

export const updateExpansionRules = createApiFunction<
{
homebrewId: string;
rules: PartialWithFieldValue<StoredRules>;
},
void
>((params) => {
const { homebrewId, rules } = params;
return new Promise((resolve, reject) => {
setDoc(getHomebrewRulesDoc(homebrewId), rules, { merge: true })
.then(() => {
resolve();
})
.catch(reject);
});
}, "Failed to update rules.");
17 changes: 17 additions & 0 deletions src/api-calls/homebrew/updateHomebrewExpansion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { updateDoc } from "firebase/firestore";
import { getHomebrewCollectionDoc } from "./_getRef";
import { BaseExpansion } from "types/HomebrewCollection.type";
import { createApiFunction } from "api-calls/createApiFunction";

export const updateHomebrewExpansion = createApiFunction<
{ id: string; expansion: Partial<BaseExpansion> },
void
>((params) => {
const { id, expansion } = params;

return new Promise((resolve, reject) => {
updateDoc(getHomebrewCollectionDoc(id), expansion)
.then(() => resolve())
.catch(reject);
});
}, "Failed to update expansion.");
11 changes: 8 additions & 3 deletions src/components/features/Track.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface TrackProps {
min: number;
max: number;
value: number;
onChange: (newValue: number) => Promise<boolean | void>;
onChange?: (newValue: number) => Promise<boolean | void>;
sx?: SxProps<Theme>;
disabled?: boolean;
}
Expand All @@ -32,7 +32,7 @@ export function Track(props: TrackProps) {

const [localValue, setLocalValue] = useDebouncedState(
(newValue) => {
if (newValue !== value) {
if (newValue !== value && onChange) {
hasUnsavedChangesRef.current = false;
onChange(newValue).catch(() => setLocalValue(value));
}
Expand All @@ -42,7 +42,12 @@ export function Track(props: TrackProps) {
);

const handleChange = (newValue: number | undefined) => {
if (typeof newValue === "number" && newValue >= min && newValue <= max) {
if (
onChange &&
typeof newValue === "number" &&
newValue >= min &&
newValue <= max
) {
hasUnsavedChangesRef.current = true;
setLocalValue(newValue);
}
Expand Down
23 changes: 23 additions & 0 deletions src/components/shared/ClampedMarkdownRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Box } from "@mui/material";
import { MarkdownRenderer, MarkdownRendererProps } from "./MarkdownRenderer";

export interface ClampedMarkdownRendererProps extends MarkdownRendererProps {
maxLines?: number;
}

export function ClampedMarkdownRenderer(props: ClampedMarkdownRendererProps) {
const { maxLines = 1, ...markdownRendererProps } = props;
return (
<Box
component={"span"}
sx={{
display: "-webkit-box",
WebkitBoxOrient: "vertical",
WebkitLineClamp: maxLines,
overflow: "hidden",
}}
>
<MarkdownRenderer {...markdownRendererProps} />
</Box>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export function HomebrewMenu() {
>
<ListItemText
primary={
homebrewCollections[collectionId].title ?? "Unnamed Collection"
homebrewCollections[collectionId].base.title ??
"Unnamed Collection"
}
/>
</ListItemButton>
Expand Down
12 changes: 10 additions & 2 deletions src/components/shared/RichTextEditor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ export interface EditorProps {
editor: TTEditor | null;
saving?: boolean;
toolbar?: ReactNode;
minHeight?: number;
}

export function Editor(props: EditorProps) {
const { outlined, editable, editor, toolbar, saving } = props;
const {
outlined,
editable,
editor,
toolbar,
saving,
minHeight = 300,
} = props;

return (
<Box
Expand Down Expand Up @@ -58,7 +66,7 @@ export function Editor(props: EditorProps) {
overflowX: "hidden",
...(outlined
? {
minHeight: editable ? "300px" : 0,
minHeight: editable ? `${minHeight}px` : 0,
display: "flex",
flexDirection: "column",
}
Expand Down
63 changes: 63 additions & 0 deletions src/components/shared/RichTextEditor/MarkdownEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { Markdown } from "tiptap-markdown";
import { Editor } from "./Editor";
import { Box, Typography } from "@mui/material";
import { MarkdownEditorToolbar } from "./MarkdownEditorToolbar";
export interface MarkdownEditorProps {
label: string;
content: string;
onChange: (markdown: string) => void;
onBlur: () => void;
}

export function MarkdownEditor(props: MarkdownEditorProps) {
const { label, content, onChange, onBlur } = props;

const editor = useEditor(
{
extensions: [StarterKit, Markdown],
content,
onBlur,
onUpdate: ({ editor }) => {
const markdown = editor.storage.markdown.getMarkdown();
onChange(markdown);
},
},
[]
);

return (
<Editor
toolbar={
<>
<Box
display={"flex"}
position={"relative"}
sx={(theme) => ({
top: theme.spacing(-1.5),
left: theme.spacing(1),
})}
>
<Typography
component={"span"}
variant={"caption"}
color={"text.secondary"}
sx={{
px: 1,
bgcolor: "background.paper",
}}
>
{label}
</Typography>
</Box>
<MarkdownEditorToolbar editor={editor} />
</>
}
editor={editor}
editable
outlined
minHeight={100}
/>
);
}
Loading

0 comments on commit 3ef4c8d

Please sign in to comment.