Chorehancement: update to Angular v22, 'zoneless' / 'reactive' (#13114)

This commit is contained in:
shamoon authored and GitHub committed 2026-07-10 00:42:16 -07:00
1 parent f244442c65
commit 106b41a15c
213 files changed
+5363 -5842

No files matched your search

@@ -1,15 +1,15 @@
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{title}}</h4>
<h4 class="modal-title" id="modal-basic-title">{{title()}}</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
</div>
<div class="modal-body p-0">
<ul class="list-group list-group-flush">
@if (!shareLinks || shareLinks.length === 0) {
@if (!shareLinks() || shareLinks().length === 0) {
<li class="list-group-item fst-italic small text-center text-secondary" i18n>
No existing links
</li>
}
@for (link of shareLinks; track link) {
@for (link of shareLinks(); track link) {
<li class="list-group-item">
<div class="input-group w-100">
<input type="text" class="form-control" aria-label="Share link" [value]="getShareUrl(link)" readonly>
@@ -19,10 +19,10 @@
</span>
}
<button type="button" class="btn btn-outline-primary" (click)="copy(link)">
@if (copied !== link.id) {
@if (copied() !== link.id) {
<i-bs width="1.2em" height="1.2em" name="clipboard-fill"></i-bs>
}
@if (copied === link.id) {
@if (copied() === link.id) {
<i-bs width="1.2em" height="1.2em" name="clipboard-check-fill"></i-bs>
}
<span class="visually-hidden" i18n>Copy</span>
@@ -36,7 +36,7 @@
<i-bs width="1.2em" height="1.2em" name="trash"></i-bs><span class="visually-hidden" i18n>Delete</span>
</button>
</div>
<span class="badge copied-badge bg-primary small fade ms-4 position-absolute top-50 translate-middle-y pe-none z-3" [class.show]="copied === link.id" i18n>Copied!</span>
<span class="badge copied-badge bg-primary small fade ms-4 position-absolute top-50 translate-middle-y pe-none z-3" [class.show]="copied() === link.id" i18n>Copied!</span>
</li>
}
</ul>
@@ -44,7 +44,7 @@
<div class="modal-footer">
<div class="input-group w-100">
<div class="form-check form-switch ms-auto">
<input class="form-check-input" type="checkbox" role="switch" id="versionSwitch" [disabled]="!hasArchiveVersion" [(ngModel)]="useArchiveVersion">
<input class="form-check-input" type="checkbox" role="switch" id="versionSwitch" [disabled]="!hasArchiveVersion()" [(ngModel)]="useArchiveVersion">
<label class="form-check-label" for="versionSwitch" i18n>Share archive version</label>
</div>
</div>
@@ -55,11 +55,11 @@
<option [ngValue]="option.value">{{ option.label }}</option>
}
</select>
<button class="btn btn-outline-primary ms-auto" type="button" (click)="createLink()" [disabled]="loading">
@if (loading) {
<button class="btn btn-outline-primary ms-auto" type="button" (click)="createLink()" [disabled]="loading()">
@if (loading()) {
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
}
@if (!loading) {
@if (!loading()) {
<i-bs name="plus"></i-bs>
}
<ng-container i18n>Create</ng-container>
@@ -4,12 +4,7 @@ import {
HttpTestingController,
provideHttpClientTesting,
} from '@angular/common/http/testing'
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { By } from '@angular/platform-browser'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@@ -56,7 +51,7 @@ describe('ShareLinksDialogComponent', () => {
it('should support refresh to retrieve links', () => {
const getSpy = jest.spyOn(shareLinkService, 'getLinksForDocument')
component.documentId = 99
component.documentId.set(99)
const now = new Date()
const expiration7days = new Date()
@@ -88,7 +83,7 @@ describe('ShareLinksDialogComponent', () => {
fixture.detectChanges()
expect(component.shareLinks).toHaveLength(2)
expect(component.shareLinks()).toHaveLength(2)
})
it('should show error on refresh if needed', () => {
@@ -96,16 +91,17 @@ describe('ShareLinksDialogComponent', () => {
jest
.spyOn(shareLinkService, 'getLinksForDocument')
.mockReturnValueOnce(throwError(() => new Error('Unable to get links')))
component.documentId = 99
component.documentId.set(99)
component.ngOnInit()
fixture.detectChanges()
expect(toastSpy).toHaveBeenCalled()
})
it('should support link creation then refresh & copy url', fakeAsync(() => {
it('should support link creation then refresh & copy url', () => {
jest.useFakeTimers()
const createSpy = jest.spyOn(shareLinkService, 'createLinkForDocument')
component.documentId = 99
component.documentId.set(99)
component.expirationDays = 7
component.useArchiveVersion = false
@@ -126,16 +122,17 @@ describe('ShareLinksDialogComponent', () => {
expiration: expiration.toISOString(),
})
fixture.detectChanges()
tick(3000)
jest.advanceTimersByTime(3000)
expect(refreshSpy).toHaveBeenCalled()
expect(copySpy).toHaveBeenCalled()
expect(component.copied).toEqual(1)
tick(100) // copy timeout
}))
expect(component.copied()).toEqual(1)
jest.advanceTimersByTime(100) // copy timeout
jest.useRealTimers()
})
it('should show error on link creation if needed', () => {
component.documentId = 99
component.documentId.set(99)
component.expirationDays = 7
const expiration = new Date()
@@ -227,7 +224,7 @@ describe('ShareLinksDialogComponent', () => {
})
it('should disable archive switch & option if no archive available', (done) => {
component.hasArchiveVersion = false
component.hasArchiveVersion.set(false)
component.ngOnInit()
fixture.detectChanges()
expect(component.useArchiveVersion).toBeFalsy()
@@ -1,5 +1,5 @@
import { Clipboard } from '@angular/cdk/clipboard'
import { Component, Input, OnInit, inject } from '@angular/core'
import { Component, OnInit, effect, inject, input, signal } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
@@ -27,55 +27,40 @@ export class ShareLinksDialogComponent implements OnInit {
readonly expirationOptions = SHARE_LINK_EXPIRATION_OPTIONS
@Input()
title = $localize`Share Links`
_documentId: number
@Input()
set documentId(id: number) {
if (id !== undefined) {
this._documentId = id
this.refresh()
}
}
private _hasArchiveVersion: boolean = true
@Input()
set hasArchiveVersion(value: boolean) {
this._hasArchiveVersion = value
this.useArchiveVersion = value
}
get hasArchiveVersion(): boolean {
return this._hasArchiveVersion
}
shareLinks: ShareLink[]
loading: boolean = false
copied: number
readonly title = input($localize`Share Links`)
readonly documentId = signal<number>(undefined)
readonly hasArchiveVersion = signal(true)
readonly shareLinks = signal<ShareLink[]>(undefined)
readonly loading = signal(false)
readonly copied = signal<number>(undefined)
expirationDays: number = 7
useArchiveVersion: boolean = true
constructor() {
effect(() => {
const id = this.documentId()
if (id !== undefined) this.refresh()
})
effect(() => {
this.useArchiveVersion = this.hasArchiveVersion()
})
}
ngOnInit(): void {
if (this._documentId !== undefined) this.refresh()
if (this.documentId() !== undefined) this.refresh()
}
refresh() {
if (this._documentId === undefined) return
this.loading = true
if (this.documentId() === undefined) return
this.loading.set(true)
this.shareLinkService
.getLinksForDocument(this._documentId)
.getLinksForDocument(this.documentId())
.pipe(first())
.subscribe({
next: (results) => {
this.loading = false
this.shareLinks = results
this.loading.set(false)
this.shareLinks.set(results)
},
error: (e) => {
this.toastService.showError(
@@ -104,9 +89,9 @@ export class ShareLinksDialogComponent implements OnInit {
copy(link: ShareLink) {
const success = this.clipboard.copy(this.getShareUrl(link))
if (success) {
this.copied = link.id
this.copied.set(link.id)
setTimeout(() => {
this.copied = null
this.copied.set(null)
}, 3000)
}
}
@@ -138,23 +123,23 @@ export class ShareLinksDialogComponent implements OnInit {
expiration = new Date()
expiration.setDate(expiration.getDate() + this.expirationDays)
}
this.loading = true
this.loading.set(true)
this.shareLinkService
.createLinkForDocument(
this._documentId,
this.documentId(),
this.useArchiveVersion ? FileVersion.Archive : FileVersion.Original,
expiration
)
.subscribe({
next: (result) => {
this.loading = false
this.loading.set(false)
setTimeout(() => {
this.copy(result)
}, 10)
this.refresh()
},
error: (e) => {
this.loading = false
this.loading.set(false)
this.toastService.showError($localize`Error creating link`, 10000, e)
},
})