这是indexloc提供的服务,不要输入任何密码
Skip to content

feat: toggle publish unpublish documents #421

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

Merged
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 8 additions & 1 deletion functions/src/models/TanamDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ export abstract class TanamDocument<TimestampType, FieldValueType> {
public readonly createdAt?: TimestampType;
public readonly updatedAt?: TimestampType;

abstract get status(): TanamPublishStatus;
get status(): TanamPublishStatus {
if (!this.publishedAt) {
return "unpublished";
} else {
return "published";
}
}

protected abstract getServerTimestamp(): FieldValueType;

toJson(): object {
Expand Down
12 changes: 1 addition & 11 deletions functions/src/models/TanamDocumentAdmin.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import {FieldValue, Timestamp} from "firebase-admin/firestore";
import {ITanamDocument, TanamDocument, TanamPublishStatus} from "./TanamDocument";
import {DocumentSnapshot} from "firebase-functions/v2/firestore";
import {ITanamDocument, TanamDocument} from "./TanamDocument";

export class TanamDocumentAdmin extends TanamDocument<Timestamp, FieldValue> {
constructor(id: string, json: ITanamDocument<Timestamp>) {
super(id, json);
}

get status(): TanamPublishStatus {
if (!this.publishedAt) {
return "unpublished";
} else if (this.publishedAt.toMillis() > Timestamp.now().toMillis()) {
return "scheduled";
} else {
return "published";
}
}

getServerTimestamp(): FieldValue {
return FieldValue.serverTimestamp();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function DocumentDetailsPage() {
const router = useRouter();
const {documentId} = useParams<{documentId: string}>() ?? {};
const {data: document, error: documentError} = useTanamDocument(documentId);
const {update, error: writeError} = useCrudTanamDocument(documentId);
const {update, error: writeError} = useCrudTanamDocument();
const [readonlyMode] = useState<boolean>(false);
const [notification, setNotification] = useState<UserNotification | null>(null);
if (!!document?.documentType && document?.documentType !== "article") {
Expand All @@ -26,7 +26,12 @@ export default function DocumentDetailsPage() {

async function onDocumentContentChange(content: string) {
console.log("[onDocumentContentChange]", content);
await update({data: {...document?.data, content}});
if (!document) {
return;
}

document.data.content = content;
await update(document);
}

return (
Expand Down
12 changes: 7 additions & 5 deletions hosting/src/app/(protected)/content/article/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,26 @@ import Loader from "@/components/common/Loader";
import Notification from "@/components/common/Notification";
import PageHeader from "@/components/common/PageHeader";
import {useTanamDocumentType} from "@/hooks/useTanamDocumentTypes";
import {useCreateTanamDocument, useTanamDocuments} from "@/hooks/useTanamDocuments";
import {useCrudTanamDocument, useTanamDocuments} from "@/hooks/useTanamDocuments";
import {UserNotification} from "@/models/UserNotification";
import {useRouter} from "next/navigation";
import {Suspense, useEffect, useState} from "react";

export default function DocumentTypeDocumentsPage() {
const {data: documentType} = useTanamDocumentType("article");
const {create, error: writeError} = useCreateTanamDocument(documentType?.id);
const {create, error: crudError} = useCrudTanamDocument();
const {data: documents, error: docsError} = useTanamDocuments("article");
const [notification, setNotification] = useState<UserNotification | null>(null);
const router = useRouter();

useEffect(() => {
setNotification(docsError || writeError);
}, [docsError, writeError]);
setNotification(docsError || crudError);
}, [docsError, crudError]);

const addNewArticle = async () => {
const id = await create();
const id = await create(documentType?.id);

if (!id) return;

router.push(`article/${id}`);
};
Expand Down
8 changes: 4 additions & 4 deletions hosting/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ export function Button({title, onClick, style = "normal", color = "primary", chi

switch (color) {
case "primary":
styles.push("bg-primary", "text-white");
styles.push("bg-primary", !style ? "text-white" : "");
break;
case "meta-3":
styles.push("bg-meta-3", "text-white");
styles.push("bg-meta-3", !style ? "text-white" : "");
break;
case "black":
styles.push("bg-black", "text-white");
styles.push("bg-black", !style ? "text-white" : "");
break;
default:
styles.push("bg-primary", "text-white");
styles.push("bg-primary", !style ? "text-white" : "");
}

switch (style) {
Expand Down
4 changes: 4 additions & 0 deletions hosting/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import DarkModeSwitcher from "@/components/Header/DarkModeSwitcher";
import DropdownUser from "@/components/Header/DropdownUser";
import {TogglePublishDocument} from "@/components/TogglePublishDocument";
import {useAuthentication} from "@/hooks/useAuthentication";
import Image from "next/image";
import Link from "next/link";

const Header = (props: {sidebarOpen: string | boolean | undefined; setSidebarOpen: (arg0: boolean) => void}) => {
const {authUser} = useAuthentication();

return (
<header className="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
<div className="flex flex-grow items-center justify-between px-4 py-4 shadow-2 md:px-6 2xl:px-11">
<TogglePublishDocument />

<div className="flex items-center gap-2 sm:gap-4 lg:hidden">
{/* <!-- Hamburger Toggle BTN --> */}
<button
Expand Down
30 changes: 30 additions & 0 deletions hosting/src/components/TogglePublishDocument.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {Button} from "@/components/Button";
import {useTanamDocument} from "@/hooks/useTanamDocuments";
import {useParams} from "next/navigation";

export function TogglePublishDocument() {
const {documentId} = useParams<{documentId: string}>() ?? {};
const {data: document, changeStatus} = useTanamDocument(documentId);

async function onTogglePublishDocument() {
if (!document) {
return;
}

// Toggle the status of the document
return changeStatus(document.status === "published" ? "unpublished" : "published");
}

return (
documentId && (
<>
Copy link
Member

Choose a reason for hiding this comment

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

I don't think you need this empty <>. Please try remove it to confirm.

<Button
title={document?.status === "published" ? "Unpublish" : "Publish"}
onClick={onTogglePublishDocument}
style="outline"
color="primary"
/>
</>
)
);
}
91 changes: 41 additions & 50 deletions hosting/src/hooks/useTanamDocuments.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
"use client";
import {TanamDocumentClient} from "@/models/TanamDocumentClient";
import {UserNotification} from "@/models/UserNotification";
import {firestore} from "@/plugins/firebase";
import {ITanamDocument} from "@functions/models/TanamDocument";
import {
Timestamp,
collection,
doc,
onSnapshot,
query,
serverTimestamp,
setDoc,
updateDoc,
where,
} from "firebase/firestore";
import {TanamPublishStatus} from "@functions/models/TanamDocument";
import {collection, doc, onSnapshot, query, serverTimestamp, setDoc, updateDoc, where} from "firebase/firestore";
import {useEffect, useState} from "react";
import {UserNotification} from "@/models/UserNotification";

interface UseTanamDocumentsResult {
data: TanamDocumentClient[];
Expand Down Expand Up @@ -59,6 +50,7 @@ export function useTanamDocuments(documentTypeId?: string): UseTanamDocumentsRes

interface UseTanamDocumentResult {
data: TanamDocumentClient | null;
changeStatus: (status: TanamPublishStatus) => Promise<void>;
error: UserNotification | null;
}

Expand Down Expand Up @@ -92,67 +84,66 @@ export function useTanamDocument(documentId?: string): UseTanamDocumentResult {
return () => unsubscribe();
}, [documentId]);

return {data, error};
}

export function useCrudTanamDocument(documentId?: string) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<UserNotification | null>(null);
/**
* Method to publish or unpublish a document
*
* @param {TanamPublishStatus} status Flag to publish or unpublish the document
* @return {Promise<void>} Promise
*/
async function changeStatus(status: TanamPublishStatus): Promise<void> {
if (!documentId) {
setError(new UserNotification("error", "Missing parameter", "Document id parameter is missing"));
return;
}

async function update(data: Partial<ITanamDocument<Timestamp>>): Promise<void> {
setIsLoading(true);
try {
if (!documentId) {
setError(new UserNotification("error", "Missing parameter", "Document id parameter is missing"));
return;
}

const typeRef = doc(firestore, "tanam-documents", documentId);
await updateDoc(typeRef, {...data, updatedAt: serverTimestamp()});
await updateDoc(typeRef, {
publishedAt: status === "published" ? serverTimestamp() : null,
status,
} as Partial<TanamDocumentClient>);
} catch (err) {
setError(
new UserNotification(
"error",
"UserNotification updating document",
"An error occurred while updating the document",
),
new UserNotification("error", "Error publishing document", "An error occurred while publishing the document"),
);
} finally {
setIsLoading(false);
}
}
return {update, isLoading, error};

return {data, changeStatus, error};
}

export function useCreateTanamDocument(documentType?: string) {
const [isLoading, setIsLoading] = useState(false);
export function useCrudTanamDocument() {
const [error, setError] = useState<UserNotification | null>(null);

async function create() {
setIsLoading(true);
async function create(documentType?: string) {
Copy link
Member

Choose a reason for hiding this comment

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

Perfect. This is good.

try {
if (!documentType) {
setError(new UserNotification("error", "Missing parameter", "Document id parameter is missing"));
setError(new UserNotification("error", "Missing parameter", "Document type parameter is missing"));
return;
}
const docRef = doc(collection(firestore, "tanam-documents"));
const docId = docRef.id;

const tanamDocument = new TanamDocumentClient(docId, {data: {}, documentType}).toJson();
await setDoc(docRef, tanamDocument);
const tanamDocument = new TanamDocumentClient(docId, {data: {}, documentType});
await setDoc(docRef, tanamDocument.toJson());
return docId;
} catch (err) {
setError(
new UserNotification(
"error",
"UserNotification creating document",
"An error occurred while creating the document",
),
new UserNotification("error", "Error creating document", "An error occurred while creating the document"),
);
}
}

async function update(document: TanamDocumentClient): Promise<void> {
try {
const typeRef = doc(firestore, "tanam-documents", document.id);
await updateDoc(typeRef, document.toJson());
} catch (err) {
setError(
new UserNotification("error", "Error updating document", "An error occurred while updating the document"),
);
} finally {
setIsLoading(false);
}
}

return {create, isLoading, error};
return {error, create, update};
}
12 changes: 1 addition & 11 deletions hosting/src/models/TanamDocumentClient.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import {ITanamDocument, TanamDocument, TanamPublishStatus} from "@functions/models/TanamDocument";
import {ITanamDocument, TanamDocument} from "@functions/models/TanamDocument";
import {DocumentSnapshot, FieldValue, serverTimestamp, Timestamp} from "firebase/firestore";

export class TanamDocumentClient extends TanamDocument<Timestamp, FieldValue> {
constructor(id: string, json: ITanamDocument<Timestamp>) {
super(id, json);
}

get status(): TanamPublishStatus {
if (!this.publishedAt) {
return "unpublished";
} else if (this.publishedAt.toMillis() > Timestamp.now().toMillis()) {
return "scheduled";
} else {
return "published";
}
}

getServerTimestamp(): FieldValue {
return serverTimestamp();
}
Expand Down