Skip to content

Commit

Permalink
Fix masonry/thumbnail bug?
Browse files Browse the repository at this point in the history
  • Loading branch information
CaramelFur committed Nov 1, 2024
1 parent 0c063e4 commit d24e4a7
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 104 deletions.
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "picsur-backend",
"version": "0.5.3",
"version": "0.5.5",
"description": "Backend for Picsur",
"license": "GPL-3.0",
"repository": "https://github.com/caramelfur/Picsur",
Expand Down
6 changes: 2 additions & 4 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "picsur-frontend",
"version": "0.5.3",
"version": "0.5.5",
"description": "Frontend for Picsur",
"license": "GPL-3.0",
"repository": "https://github.com/caramelfur/Picsur",
Expand Down Expand Up @@ -57,10 +57,8 @@
"typescript": "~5.5.4",
"webpack": "^5.95.0",
"webpack-bundle-analyzer": "^4.10.2",
"@ngx-dropzone/cdk": "^18.1.1",
"zod": "^3.23.8",
"zone.js": "~0.14.10"
},
"dependencies": {
"@ngx-dropzone/cdk": "^18.1.1"
}
}
73 changes: 37 additions & 36 deletions frontend/src/app/components/masonry/masonry-item.directive.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,53 @@
import { Directive, ElementRef, Inject } from '@angular/core';
import {
ResizeObserverService,
WA_RESIZE_OPTION_BOX
} from '@ng-web-apis/resize-observer';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { Observable, map } from 'rxjs';
import { Directive, TemplateRef, ViewRef } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Directive({
selector: '[masonry-item]',
providers: [
ResizeObserverService,
{
provide: WA_RESIZE_OPTION_BOX,
deps: [ElementRef],
useValue: "border-box",
},
],
selector: 'ng-template[masonry-item]',
})
export class MasonryItemDirective {
private lastEntry: ResizeObserverEntry | null = null;
private viewRef: ViewRef | null = null;
private resizeObserver: ResizeObserver | null = null;
private sizeSubject: BehaviorSubject<{ width: number; height: number }> =
new BehaviorSubject({ width: 0, height: 0 });

private resizeObserver: Observable<ResizeObserverEntry>;
constructor(private template: TemplateRef<HTMLElement>) {}

constructor(
private element: ElementRef<HTMLElement>,
@Inject(ResizeObserverService)
resize: Observable<ResizeObserverEntry[]>,
) {
this.resizeObserver = resize.pipe(map((entries) => entries[0]));
this.subscribeResize();
public getTemplate(): TemplateRef<HTMLElement> {
return this.template;
}

@AutoUnsubscribe()
private subscribeResize() {
return this.resizeObserver.subscribe((value) => {
this.lastEntry = value;
});
public getViewRef(): ViewRef {
if (!this.viewRef || this.viewRef.destroyed) {
this.viewRef = this.template.createEmbeddedView(null as any);
this.resubscribeResizeObserver();
}
return this.viewRef;
}

public getElement() {
return this.element.nativeElement;
public getElement(): HTMLElement | null {
const anyRef = this.getViewRef() as any;
return anyRef.rootNodes.length > 0 ? anyRef.rootNodes[0] : null;
}

public getSize() {
return this.resizeObserver;
public getSizeObservable() {
return this.sizeSubject.asObservable();
}

public getLastSize() {
return this.lastEntry;
public getCurrentSize() {
return this.sizeSubject.value;
}

public resubscribeResizeObserver() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
const element = this.getElement();
if (element) {
this.resizeObserver = new ResizeObserver((items) => {
const { width, height } = items[0].contentRect;
this.sizeSubject.next({ width, height });
});
this.resizeObserver.observe(element);
}
}
}
6 changes: 3 additions & 3 deletions frontend/src/app/components/masonry/masonry.component.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- <ng-content></ng-content> -->
<div
#column
class="column"
*ngFor="let item of [].constructor(_column_count); let i = index"
></div>
>
<ng-container #column></ng-container>
</div>
61 changes: 41 additions & 20 deletions frontend/src/app/components/masonry/masonry.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import {
ChangeDetectorRef,
Component,
ContentChildren,
ElementRef,
Input,
OnDestroy,
QueryList,
ViewChildren,
ViewContainerRef
} from '@angular/core';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { combineLatest, Subscription } from 'rxjs';
import { MasonryItemDirective } from './masonry-item.directive';
import { RemoveChildren } from '../../util/remove-children';
import { Throttle } from '../../util/throttle';
import { MasonryItemDirective } from './masonry-item.directive';

@Component({
selector: 'masonry',
Expand All @@ -27,31 +26,43 @@ export class MasonryComponent implements AfterViewInit, OnDestroy {

@Input('columns') public set column_count(value: number) {
this._column_count = value;
this.cleanAllColumns();
this.changeDetector.markForCheck();
}
public _column_count = 1;
@Input('update-speed') update_speed = 200;

@ContentChildren(MasonryItemDirective)
private content: QueryList<MasonryItemDirective>;
private items: QueryList<MasonryItemDirective>;

@ViewChildren('column')
private columns: QueryList<ElementRef<HTMLDivElement>>;
@ViewChildren('column', { read: ViewContainerRef })
private columns: QueryList<ViewContainerRef>;

private sizesSubscription: Subscription | null = null;

ngAfterViewInit(): void {
this.subscribeContent();
this.subscribeColumns();
}

@AutoUnsubscribe()
private subscribeColumns() {
return this.columns.changes.subscribe(this.handleColumnsChange.bind(this));
}

private handleColumnsChange() {
this.resortItems();
this.changeDetector.markForCheck();
}

@AutoUnsubscribe()
private subscribeContent() {
this.handleContentChange(this.content);
return this.content.changes.subscribe(this.handleContentChange.bind(this));
this.handleContentChange();
return this.items.changes.subscribe(this.handleContentChange.bind(this));
}

private handleContentChange(items: QueryList<MasonryItemDirective>) {
const sizes = items.map((i) => i.getSize());
private handleContentChange() {
const sizes = this.items.map((i) => i.getSizeObservable());

if (this.sizesSubscription) {
this.sizesSubscription.unsubscribe();
Expand All @@ -60,22 +71,20 @@ export class MasonryComponent implements AfterViewInit, OnDestroy {
this.sizesSubscription = combineLatest(sizes)
.pipe(Throttle(this.update_speed))
.subscribe(() => {
this.resortItems(items);
this.resortItems();
});

this.resortItems(items);
this.resortItems();

this.changeDetector.markForCheck();
}

private resortItems(items: QueryList<MasonryItemDirective>) {
const itemsArray = items.toArray();
const columnsArray = this.columns.map((c) => c.nativeElement);
private resortItems() {
const itemsArray = this.items.toArray();

for (let i = 0; i < columnsArray.length; i++) {
RemoveChildren(columnsArray[i]);
}
this.cleanAllColumns();

const columnsArray = this.columns.map((c) => c);
const columnSizes = columnsArray.map(() => 0);

for (let i = 0; i < itemsArray.length; i++) {
Expand All @@ -92,9 +101,21 @@ export class MasonryComponent implements AfterViewInit, OnDestroy {
}
}

columnsArray[smallestColumn].appendChild(item.getElement());
columnsArray[smallestColumn].insert(item.getViewRef())
columnSizes[smallestColumn] +=
item.getLastSize()?.contentRect.height ?? 0;
item.getCurrentSize()?.height ?? 0;
}
}

private cleanAllColumns() {
this.columns?.forEach((column) => {
this.removeChildren(column);
});
}

private removeChildren(parent: ViewContainerRef) {
while (parent.length) {
parent.detach(0);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
width="0"
#targetcanvas
></canvas>

<img
*ngIf="state === 'image' || state === 'loading'"
[style.display]="state === 'loading' ? 'none' : 'block'"
loading="lazy"
#targetimg
/>

<mat-icon *ngIf="state === 'error'">broken_image</mat-icon>

<mat-spinner
Expand Down
57 changes: 29 additions & 28 deletions frontend/src/app/routes/images/images.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,36 @@ <h2 class="text-center" *ngIf="images !== null && images.length <= 0">

<ng-container *ngIf="images !== null && images.length > 0">
<h1>Your Images</h1>

<masonry [columns]="columns">
<div *ngFor="let image of images" class="m-2" masonry-item>
<mat-card>
<mat-card-header>
<mat-card-title>{{ image.file_name | truncate }}</mat-card-title>
<mat-card-subtitle>
Uploaded {{ image.created | amTimeAgo }}
{{
image.expires_at === null
? ''
: '| Expires ' + (image.expires_at | amTimeAgo)
}}
</mat-card-subtitle>
</mat-card-header>
<picsur-img
mat-card-image
[src]="getThumbnailUrl(image)"
alt="Image uploaded by you"
>
</picsur-img>
<mat-card-actions>
<button mat-stroked-button (click)="viewImage(image)">VIEW</button>
<button mat-button color="warn" (click)="deleteImage(image)">
DELETE
</button>
</mat-card-actions>
</mat-card>
</div>
<ng-template *ngFor="let image of images" masonry-item>
<div class="m-2">
<mat-card>
<mat-card-header>
<mat-card-title>{{ image.file_name | truncate }}</mat-card-title>
<mat-card-subtitle>
Uploaded {{ image.created | amTimeAgo }}
{{
image.expires_at === null
? ''
: '| Expires ' + (image.expires_at | amTimeAgo)
}}
</mat-card-subtitle>
</mat-card-header>
<picsur-img
mat-card-image
[src]="getThumbnailUrl(image)"
alt="Image uploaded by you"
>
</picsur-img>
<mat-card-actions>
<button mat-stroked-button (click)="viewImage(image)">VIEW</button>
<button mat-button color="warn" (click)="deleteImage(image)">
DELETE
</button>
</mat-card-actions>
</mat-card>
</div>
</ng-template>
</masonry>

<paginator
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/app/util/remove-children.ts

This file was deleted.

9 changes: 4 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion shared/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "picsur-shared",
"version": "0.5.3",
"version": "0.5.5",
"description": "Shared libraries for Picsur",
"license": "GPL-3.0",
"repository": "https://github.com/caramelfur/Picsur",
Expand Down
2 changes: 1 addition & 1 deletion support/setversion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fi

SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

UPDATE_VERSION="yarn version"
UPDATE_VERSION="pnpm version --f"

cd $SCRIPT_PATH/..
$UPDATE_VERSION $VERSION
Expand Down

0 comments on commit d24e4a7

Please sign in to comment.