Skip to content

Commit

Permalink
feat: attachments & sort examples
Browse files Browse the repository at this point in the history
  • Loading branch information
voznik committed Feb 11, 2024
1 parent cefa3d8 commit 2de0f35
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 90 deletions.
38 changes: 36 additions & 2 deletions examples/demo/src/app/todos/todos.component.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* @import "https://unpkg.com/open-props"; */

.clear-completed:disabled {
color: #999;
cursor: not-allowed;
text-decoration: none;
}

.todo-list li label + .last-modified {
.todo-list li label+.last-modified {
position: absolute;
bottom: 4px;
right: 24px;
Expand All @@ -14,6 +16,38 @@
text-decoration: none !important;
}

.todo-list li:hover label:not(.editing) + .last-modified {
.todo-list li:hover label:not(.editing)+.last-modified {
display: block;
}

dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
width: 80%;
max-width: 500px;
padding: 20px;
border: 1px solid #ccc;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
background: white;
color: #111111;
font-family: unset;
}

dialog>form {
padding-top: 1em;
border-top: 1px solid #ccc;
width: 100%;
display: flex;
justify-content: space-between;
}
dialog>form>button {
display: block;
}

dialog::backdrop {
/* make the backdrop a semi-transparent black */
background-color: rgba(0, 0, 0, 0.4);
}
23 changes: 15 additions & 8 deletions examples/demo/src/app/todos/todos.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ <h1>todos</h1>
<footer
class="footer"
[hidden]="$.count === 0"
*ngrxLet="($.todos | byStatus: 'ACTIVE')?.length; let remainig"
*ngrxLet="($.todos | byStatus: 'ACTIVE':true)?.length; let remainig"

Check failure on line 62 in examples/demo/src/app/todos/todos.component.html

View workflow job for this annotation

GitHub Actions / build

Replace `:` with `·:·`
>
<ng-container *ngIf="remainig">
<span class="todo-count" [ngPlural]="remainig">
Expand All @@ -69,6 +69,8 @@ <h1>todos</h1>
</span>
</ng-container>
<ul class="filters">
<li><a href="javascript:void(0);" (click)="todosService.sortTodos('desc')">&#8675;</a></li>

Check failure on line 72 in examples/demo/src/app/todos/todos.component.html

View workflow job for this annotation

GitHub Actions / build

Replace `<a·href="javascript:void(0);"·(click)="todosService.sortTodos('desc')">&#8675;</a>` with `⏎········<a·href="javascript:void(0);"·(click)="todosService.sortTodos('desc')">&#8675;</a>⏎······`
<li><a href="javascript:void(0);" (click)="todosService.sortTodos('asc')">&#8673;</a></li>

Check failure on line 73 in examples/demo/src/app/todos/todos.component.html

View workflow job for this annotation

GitHub Actions / build

Replace `<a·href="javascript:void(0);"·(click)="todosService.sortTodos('asc')">&#8673;</a>` with `⏎········<a·href="javascript:void(0);"·(click)="todosService.sortTodos('asc')">&#8673;</a>⏎······`
<li>
<a
href="javascript:void(0);"
Expand Down Expand Up @@ -108,8 +110,8 @@ <h1>todos</h1>
</footer>
</section>

<dialog [open]="isDialogOpen">
Attachments:
<dialog [open]="isDialogOpen" class="todo-dialog">
<header>Attachments:</header>
<ul *ngIf="selectedTodo?._attachments">
<li *ngFor="let attachment of selectedTodo._attachments | keyvalue">
<a
Expand All @@ -121,23 +123,28 @@ <h1>todos</h1>
&nbsp;
<button
type="button"
(click)="todosService.removeAttachment(selectedTodo.id, attachment.key)"
class="destroy"
(click)="
todosService.removeAttachment(selectedTodo.id, attachment.key);
isDialogOpen = false
"
>
🗙
</button>
</li>
</ul>
<form method="dialog" (ngSubmit)="selectedTodo = undefined; isDialogOpen = false">
<button type="button" (click)="fileInput.click()">Upload txt attachment</button>
<button type="button" (click)="fileInput.click()">Upload attachment</button>
<input
type="file"
accept=".txt"
#fileInput
style="display: none"
(change)="
todosService.uploadAttachment(selectedTodo.id, $any($event.target).files[0])
todosService.uploadAttachment(selectedTodo.id, $any($event.target).files[0]);
isDialogOpen = false
"
/>
<button>OK</button>
<button>Close Dialog</button>
</form>
</dialog>
8 changes: 5 additions & 3 deletions examples/demo/src/app/todos/todos.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Pipe, PipeTransform, inject } from '@angular/core';
import { RXDB_CONFIG_COLLECTION } from '@ngx-odm/rxdb/config';
import { Todo, TodosFilter } from './todos.model';

