Implement publishing.
rodzic
c26d9ef818
commit
5bee62881b
|
@ -17,10 +17,15 @@ import {
|
|||
orderBy,
|
||||
CollectionReference,
|
||||
Timestamp,
|
||||
addDoc,
|
||||
} from "firebase/firestore";
|
||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { AuthContext } from "./auth-context";
|
||||
import { GalleryComposition, GalleryContext } from "./gallery-context";
|
||||
import {
|
||||
GalleryComposition,
|
||||
GalleryContext,
|
||||
GallerySubmitStatus,
|
||||
} from "./gallery-context";
|
||||
|
||||
const GALLERY_COLLECTION = "compositions";
|
||||
|
||||
|
@ -123,23 +128,59 @@ function getGalleryCollection(appCtx: FirebaseAppContext) {
|
|||
) as CollectionReference<FirebaseCompositionDocument>;
|
||||
}
|
||||
|
||||
function docToComp(
|
||||
doc: FirebaseCompositionDocument,
|
||||
id: string
|
||||
): GalleryComposition {
|
||||
const { createdAt, ...data } = doc;
|
||||
return {
|
||||
...data,
|
||||
id,
|
||||
createdAt: createdAt.toDate(),
|
||||
};
|
||||
}
|
||||
|
||||
export const FirebaseGalleryProvider: React.FC<{}> = ({ children }) => {
|
||||
const appCtx = useContext(FirebaseAppContext);
|
||||
const [compositions, setCompositions] = useState<GalleryComposition[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [lastRefresh, setLastRefresh] = useState(0);
|
||||
|
||||
const handleError = (e: Error) => {
|
||||
setIsLoading(false);
|
||||
setError(e.message);
|
||||
};
|
||||
const [submitStatus, setSubmitStatus] = useState<GallerySubmitStatus>("idle");
|
||||
const [lastSubmission, setLastSubmission] = useState<
|
||||
GalleryComposition | undefined
|
||||
>(undefined);
|
||||
|
||||
const context: GalleryContext = {
|
||||
compositions,
|
||||
isLoading,
|
||||
error,
|
||||
lastRefresh,
|
||||
lastSubmission,
|
||||
submitStatus,
|
||||
submit(props, onSuccess) {
|
||||
if (!(appCtx && submitStatus === "idle")) return;
|
||||
|
||||
const doc: FirebaseCompositionDocument = {
|
||||
...props,
|
||||
createdAt: Timestamp.now(),
|
||||
};
|
||||
|
||||
setSubmitStatus("submitting");
|
||||
setLastSubmission(undefined);
|
||||
addDoc(getGalleryCollection(appCtx), doc)
|
||||
.then((docRef) => {
|
||||
const comp = docToComp(doc, docRef.id);
|
||||
setSubmitStatus("idle");
|
||||
setCompositions([comp, ...compositions]);
|
||||
setLastSubmission(comp);
|
||||
onSuccess(docRef.id);
|
||||
})
|
||||
.catch((e) => {
|
||||
setSubmitStatus("error");
|
||||
console.log(e);
|
||||
});
|
||||
},
|
||||
refresh: useCallback(() => {
|
||||
if (!(appCtx && !isLoading)) return false;
|
||||
|
||||
|
@ -150,17 +191,13 @@ export const FirebaseGalleryProvider: React.FC<{}> = ({ children }) => {
|
|||
setLastRefresh(Date.now());
|
||||
setIsLoading(false);
|
||||
setCompositions(
|
||||
snapshot.docs.map((doc) => {
|
||||
const { createdAt, ...data } = doc.data();
|
||||
return {
|
||||
...data,
|
||||
id: doc.id,
|
||||
createdAt: createdAt.toDate(),
|
||||
};
|
||||
})
|
||||
snapshot.docs.map((doc) => docToComp(doc.data(), doc.id))
|
||||
);
|
||||
})
|
||||
.catch(handleError);
|
||||
.catch((e) => {
|
||||
setIsLoading(false);
|
||||
setError(e.message);
|
||||
});
|
||||
return true;
|
||||
}, [appCtx, isLoading]),
|
||||
};
|
||||
|
|
|
@ -2,6 +2,8 @@ import React from "react";
|
|||
|
||||
export type GalleryCompositionKind = "creature" | "mandala";
|
||||
|
||||
export type GallerySubmitStatus = "idle" | "submitting" | "error";
|
||||
|
||||
export type GalleryComposition = {
|
||||
/** A unique identifier/primary key for the composition. */
|
||||
id: string;
|
||||
|
@ -41,6 +43,15 @@ export interface GalleryContext {
|
|||
*/
|
||||
compositions: GalleryComposition[];
|
||||
|
||||
submitStatus: GallerySubmitStatus;
|
||||
|
||||
submit(
|
||||
composition: Omit<GalleryComposition, "id" | "createdAt">,
|
||||
onSuccess: (id: string) => void
|
||||
): void;
|
||||
|
||||
lastSubmission?: GalleryComposition;
|
||||
|
||||
/** Whether we're currently loading the gallery from the network. */
|
||||
isLoading: boolean;
|
||||
|
||||
|
@ -67,5 +78,7 @@ export const GalleryContext = React.createContext<GalleryContext>({
|
|||
compositions: [],
|
||||
isLoading: false,
|
||||
refresh: () => true,
|
||||
submitStatus: "idle",
|
||||
submit: () => {},
|
||||
lastRefresh: 0,
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { assertNotNull } from "@justfixnyc/util";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { AuthContext } from "./auth-context";
|
||||
import { GalleryCompositionKind } from "./gallery-context";
|
||||
import { GalleryCompositionKind, GalleryContext } from "./gallery-context";
|
||||
|
||||
export type GalleryWidgetProps = {
|
||||
kind: GalleryCompositionKind;
|
||||
|
@ -44,29 +45,73 @@ const LoginWidget: React.FC<{}> = () => {
|
|||
|
||||
const PublishWidget: React.FC<GalleryWidgetProps> = (props) => {
|
||||
const authCtx = useContext(AuthContext);
|
||||
const user = assertNotNull(authCtx.loggedInUser, "User must be logged in");
|
||||
const galleryCtx = useContext(GalleryContext);
|
||||
const [title, setTitle] = useState("");
|
||||
const [publishedId, setPublishedId] = useState("");
|
||||
const handlePublish = () => {
|
||||
const serializedValue = props.serializeValue();
|
||||
console.log("TODO: Publish", props.kind, serializedValue);
|
||||
galleryCtx.submit(
|
||||
{
|
||||
title,
|
||||
kind: props.kind,
|
||||
serializedValue: props.serializeValue(),
|
||||
owner: user.id,
|
||||
ownerName: user.name,
|
||||
},
|
||||
setPublishedId
|
||||
);
|
||||
};
|
||||
const isSubmitting = galleryCtx.submitStatus === "submitting";
|
||||
|
||||
if (!authCtx.providerName) return null;
|
||||
if (galleryCtx.lastSubmission?.id === publishedId) {
|
||||
return (
|
||||
<>
|
||||
<p>Your composition "{title}" has been published!</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
setPublishedId("");
|
||||
setTitle("");
|
||||
}}
|
||||
>
|
||||
I want to publish more!
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Here you can publish your composition to our publicly-viewable gallery.
|
||||
</p>
|
||||
<div className="flex-widget thingy">
|
||||
<label htmlFor="gallery-title">Composition title:</label>
|
||||
<input
|
||||
id="gallery-title"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button onClick={handlePublish}>Publish to gallery</button> <AuthWidget />
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handlePublish();
|
||||
}}
|
||||
>
|
||||
<div className="flex-widget thingy">
|
||||
<label htmlFor="gallery-title">Composition title:</label>
|
||||
<input
|
||||
id="gallery-title"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" disabled={isSubmitting}>
|
||||
Publish to gallery
|
||||
</button>{" "}
|
||||
{!isSubmitting && <AuthWidget />}
|
||||
{galleryCtx.submitStatus === "error" && (
|
||||
<p className="error">
|
||||
Sorry, an error occurred while submitting your composition. Please
|
||||
try again later.
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"@babel/preset-react": "^7.13.13",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/register": "^7.12.10",
|
||||
"@justfixnyc/util": "^0.3.0",
|
||||
"@types/cheerio": "^0.22.23",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^14.14.22",
|
||||
|
@ -2904,6 +2905,11 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@justfixnyc/util": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@justfixnyc/util/-/util-0.3.0.tgz",
|
||||
"integrity": "sha512-Iuj6x5PPhtaHFNLYfZoIOFIwV4ysPGt7EapgfO8hYlQprHRcZfxBXb51ZHmWx3I7Ak5VZLgOLc8/HLFkZjWKlw=="
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
@ -13468,6 +13474,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@justfixnyc/util": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@justfixnyc/util/-/util-0.3.0.tgz",
|
||||
"integrity": "sha512-Iuj6x5PPhtaHFNLYfZoIOFIwV4ysPGt7EapgfO8hYlQprHRcZfxBXb51ZHmWx3I7Ak5VZLgOLc8/HLFkZjWKlw=="
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"@babel/preset-react": "^7.13.13",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/register": "^7.12.10",
|
||||
"@justfixnyc/util": "^0.3.0",
|
||||
"@types/cheerio": "^0.22.23",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^14.14.22",
|
||||
|
|
Ładowanie…
Reference in New Issue