Multicolumn Bluesky client powered by Angular

feat: open nested auxpanes

kbenlloch 7c434cf5 50adc403

Changed files
+79 -39
src
+7 -7
src/app/components/aux-panes/thread-view/thread-view.component.html
··· 13 13 <post-card 14 14 [post]="parent()" 15 15 (postChange)="parent.set($event)" 16 - (click)="dialogService.openThread(parent().uri)" 17 - (onEmbedRecord)="dialogService.openRecord($event)" 16 + (click)="dialogService.openThread(parent().uri, true)" 17 + (onEmbedRecord)="dialogService.openRecord($event, true)" 18 18 class="cursor-pointer hover:bg-primary/2 w-full px-3 pt-3 pb-1" 19 19 parent 20 20 /> ··· 30 30 @for (reply of replies(); track reply.uuid) { 31 31 <divider/> 32 32 33 - @for (post of reply.thread; track post.uuid; let i = $index) { 33 + @for (child of reply.thread; track child.uuid; let i = $index) { 34 34 <post-card 35 - [post]="post.post()" 36 - (postChange)="post.post.set($event)" 37 - (click)="dialogService.openThread(post.post().uri)" 38 - (onEmbedRecord)="dialogService.openRecord($event)" 35 + [post]="child.post()" 36 + (postChange)="child.post.set($event)" 37 + (click)="dialogService.openThread(child.post().uri, true)" 38 + (onEmbedRecord)="dialogService.openRecord($event, true)" 39 39 class="cursor-pointer hover:bg-primary/2 w-full px-3 pt-3 pb-1" 40 40 [parent]="i !== reply.thread.length - 1" 41 41 />
+12 -12
src/app/components/aux-panes/thread-view/thread-view.component.ts
··· 82 82 this.parents.set(parents); 83 83 } 84 84 85 - this.loadReady.set(true); 86 - this.cdRef.markForCheck(); 87 - 88 - if (thread.parent) { 89 - setTimeout(() => { 90 - this.scroll().nativeElement.scrollTo({ 91 - top: this.mainCard().nativeElement.offsetTop, 92 - behavior: 'smooth' 93 - }); 94 - }, 50); 95 - } 96 - 97 85 //Set grouped replies 98 86 if (thread.replies) { 99 87 const replies = thread.replies ··· 140 128 }) 141 129 } 142 130 }); 131 + } 132 + 133 + this.loadReady.set(true); 134 + this.cdRef.markForCheck(); 135 + 136 + if (thread.parent) { 137 + setTimeout(() => { 138 + this.scroll().nativeElement.scrollTo({ 139 + top: this.mainCard().nativeElement.offsetTop, 140 + behavior: 'smooth' 141 + }); 142 + }, 50); 143 143 } 144 144 } 145 145 }, error: err => this.messageService.error(err.message)
+27 -6
src/app/components/navigation/auxbar/auxbar.component.html
··· 7 7 [ngClass]="{'border-b border-primary': dialogService.auxPanes().length}" 8 8 > 9 9 @if (dialogService.auxPanes().length) { 10 + @if (dialogService.auxPanes()[0].children.length) { 11 + <button 12 + (click)="dialogService.closeAuxPaneChildren()" 13 + class="btn-primary border-b-0 font-black text-lg" 14 + ><</button> 15 + } 10 16 <span 11 - class="bg-primary text-bg text-xl font-medium flex items-center px-3" 17 + class="bg-primary text-bg text-xl font-medium flex items-center pr-3" 18 + [ngClass]="{ 19 + 'pl-2': dialogService.auxPanes()[0].children.length, 20 + 'pl-3': !dialogService.auxPanes()[0].children.length, 21 + }" 12 22 >thread view</span> 13 - <span 14 - (click)="closePane()" 15 - class="text-primary font-semibold flex items-center px-3 hover:underline cursor-pointer" 16 - >close</span> 23 + <button 24 + (click)="dialogService.closeAuxPane(); cdRef.markForCheck()" 25 + class="h-9 w-9 text-xl font-bold border border-primary hover:bg-primary/10 cursor-pointer" 26 + >x</button> 17 27 18 28 <span 19 29 (click)="dialogService.auxPanes.set([])" ··· 55 65 <ng-template 56 66 #auxPane 57 67 > 58 - @for (pane of dialogService.auxPanes(); track $index) { 68 + @for (pane of dialogService.auxPanes(); track pane.uuid; let iPane = $index) { 59 69 <div 60 70 class="absolute h-full w-full bg-bg" 71 + [class.hidden]="pane.children.length || iPane" 61 72 > 73 + @if (pane.children.length) { 74 + @for (child of pane.children; track child.uuid; let iChild = $index) { 75 + @if (child | isAuxPaneThread) { 76 + <thread-view 77 + [uri]="child.uri" 78 + [class.hidden]="iChild" 79 + /> 80 + } 81 + } 82 + } 62 83 @if (pane | isAuxPaneThread) { 63 84 <thread-view 64 85 [uri]="pane.uri"
+1 -8
src/app/components/navigation/auxbar/auxbar.component.ts
··· 29 29 protected dialogService: DialogService, 30 30 private messageService: MessageService, 31 31 private authService: AuthService, 32 - private cdRef: ChangeDetectorRef 32 + protected cdRef: ChangeDetectorRef 33 33 ) { 34 34 this.loadTopics(); 35 35 } ··· 43 43 this.topics.set(response.data.topics); 44 44 this.cdRef.markForCheck(); 45 45 }, error: err => this.messageService.error(err.message) 46 - }); 47 - } 48 - 49 - closePane() { 50 - this.dialogService.auxPanes.update(panes => { 51 - panes.pop(); 52 - return panes; 53 46 }); 54 47 } 55 48 }
+4
src/app/models/aux-pane.ts
··· 1 + import * as uuid from "uuid"; 2 + 1 3 export class AuxPane { 4 + uuid: string = uuid.v4(); 5 + children: Partial<AuxPane>[] = []; 2 6 } 3 7 4 8 export class ThreadAuxPane extends AuxPane {
+28 -6
src/app/services/dialog.service.ts
··· 21 21 }); 22 22 } 23 23 24 - openThread(uri: string) { 24 + closeAuxPane() { 25 + this.auxPanes.update(panes => { 26 + panes.shift(); 27 + return panes; 28 + }) 29 + } 30 + 31 + closeAuxPaneChildren() { 32 + this.auxPanes.update(panes => { 33 + panes[0].children.shift(); 34 + return panes; 35 + }) 36 + } 37 + 38 + openThread(uri: string, children?: boolean) { 25 39 // Cancel action if user is selecting text 26 40 if (window.getSelection().toString().length) return; 27 41 // Cancel action if post is the same than the last opened thread ··· 37 51 38 52 const pane = new ThreadAuxPane(); 39 53 pane.uri = uri; 40 - this.auxPanes.update(panes => { 41 - return [...panes, pane]; 42 - }); 54 + 55 + if (children) { 56 + this.auxPanes.update(panes => { 57 + panes[0].children.unshift(pane); 58 + return panes; 59 + }); 60 + } else { 61 + this.auxPanes.update(panes => { 62 + return [pane, ...panes]; 63 + }); 64 + } 43 65 } 44 66 45 - openRecord(record: AppBskyEmbedRecord.View) { 67 + openRecord(record: AppBskyEmbedRecord.View, children?: boolean) { 46 68 switch (record.record.$type) { 47 69 case 'app.bsky.embed.record#viewRecord': 48 - this.openThread((record.record as AppBskyEmbedRecord.ViewRecord).uri); 70 + this.openThread((record.record as AppBskyEmbedRecord.ViewRecord).uri, children); 49 71 break; 50 72 case 'app.bsky.graph.defs#listView': 51 73 break;