Calendar
This demo shows the real-time collaborative version of a Calendar with Yorkie and Next.js.
page.tsx
1/**2 * yorkie-js-sdk must be loaded on client-side3 */4'use client';56import styles from './styles/page.module.css';7import React, { useEffect, useState } from 'react';89import { ContentTypes, ENVtypes } from './utils/types';10import { displayPeers, createRandomPeers } from './utils/handlePeers';11import { parseDate } from './utils/parseDate';12import yorkie, { Document, JSONArray, DocEventType } from '@yorkie-js/sdk';13import Scheduler from './Scheduler';1415// parseDate() value's format = "DD-MM-YYYY"16const defaultContent = [17 {18 date: parseDate(new Date()).replace(/^\d{2}/, '01'),19 text: 'payday',20 },21 {22 date: parseDate(new Date()).replace(/^\d{2}/, '17'),23 text: "Garry's birthday",24 },25] as JSONArray<ContentTypes>;2627const ENV: ENVtypes = {28 url: process.env.NEXT_PUBLIC_YORKIE_API_ADDR!,29 apiKey: process.env.NEXT_PUBLIC_YORKIE_API_KEY!,30};3132const documentKey = `next.js-Scheduler-${parseDate(new Date())}`;3334/**35 * main page36 */37export default function Editor() {38 const [peers, setPeers] = useState<Array<string>>([]);39 const [content, setContent] = useState<Array<ContentTypes>>(defaultContent);4041 // create Yorkie Document with useState value42 const [doc] = useState<Document<{ content: JSONArray<ContentTypes> }>>(43 () =>44 new yorkie.Document<{ content: JSONArray<ContentTypes> }>(documentKey),45 );4647 const actions = {48 // push new content to Yorkie's database49 addContent(date: string, text: string) {50 doc.update((root) => {51 root.content.push({ date, text });52 });53 },5455 // delete selected content at Yorkie's database56 deleteContent(date: string) {57 doc.update((root) => {58 const idx = root.content.findIndex((item) => item.date === date);59 if (idx !== -1) {60 root.content.delete?.(idx);61 }62 });63 },6465 // edit selected content at Yorkie's database66 updateContent(date: string, text: string) {67 doc.update((root) => {68 let target;69 for (const item of root.content) {70 if (item.date === date) {71 target = item;72 break;73 }74 }7576 if (target) {77 target.text = text;78 }79 });80 },81 };8283 useEffect(() => {84 // create Yorkie Client at client-side85 const client = new yorkie.Client({86 rpcAddr: ENV.url,87 apiKey: ENV.apiKey,88 });8990 // subscribe document event of "PresenceChanged"(="peers-changed")91 doc.subscribe('presence', (event) => {92 if (event.type !== DocEventType.PresenceChanged) {93 setPeers(displayPeers(doc.getPresences()));94 }95 });9697 /**98 * `attachDoc` is a helper function to attach the document into the client.99 */100 async function attachDoc(101 doc: Document<{ content: JSONArray<ContentTypes> }>,102 callback: (props: JSONArray<ContentTypes>) => void,103 ) {104 // 01. activate client105 await client.activate();106 // 02. attach the document into the client with presence107 await client.attach(doc, {108 initialPresence: {109 userName: createRandomPeers(),110 },111 });112113 // 03. create default content if not exists.114 doc.update((root) => {115 if (!root.content) {116 root.content = defaultContent;117 }118 }, 'create default content if not exists');119120 // 04. subscribe doc's change event from local and remote.121 doc.subscribe(() => {122 callback(doc.getRoot().content);123 });124125 // 05. set content to the attached document.126 callback(doc.getRoot().content);127 }128129 attachDoc(doc, (content) => setContent(content));130 }, []);131132 return (133 <main className={styles.main}>134 <header>135 <div className={styles.peersRow} aria-label="Active collaborators">136 {peers.length === 0 && (137 <span className={styles.muted}>No peers connected</span>138 )}139 {peers.map((p, i) => (140 <span className={styles.peerChip} key={i}>141 {p}142 </span>143 ))}144 </div>145 </header>146 <Scheduler content={content} actions={actions} />147 </main>148 );149}
- User 1
- User 2
- User 1
- User 2