diff --git a/src/app/update-account/update-account.component.html b/src/app/update-account/update-account.component.html
index b0b19c5..9a666c8 100644
--- a/src/app/update-account/update-account.component.html
+++ b/src/app/update-account/update-account.component.html
@@ -16,16 +16,20 @@
{{snippetService.getSnippet('update-name-modal.t
Account Image URL
-
-
- Not a valid url
-
+
diff --git a/src/app/update-account/update-account.component.ts b/src/app/update-account/update-account.component.ts
index b56d4fc..39eb061 100644
--- a/src/app/update-account/update-account.component.ts
+++ b/src/app/update-account/update-account.component.ts
@@ -3,6 +3,11 @@ import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
import {AccountService, UpdateAccountSettingsOptions} from '../account.service'
import {ToastService} from '../toast.service'
import {SnippetService} from '../snippet.service'
+import {AsyncValidatorFn, FormBuilder} from '@angular/forms'
+import {debounceTime, switchMap} from 'rxjs'
+import {distinctUntilChanged, first, map} from 'rxjs/operators'
+import {fromPromise} from 'rxjs/internal/observable/innerFrom'
+import axios from 'axios'
@Component({
selector: 'app-update-account',
@@ -11,58 +16,51 @@ import {SnippetService} from '../snippet.service'
})
export class UpdateAccountComponent {
public newName = null
- public newImageUrl?: string
- public faCircleNotch = faCircleNotch
+ public isValidatingImageUrl: boolean = false
+ public readonly faCircleNotch = faCircleNotch
+ public readonly imageUrlForm = this.formBuilder.group({
+ imageUrl: [
+ this.accountService.account?.settings.profile?.imageUrl,
+ {
+ asyncValidators: [
+ this.makeImageUrlValidator(),
+ ],
+ updateOn: 'change',
+ },
+ ],
+ })
- private get newImageUrlTrimmed(): string|undefined {
- const newImageUrl = this.newImageUrl?.trim()
- if (newImageUrl === '') {
- return undefined
- }
+ public get newImageUrl(): string|undefined {
+ const value = this.imageUrlForm.controls['imageUrl'].getRawValue()
- return newImageUrl
+ return value?.trim() === '' ? undefined : value.trim()
}
constructor(
public accountService: AccountService,
public snippetService: SnippetService,
private readonly toastService: ToastService,
+ private readonly formBuilder: FormBuilder,
) {
this.newName = this.accountService.account.name
- this.newImageUrl = this.accountService.account?.settings.profile?.imageUrl
}
get isNewNameValid(): boolean {
return !this.newNameOrUndefined || this.newNameOrUndefined.length <= 64
}
- public get isNewImageUrlValid(): boolean {
- if (this.newImageUrlTrimmed === undefined) {
- return true
- }
-
- let url: URL
- try {
- url = new URL(this.newImageUrlTrimmed)
- } catch (_) {
- return false
- }
-
- return url.protocol === 'https:' && this.newImageUrlTrimmed.length < 1024
- }
-
get canUpdateAccount(): boolean {
if (this.accountService.isUpdatingAccount) {
return false
}
- if (!this.isNewNameValid || !this.isNewImageUrlValid) {
+ if (!this.isNewNameValid || !this.imageUrlForm.valid) {
return false
}
if (this.accountService.account.name !== this.newNameOrUndefined) {
return true
}
- return this.accountService.account?.settings.profile?.imageUrl !== this.newImageUrlTrimmed
+ return this.accountService.account?.settings.profile?.imageUrl !== this.newImageUrl
}
get newNameOrUndefined(): string|undefined {
@@ -81,7 +79,7 @@ export class UpdateAccountComponent {
willUpdateName = true
}
let willUpdateImageUrl = false
- const newImageUrl = this.newImageUrlTrimmed
+ const newImageUrl = this.newImageUrl
if (newImageUrl !== this.accountService.account?.settings.profile?.imageUrl) {
updateOptions.imageUrl = newImageUrl
willUpdateImageUrl = true
@@ -106,4 +104,37 @@ export class UpdateAccountComponent {
this.toastService.showErrorToast(err.message)
}
}
+
+ private makeImageUrlValidator(): AsyncValidatorFn {
+ return control => control.valueChanges.pipe(
+ debounceTime(300),
+ distinctUntilChanged(),
+ switchMap(value => fromPromise(this.isValidImageUrl(value))),
+ map((isValid: boolean) => (isValid ? null : { invalid: true })),
+ first(),
+ )
+ }
+
+ private async isValidImageUrl(imageUrl: string|undefined): Promise {
+ imageUrl = imageUrl?.trim() === '' ? undefined : imageUrl.trim()
+ if (imageUrl === undefined) {
+ return true
+ }
+
+ this.isValidatingImageUrl = true
+ try {
+ const url = new URL(imageUrl)
+
+ if (url.protocol !== 'https:' || imageUrl.length >= 1024) {
+ return false
+ }
+ await axios.get(imageUrl)
+
+ return true
+ } catch (_) {
+ return false
+ } finally {
+ this.isValidatingImageUrl = false
+ }
+ }
}