@Pipe({ name: 'byStatus' })
export class TodosPipe implements PipeTransform {
transform(value: Todo[], status: TodosFilter): Todo[] {
if (!value) {
colConfig = inject(RXDB_CONFIG_COLLECTION);
transform(value: Todo[], status: TodosFilter, force = false): Todo[] {
if (!value || (this.colConfig.options.useQueryParams && !force)) {
return value;
}
if (status === 'ALL') {
Expand Down
9 changes: 4 additions & 5 deletions examples/demo/src/app/todos/todos.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { Injectable, inject } from '@angular/core';
import { NgxRxdbCollection, NgxRxdbCollectionService } from '@ngx-odm/rxdb/collection';
import { DEFAULT_LOCAL_DOCUMENT_ID } from '@ngx-odm/rxdb/config';
import { RxAttachment, type RxDatabaseCreator } from 'rxdb';
import { Observable, distinctUntilChanged, startWith } from 'rxjs';
import { v4 as uuid } from 'uuid';
import { Todo, TodosFilter, TodosLocalState } from './todos.model';
Expand All @@ -28,10 +27,6 @@ export class TodosService {
withAttachments
);

get dbOptions(): Readonly<RxDatabaseCreator> {
return this.collectionService.dbOptions;
}

get isAddTodoDisabled() {
return this.newTodo.length < 4;
}
Expand Down Expand Up @@ -111,6 +106,10 @@ export class TodosService {
);
}

sortTodos(dir: 'asc' | 'desc'): void {
this.collectionService.patchQueryParams({ sort: [{ last_modified: dir }] });
}

async uploadAttachment(id: string, file: File) {
await this.collectionService.putAttachment(id, {
id: file.name, // (string) name of the attachment
Expand Down
78 changes: 52 additions & 26 deletions packages/rxdb/collection/src/lib/rxdb-collection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ import {
isValidRxReplicationState,
mapFindResultToJsonArray,
} from '@ngx-odm/rxdb/utils';
import {
import type {
FilledMangoQuery,
RXJS_SHARE_REPLAY_DEFAULTS,
type MangoQuery,
type RxDatabase,
type RxDatabaseCreator,
type RxDocument,
type RxDumpCollection,
type RxDumpCollectionAny,
type RxLocalDocument,
type RxStorageWriteError,
type RxCollection as _RxCollection,
RxAttachment,
MangoQuery,
RxAttachmentCreator,
RxDatabase,
RxDatabaseCreator,
RxDocument,
RxDumpCollection,
RxDumpCollectionAny,
RxLocalDocument,
RxStorageWriteError,
RxCollection as _RxCollection,
} from 'rxdb';
import { RXJS_SHARE_REPLAY_DEFAULTS, RxError, removeRxDatabase } from 'rxdb';
import { RxReplicationState } from 'rxdb/plugins/replication';
import {
NEVER,
Observable,
ReplaySubject,
distinctUntilChanged,
Expand Down Expand Up @@ -100,7 +100,7 @@ export class NgxRxdbCollection<T extends Entity = { id: EntityId }> {
}

get queryParams$(): Observable<FilledMangoQuery<T>> {
return this.initialized$.pipe(switchMap(() => this.collection.queryParams$));
return this.initialized$.pipe(switchMap(() => this.collection.queryParams$ || NEVER));
}

constructor(public readonly config: RxCollectionCreatorExtended) {
Expand All @@ -115,11 +115,11 @@ export class NgxRxdbCollection<T extends Entity = { id: EntityId }> {
}

setQueryParams(query: MangoQuery<T>): void {
this.collection.queryParamsSet(query);
this.collection.queryParamsSet?.(query);
}

patchQueryParams(query: MangoQuery<T>): void {
this.collection.queryParamsPatch(query);
this.collection.queryParamsPatch?.(query);
}

async sync(): Promise<void> {
Expand Down Expand Up @@ -369,6 +369,10 @@ export class NgxRxdbCollection<T extends Entity = { id: EntityId }> {
return this.collection.remove();
}

/**
* Returns an array of Blobs of all attachments of the RxDocument.
* @param docId
*/
async getAttachments(docId: string): Promise<Blob[] | null> {
await this.ensureCollection();
const doc = await this.collection.findOne(docId).exec();
Expand All @@ -379,6 +383,11 @@ export class NgxRxdbCollection<T extends Entity = { id: EntityId }> {
return Promise.all(attachmentsData);
}

/**
* Returns data of an RxAttachment by its id. Returns null when the attachment does not exist.
* @param docId
* @param attachmentId
*/
async getAttachmentById(docId: string, attachmentId: string): Promise<Blob | null> {
await this.ensureCollection();
const doc = await this.collection.findOne(docId).exec();
Expand All @@ -392,24 +401,37 @@ export class NgxRxdbCollection<T extends Entity = { id: EntityId }> {
return attachment.getData();
}

/**
* Adds an attachment to a RxDocumen
* @param docId
* @param attachment
*/
async putAttachment(docId: string, attachment: RxAttachmentCreator): Promise<void> {
await this.ensureCollection();
const doc = await this.collection.findOne(docId).exec();
if (!doc) {
throw new Error(`Document with id "${docId}" not found.`);
logger.log(`document with id "${docId}" not found.`);
return;
}
await doc.putAttachment(attachment);
}

/**
* Removes the attachment
* @param docId
* @param attachmentId
*/
async removeAttachment(docId: string, attachmentId: string): Promise<void> {
await this.ensureCollection();
const doc = await this.collection.findOne(docId).exec();
if (!doc) {
throw new Error(`Document with id "${docId}" not found.`);
logger.log(`document with id "${docId}" not found.`);
return;
}
const attachment = doc.getAttachment(attachmentId);
if (!attachment) {
throw new Error(`Attachment with id "${attachmentId}" not found.`);
logger.log(`attachment with id "${attachmentId}" not found.`);
return;
}
await attachment.remove();
}
Expand Down Expand Up @@ -528,23 +550,27 @@ export class NgxRxdbCollection<T extends Entity = { id: EntityId }> {
this._collection = this.db.collections[name] as RxCollection<T>;
// Init query params plugin
if (this.config.options?.useQueryParams) {
this.collection.queryParamsInit(this.currentUrl$, this.updateQueryParamsFn);
this.collection.queryParamsInit!(this.currentUrl$, this.updateQueryParamsFn);
}
this._init$.next(true);
this._init$.complete();
} catch (e) {
} catch (err) {
// @see rx-database-internal-store.ts:isDatabaseStateVersionCompatibleWithDatabaseCode
// @see test/unit/data-migration.test.ts#L16
if (e.message.includes('DM5')) {
if (
err.message.includes('DM5') ||
(err.message.includes('DB6') &&
(err as RxError).parameters.previousSchema?.version ===
(err as RxError).parameters.schema?.version)
) {
logger.log(
`Database version conflict.
Opening an older RxDB database state with a new major version should throw an error`
'Reload the page to fix the issue. The database is in a state where it can not be used.'
);
// await dbService.db.destroy();
throw new Error(e);
await removeRxDatabase(this.db.name, this.db.storage);
window?.location?.reload?.();
} else {
this._init$.complete();
throw new Error(e.message ?? e);
throw err;
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions packages/rxdb/config/src/lib/rxdb.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ export type RxCollectionExtended<T = any> = RxCollection<T> &
RxCollectionWiithQueryParams<T>;

export type RxCollectionWiithQueryParams<T = any> = {
queryParamsInit: (
queryParamsInit?: (
currentUrl$: Observable<string>,
updateQueryParamsInLocationFn: (queryParams: MangoQueryParams) => Promise<any>
) => void;
queryParamsGet(): MangoQuery<T>;
queryParamsSet(query: MangoQuery<T>): void;
queryParamsPatch(query: MangoQuery<T>): void;
queryParams$: Observable<FilledMangoQuery<T>>;
queryParamsGet?(): MangoQuery<T>;
queryParamsSet?(query: MangoQuery<T>): void;
queryParamsPatch?(query: MangoQuery<T>): void;
queryParams$?: Observable<FilledMangoQuery<T>>;
};

export type RxCollectionWithMetadata = {
Expand Down
10 changes: 5 additions & 5 deletions packages/rxdb/core/src/lib/rxdb.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ export class NgxRxdbService {
* @param config
*/
async initDb(config: RxDatabaseCreator): Promise<void> {
// eslint-disable-next-line no-useless-catch
try {
await loadRxDBPlugins(config.options?.plugins);
this.dbInstance = await createRxDatabase(config).catch(e => {
throw new Error(e.message ?? e);
});
this.dbInstance = await createRxDatabase(config);
this.options = config;
NgxRxdbUtils.logger.log(
`created database "${this.db.name}" with config "${JSON.stringify(config)}"`
Expand All @@ -68,7 +67,7 @@ export class NgxRxdbService {
);
}
} catch (error) {
throw new Error(error.message);
throw error;
}
}

Expand All @@ -79,11 +78,12 @@ export class NgxRxdbService {
async initCollections(colConfigs: {
[name: string]: RxCollectionCreatorExtended;
}): Promise<CollectionsOfDatabase> {
// eslint-disable-next-line no-useless-catch
try {
const colCreators = await prepareCollections(colConfigs);
return await this.db.addCollections(colCreators);
} catch (error) {
throw new Error(error.message);
throw error;
}
}
}
Loading

0 comments on commit 2de0f35

Please sign in to comment.