From 0224b1ff8d1763b4328a82567971e61b41984a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludv=C3=ADk=20Prokopec?= Date: Thu, 8 Dec 2022 12:39:47 +0100 Subject: [PATCH] Files observing --- src/lib/database.ts | 198 +++++++++++++++++-------------------- src/lib/storage.ts | 116 ++++++++++++++++++++++ src/lib/stores/appwrite.ts | 9 +- 3 files changed, 216 insertions(+), 107 deletions(-) create mode 100644 src/lib/storage.ts diff --git a/src/lib/database.ts b/src/lib/database.ts index 1b3dd5e..8961529 100644 --- a/src/lib/database.ts +++ b/src/lib/database.ts @@ -4,107 +4,6 @@ import { Models, Query, RealtimeResponseEvent } from 'appwrite' import { ID } from 'appwrite' import type { Writable } from 'svelte/store' -const subscribeUpdate = (databaseId: string, collectionId: string, store: Writable, document: Models.Document) => { - client.subscribe(`databases.${databaseId}.collections.${collectionId}.documents.${document.$id}`, (response: RealtimeResponseEvent) => { - - if (response.events.includes(`databases.${databaseId}.collections.${collectionId}.documents.${document.$id}.delete`)) { - store.update(current => { - current.splice(current.indexOf(document), 1) - return current - }) - return - } - - if (response.events.includes(`databases.${databaseId}.collections.${collectionId}.documents.${document.$id}.update`)) { - store.update(current => { - current[current.indexOf(document)] = response.payload - return current - }) - return - } - - }) -} - -const subscribeInsert = (databaseId: string, collectionId: string, store: Writable) => { - client.subscribe(`databases.${databaseId}.collections.${collectionId}.documents`, (response: RealtimeResponseEvent) => { - if (response.events.includes(`databases.${databaseId}.collections.${collectionId}.documents.*.create`)) { - let lastIndex = 0 - - store.update(current => { - current.push(response.payload) - lastIndex = current.length - 1 - return current - }) - - subscribeUpdate(databaseId, collectionId, store, response.payload) - } - }) -} - -/** - * Subscribe collection insert - * @param databaseId - * @param collectionId - * @returns - */ -const observe = (databaseId: string, collectionId: string) => { - const dataStore = writable([]) - subscribeInsert(databaseId, collectionId, dataStore) - return { subscribe: dataStore.subscribe } -} - - -/** - * Subscribe collection update, delete - * @param databaseId - * @param collectionId - * @param queries - * @returns - */ -const subscribe = (databaseId: string, collectionId: string, queries: string[]) => { - const loadingStore = writable(true) - const dataStore = writable([]) - - databases.listDocuments(databaseId, collectionId, queries).then(data => { - data.documents.forEach((document) => subscribeUpdate(databaseId, collectionId, dataStore, document)) - - dataStore.set(data.documents) - loadingStore.set(false) - }) - - return [{ subscribe: dataStore.subscribe }, { subscribe: loadingStore.subscribe }] as const -} - -/** - * Paginate collection - * @param databaseId - * @param collectionId - * @param limit - * @returns - */ -const paginate = (databaseId: string, collectionId: string, limit: number, queries: string[] = []) => { - const dataStore = writable([]) - const loadingStore = writable(true) - let offset = 0 - - const store = { - subscribe: dataStore.subscribe, - async next() { - await databases.listDocuments(databaseId, collectionId, [...queries, Query.limit(limit), Query.offset(offset)]).then(data => { - data.documents.forEach((document) => subscribeUpdate(databaseId, collectionId, dataStore, document)) - - dataStore.update(current => [...current, ...data.documents]) - offset += limit - }) - } - } - - store.next().then(() => loadingStore.set(false)) - - return [store, { subscribe: loadingStore.subscribe }] as const -} - class Collection { constructor(protected databaseId: string, protected collectionId: string) { } @@ -121,16 +20,105 @@ class Collection { } createObserver() { - return observe(this.databaseId, this.collectionId) + const dataStore = writable([]) + + client.subscribe(`databases.${this.databaseId}.collections.${this.collectionId}.documents`, (response: RealtimeResponseEvent) => { + if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.*.create`)) { + dataStore.update(current => { + current.push(response.payload) + return current + }) + + this.subscribeCollectionUpdate(response.payload, dataStore) + } + }) + + return { subscribe: dataStore.subscribe } } createSubscriber(queries: string[] = []) { - return subscribe(this.databaseId, this.collectionId, queries) + const loadingStore = writable(true) + const dataStore = writable([]) + + databases.listDocuments(this.databaseId, this.collectionId, queries).then(data => { + data.documents.forEach((document) => this.subscribeCollectionUpdate(document, dataStore)) + + dataStore.set(data.documents) + loadingStore.set(false) + }) + + return [{ subscribe: dataStore.subscribe }, { subscribe: loadingStore.subscribe }] as const } createPaginate(limit: number, queries: string[] = []) { - return paginate(this.databaseId, this.collectionId, limit, queries) + const dataStore = writable([]) + const loadingStore = writable(true) + let offset = 0 + + const store = { + subscribe: dataStore.subscribe, + async next() { + await databases.listDocuments(this.databaseId, this.collectionId, [...queries, Query.limit(limit), Query.offset(offset)]).then(data => { + data.documents.forEach((document) => this.subscribeCollectionUpdate(document, dataStore)) + + dataStore.update(current => [...current, ...data.documents]) + offset += limit + }) + } + } + + store.next().then(() => loadingStore.set(false)) + + return [store, { subscribe: loadingStore.subscribe }] as const + } + + protected subscribeCollectionUpdate(document: Models.Document, store: Writable) { + client.subscribe(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${document.$id}`, (response: RealtimeResponseEvent) => { + if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${document.$id}.delete`)) { + store.update(current => { + current.splice(current.indexOf(document), 1) + return current + }) + return + } + + if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${document.$id}.update`)) { + store.update(current => { + current[current.indexOf(document)] = response.payload + return current + }) + return + } + }) } } -export default Collection +class Document { + constructor(protected databaseId: string, protected collectionId: string, protected documentId: string) { } + + createSubscriber() { + const dataStore = writable(null) + const loadingStore = writable(true) + + databases.getDocument(this.databaseId, this.collectionId, this.documentId).then(data => { + dataStore.set(data) + loadingStore.set(false) + }) + + client.subscribe(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${this.documentId}`, (response: RealtimeResponseEvent) => { + if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${this.documentId}.update`)) { + dataStore.set(response.payload) + return + } + + if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${this.documentId}.delete`)) { + dataStore.set(null) + return + } + }) + + return [{ subscribe: dataStore.subscribe }, { subscribe: loadingStore.subscribe }] as const + } +} + +export { Collection, Document } diff --git a/src/lib/storage.ts b/src/lib/storage.ts new file mode 100644 index 0000000..4e8e907 --- /dev/null +++ b/src/lib/storage.ts @@ -0,0 +1,116 @@ +import { storage, client } from './stores/appwrite' +import { ID, Models, RealtimeResponseEvent } from 'appwrite' +import { Writable, writable } from 'svelte/store' + +class Storage { + constructor(protected bucketId: string) { } + + createFile(file, permissions: string[] = []) { + return storage.createFile(this.bucketId, ID.unique(), file, permissions) + } + + deleteFile(fileId: string) { + return storage.deleteFile(this.bucketId, fileId) + } + + updateFile(fileId: string, permissions: string[] = []) { + return storage.updateFile(this.bucketId, fileId, permissions) + } + + createUploadDispatcher(acceptManyFiles = false) { + return (node: HTMLInputElement) => { + const eventListener = (e) => { + const files = acceptManyFiles ? e.target.files : [e.target.files[0]] + files.forEach((file) => this.createFile(file)) + } + + node.addEventListener('change', eventListener) + + return { + destroy() { + node.removeEventListener('change', eventListener) + }, + } + } + } + + createSubsciber(queries: string[] = [], search = '') { + const filesStore = writable([]) + const loadingStore = writable(true) + + storage.listFiles(this.bucketId, queries, search).then(files => { + for (const file of files.files) { + this.subscribeFileUpdate(file, filesStore) + } + }) + + return [{ subscribe: filesStore.subscribe }, { subscribe: loadingStore.subscribe }] as const + } + + createObserver() { + const dataStore = writable([]) + + client.subscribe(`buckets.${this.bucketId}.files`, (response: RealtimeResponseEvent) => { + if (response.events.includes(`buckets.${this.bucketId}.files.*.create`)) { + dataStore.update(current => { + current.push(response.payload) + return current + }) + + this.subscribeFileUpdate(response.payload, dataStore) + } + }) + + return { subscribe: dataStore.subscribe } + } + + protected subscribeFileUpdate(file: Models.File, filesStore: Writable) { + client.subscribe(`buckets.${this.bucketId}.files.${file.$id}`, (response: RealtimeResponseEvent) => { + if (response.events.includes(`buckets.${this.bucketId}.files.${file.$id}.update`)) { + filesStore.update(current => { + current[current.indexOf(file)] = file + return current + }) + return + } + + if (response.events.includes(`buckets.${this.bucketId}.files.${file.$id}.delete`)) { + filesStore.update(current => { + current.splice(current.indexOf(file), 1) + return current + }) + return + } + }) + } +} + +class File { + constructor(protected bucketId: string, protected fileId: string) { } + + createSubscriber() { + const fileStore = writable(null) + const loadingStore = writable(true) + + storage.getFile(this.bucketId, this.fileId).then((result) => { + fileStore.set(result) + loadingStore.set(false) + }) + + client.subscribe(`buckets.${this.bucketId}.files.${this.fileId}`, (response: RealtimeResponseEvent) => { + if (response.events.includes(`buckets.${this.bucketId}.files.${this.fileId}.update`)) { + fileStore.set(response.payload) + return + } + + if (response.events.includes(`buckets.${this.bucketId}.files.${this.fileId}.delete`)) { + fileStore.set(null) + return + } + }) + + return [{ subscribe: fileStore.subscribe }, { subscribe: loadingStore.subscribe }] as const + } +} + +export { Storage, File } diff --git a/src/lib/stores/appwrite.ts b/src/lib/stores/appwrite.ts index be73afd..9303318 100644 --- a/src/lib/stores/appwrite.ts +++ b/src/lib/stores/appwrite.ts @@ -1,8 +1,13 @@ -import { Client, Account, Databases } from 'appwrite' +import { Client, Account, Databases, Storage, Teams, Functions, Locale, Avatars } from 'appwrite' const client = new Client() const account = new Account(client) const databases = new Databases(client) +const storage = new Storage(client) +const teams = new Teams(client) +const functions = new Functions(client) +const locale = new Locale(client) +const avatars = new Avatars(client) const url = { oauth: { @@ -14,4 +19,4 @@ const url = { client.setEndpoint('http://localhost/v1').setProject('638871b363904655d784') export default client -export { client, account, url, databases } +export { client, account, url, databases, storage, teams, functions, locale, avatars }