unoffical wafrn mirror wafrn.net
atproto social-network activitypub
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: refactor settings literally just so I can have the select be disabled when it shouldn't be active

+138 -104
+1 -1
packages/frontend/src/app/components/post-action-buttons/post-action-buttons.component.ts
··· 125 125 } 126 126 127 127 ngOnInit(): void { 128 - this.buttonList = this.settingsService.values[this.settingKey()] as SettingListItem[] 128 + this.buttonList = this.settingsService.values()[this.settingKey()] as SettingListItem[] 129 129 this.bookmarked.set(this.fragment().bookmarkers.includes(this.myId)) 130 130 } 131 131
+2 -4
packages/frontend/src/app/components/post-actions/post-actions.component.ts
··· 1 - import { Component, computed, input, OnChanges, Signal } from '@angular/core' 1 + import { Component, computed, input, OnChanges } from '@angular/core' 2 2 import { ProcessedPost } from '../../interfaces/processed-post' 3 3 import { MessageService } from '../../services/message.service' 4 4 ··· 33 33 import { LoginService } from '../../services/login.service' 34 34 35 35 import { ReportService } from '../../services/report.service' 36 - import { DeletePostService } from '../../services/delete-post.service' 37 36 import { PostsService } from '../../services/posts.service' 38 37 import { UtilsService } from '../../services/utils.service' 39 38 import { EnvironmentService } from '../../services/environment.service' 40 - import { firstValueFrom } from 'rxjs' 41 39 import { faBluesky } from '@fortawesome/free-brands-svg-icons' 42 40 import { TranslateModule } from '@ngx-translate/core' 43 41 import { SettingsService } from 'src/app/services/settings.service' ··· 64 62 if (!bskyUri) return '' 65 63 const parts = bskyUri.split('/app.bsky.feed.post/') 66 64 const userDid = parts[0].split('at://')[1] 67 - return `https://${this.settingsService.values.atprotoLinkDestination || 'bsky.app'}/profile/${userDid}/post/${parts[1]}` 65 + return `https://${this.settingsService.values().atprotoLinkDestination || 'bsky.app'}/profile/${userDid}/post/${parts[1]}` 68 66 }) 69 67 externalUrl = computed<string>(() => (this.post().bskyUri ? this.bskyUrl() : this.post().remotePostId)) 70 68
+2 -2
packages/frontend/src/app/components/post-fragment/post-fragment.component.ts
··· 422 422 423 423 async cwClick() { 424 424 // Confirmation dialog when opening 425 - if (!this.showSensitiveContent() && this.settingsService.values.confirmOpenCw) { 426 - const annoyance = this.settingsService.values.confirmOpenCwAnnoyance 425 + if (!this.showSensitiveContent() && this.settingsService.values().confirmOpenCw) { 426 + const annoyance = this.settingsService.values().confirmOpenCwAnnoyance 427 427 if (typeof annoyance !== 'string' && annoyance !== undefined) return 428 428 429 429 const confirm = await this.simpleDialog.createConfirmDialog({
+2 -2
packages/frontend/src/app/components/setting-confetti/setting-confetti.component.html
··· 13 13 placeholder="1" 14 14 type="number" 15 15 min="0" 16 - [value]="values.confettiMultiplier" 17 - (input)="updateMultiplider($event)" 16 + [value]="values().confettiMultiplier" 17 + (input)="updateMultiplier($event)" 18 18 spellcheck="false" 19 19 /> 20 20 </mat-form-field>
+4 -4
packages/frontend/src/app/components/setting-confetti/setting-confetti.component.ts
··· 6 6 import { TranslateModule } from '@ngx-translate/core' 7 7 import { KeyValueTypedPipe } from 'src/app/pipes/keyvaluetyped.pipe' 8 8 import { ParticleService } from 'src/app/services/particle.service' 9 - import { SettingData, SettingsService, SettingValues } from 'src/app/services/settings.service' 9 + import { SettingData, SettingsService } from 'src/app/services/settings.service' 10 10 import { SettingEntryComponent } from '../setting-entry/setting-entry.component' 11 11 12 12 const confettiTypeVariants = ['like', 'rewoot', 'edit', 'bookmark'] as const ··· 28 28 }) 29 29 export class SettingConfettiComponent { 30 30 data: SettingData 31 - values: SettingValues 31 + values 32 32 33 33 confettiType: ConfettiType = 'like' 34 34 confettiTypeData: Record<ConfettiType, string> = { ··· 47 47 this.values = settingsService.values 48 48 } 49 49 50 - updateMultiplider(event: Event) { 50 + updateMultiplier(event: Event) { 51 51 const target = event.target 52 52 if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { 53 - this.values.confettiMultiplier = target.value 53 + this.values().confettiMultiplier = target.value 54 54 this.settingsService.settingsModified.set(true) 55 55 } 56 56 }
+3 -2
packages/frontend/src/app/components/setting-drop-list/setting-drop-list.component.ts
··· 50 50 this.dropListDataEntry = this.data[this.settingKey].dropListData 51 51 52 52 this.defaultOrder = this.data[this.settingKey].default as SettingListItem[] 53 - this.itemList = settingsService.values[this.settingKey] as SettingListItem[] 53 + this.itemList = settingsService.values()[this.settingKey] as SettingListItem[] 54 54 55 55 // Reset if no value or new values 56 56 if (this.itemList.length === 0 || this.itemList.length !== this.defaultOrder.length) { ··· 82 82 83 83 syncList() { 84 84 this.itemList = [...this.itemList] // update DOM hack 85 - this.settingsService.values[this.settingKey] = this.itemList 85 + this.settingsService.values()[this.settingKey] = this.itemList 86 + this.settingsService.values.update((v) => v) 86 87 this.settingsService.settingsModified.set(true) 87 88 } 88 89
+15 -8
packages/frontend/src/app/components/setting-entry/setting-entry.component.html
··· 3 3 @let settingTranslationDescription = setting().translationDescriptionKey; 4 4 @switch (settingData.type) { 5 5 @case ('checkbox') { 6 - @let settingValues = values[setting().key]; 7 - <div class="mb-2"> 6 + @let settingValues = values()[setting().key]; 7 + <div [class.has-dependency]="hasDependency()" [class.has-description]="settingTranslationDescription"> 8 8 <mat-checkbox 9 9 [class.mat-checkbox-top]="settingTranslationDescription !== undefined" 10 10 (change)="updateCheckbox(setting().key, $event)" 11 11 [checked]="(settingValues ?? setting().default) === true" 12 + [disabled]="isDisabled()" 12 13 >{{ settingTranslation | translate }} 13 14 @if (settingTranslationDescription) { 14 15 <div class="setting-description">{{ settingTranslationDescription | translate }}</div> ··· 18 19 } 19 20 @case ('select') { 20 21 @if (settingData.variants) { 21 - <mat-form-field class="block mb-3 w-full mat-form-field-no-padding"> 22 + <mat-form-field class="block mb-3 w-full mat-form-field-no-padding" [class.has-dependency]="hasDependency()"> 22 23 <mat-label>{{ settingTranslation | translate }}</mat-label> 23 - <mat-select [(value)]="values[setting().key]" (selectionChange)="updateSelect(setting().key, $event)"> 24 + <mat-select 25 + [(value)]="values()[setting().key]" 26 + (selectionChange)="updateSelect(setting().key, $event)" 27 + [disabled]="isDisabled()" 28 + > 24 29 @for (variant of settingData.variants | KeyValue; track $index) { 25 30 <mat-option [value]="variant.key">{{ variant.value | translate }}</mat-option> 26 31 } ··· 34 39 } 35 40 } 36 41 @case ('input') { 37 - <div class="mb-3"> 42 + <div class="mb-3" [class.has-dependency]="hasDependency()"> 38 43 <mat-form-field class="w-full mat-form-field-no-padding"> 39 44 <mat-label>{{ settingTranslation | translate }}</mat-label> 40 45 <input 41 46 matNativeControl 42 - [value]="values[setting().key]" 47 + [value]="values()[setting().key]" 43 48 (input)="updateInput(setting().key, $event)" 49 + [disabled]="isDisabled()" 44 50 spellcheck="false" 45 51 /> 46 52 </mat-form-field> ··· 50 56 </div> 51 57 } 52 58 @case ('textarea') { 53 - <div class="mb-3"> 59 + <div class="mb-3" [class.has-dependency]="hasDependency()"> 54 60 <mat-form-field class="w-full mat-form-field-no-padding"> 55 61 <mat-label>{{ settingTranslation | translate }}</mat-label> 56 62 <textarea 57 63 matNativeControl 58 - [value]="values[setting().key]" 64 + [value]="values()[setting().key]" 59 65 (input)="updateInput(setting().key, $event)" 66 + [disabled]="isDisabled()" 60 67 spellcheck="false" 61 68 cdkTextareaAutosize 62 69 cdkAutosizeMinRows="5"
+9
packages/frontend/src/app/components/setting-entry/setting-entry.component.scss
··· 1 + .has-dependency { 2 + margin-left: 0.75rem; 3 + padding-left: 0.75rem; 4 + border-left: 2px solid var(--mat-sys-outline-variant); 5 + } 6 + 7 + .has-description ::ng-deep label { 8 + margin-block: 0.375rem; 9 + }
+24 -14
packages/frontend/src/app/components/setting-entry/setting-entry.component.ts
··· 1 - import { Component, input, viewChildren } from '@angular/core' 1 + import { Component, computed, input, viewChildren } from '@angular/core' 2 2 import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox' 3 3 import { MatInputModule } from '@angular/material/input' 4 4 import { MatSelectChange, MatSelectModule } from '@angular/material/select' 5 5 import { TranslateModule } from '@ngx-translate/core' 6 6 import { KeyValueTypedPipe } from 'src/app/pipes/keyvaluetyped.pipe' 7 - import { 8 - SettingData, 9 - SettingDataEntry, 10 - SettingKey, 11 - SettingsService, 12 - SettingValues 13 - } from 'src/app/services/settings.service' 7 + import { SettingData, SettingDataEntry, SettingKey, SettingsService } from 'src/app/services/settings.service' 14 8 import { UserSelectorComponent } from '../user-selector/user-selector.component' 15 9 16 10 @Component({ ··· 23 17 KeyValueTypedPipe, 24 18 UserSelectorComponent 25 19 ], 26 - templateUrl: './setting-entry.component.html' 20 + templateUrl: './setting-entry.component.html', 21 + styleUrl: './setting-entry.component.scss' 27 22 }) 28 23 export class SettingEntryComponent { 29 24 data: SettingData 30 - values: SettingValues 25 + values 31 26 setting = input.required<SettingDataEntry>() 32 27 33 28 matFormFieldElements = viewChildren('formSelect') 34 29 30 + hasDependency = computed(() => this.setting().enableIfSetting !== undefined) 31 + isDisabled = computed(() => { 32 + this.settingsService.settingsModified() 33 + const enablefunc = this.setting().enableIfSetting 34 + if (enablefunc) { 35 + return !enablefunc(this.values()) 36 + } else { 37 + return false 38 + } 39 + }) 40 + 35 41 constructor(private settingsService: SettingsService) { 36 42 this.data = settingsService.data 37 43 this.values = settingsService.values 38 44 } 39 45 40 46 updateCheckbox(key: SettingKey, event: MatCheckboxChange) { 41 - this.values[key] = event.checked 47 + this.values()[key] = event.checked 48 + this.values.update((v) => v) 42 49 this.settingsService.settingsModified.set(true) 43 50 } 44 51 45 52 updateSelect(key: SettingKey, event: MatSelectChange) { 46 - this.values[key] = event.value 53 + this.values()[key] = event.value 54 + this.values.update((v) => v) 47 55 this.settingsService.settingsModified.set(true) 48 56 } 49 57 50 58 updateInput(key: SettingKey, event: Event) { 51 59 const target = event.target 52 60 if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { 53 - this.values[key] = target.value 61 + this.values()[key] = target.value 62 + this.values.update((v) => v) 54 63 this.settingsService.settingsModified.set(true) 55 64 } 56 65 } 57 66 58 67 updateUserInput(event: { remoteId: string; url: string }) { 59 - this.values[this.setting().key] = event.remoteId 68 + this.values()[this.setting().key] = event.remoteId 69 + this.values.update((v) => v) 60 70 this.settingsService.settingsModified.set(true) 61 71 } 62 72 }
+2 -2
packages/frontend/src/app/pages/settings/settings-loader/settings-loader.component.ts
··· 1 1 import { Component, Inject } from '@angular/core' 2 2 import { TranslateModule } from '@ngx-translate/core' 3 3 import { SettingEntryComponent } from 'src/app/components/setting-entry/setting-entry.component' 4 - import { GroupedSettingData, SettingData, SettingsService, SettingValues } from 'src/app/services/settings.service' 4 + import { GroupedSettingData, SettingData, SettingsService } from 'src/app/services/settings.service' 5 5 import { SETTINGS_TOKEN } from '../settings.component' 6 6 import { RouterModule } from '@angular/router' 7 7 import { CdkPortalOutlet, Portal } from '@angular/cdk/portal' ··· 14 14 }) 15 15 export class SettingsLoaderComponent { 16 16 data: SettingData 17 - values: SettingValues 17 + values 18 18 group: GroupedSettingData | undefined 19 19 portal: Portal<any> | undefined 20 20
+12 -14
packages/frontend/src/app/pages/settings/settings-profile/settings-profile.component.ts
··· 1 1 import { Component, computed, signal, Signal } from '@angular/core' 2 2 import { TranslateModule } from '@ngx-translate/core' 3 3 import { SettingEntryComponent } from 'src/app/components/setting-entry/setting-entry.component' 4 - import { FediAttachment, SettingData, SettingsService, SettingValues } from 'src/app/services/settings.service' 4 + import { FediAttachment, SettingData, SettingsService } from 'src/app/services/settings.service' 5 5 import { MatCardModule } from '@angular/material/card' 6 6 import { BlogDetails } from 'src/app/interfaces/blogDetails' 7 7 import { LoginService } from 'src/app/services/login.service' ··· 37 37 }) 38 38 export class SettingsProfileComponent { 39 39 data: SettingData 40 - values: SettingValues 40 + values 41 41 fediAttachments: FediAttachment[] 42 42 43 43 newHeaderImage = signal<string | null>(null) ··· 45 45 46 46 blog: Signal<BlogDetails | undefined> 47 47 avatarUrl = computed<string>(() => { 48 - if (this.newAvatarImage() != null) return this.newAvatarImage()! 49 - return this.blog() 50 - ? EnvironmentService.environment.externalCacheurl + 48 + if (this.newAvatarImage() != null) return this.newAvatarImage()! 49 + return this.blog() 50 + ? EnvironmentService.environment.externalCacheurl + 51 51 encodeURIComponent(EnvironmentService.environment.baseMediaUrl + this.blog()?.avatar) 52 - : '' 53 - } 54 - ) 52 + : '' 53 + }) 55 54 headerUrl = computed<string>(() => { 56 - if (this.newHeaderImage() != null) return this.newHeaderImage()! 57 - return this.blog() 58 - ? EnvironmentService.environment.externalCacheurl + 55 + if (this.newHeaderImage() != null) return this.newHeaderImage()! 56 + return this.blog() 57 + ? EnvironmentService.environment.externalCacheurl + 59 58 encodeURIComponent(EnvironmentService.environment.baseMediaUrl + this.blog()?.headerImage) 60 - : '' 61 - } 62 - ) 59 + : '' 60 + }) 63 61 64 62 imageIcon = faImage 65 63 addIcon = faPlus
+1 -3
packages/frontend/src/app/pages/settings/settings.component.ts
··· 9 9 import { TranslateModule } from '@ngx-translate/core' 10 10 import { GroupedSettingData, SettingData, SettingsService, SettingValues } from 'src/app/services/settings.service' 11 11 import { SettingsLoaderComponent } from './settings-loader/settings-loader.component' 12 - import { Title } from '@angular/platform-browser' 13 - import { GlobalData } from 'src/app/services/global-data.service' 14 12 import { SimpleTitleService } from 'src/app/services/simple-title.service' 15 13 16 14 export const SETTINGS_TOKEN = new InjectionToken('settings token') ··· 31 29 }) 32 30 export class SettingsComponent { 33 31 data: SettingData 34 - values: SettingValues = {} 32 + values 35 33 groups: GroupedSettingData[] 36 34 37 35 // Portal related
+6 -7
packages/frontend/src/app/pages/view-blog/view-blog.component.ts
··· 1 - import { Component, computed, OnDestroy, OnInit, signal, WritableSignal } from '@angular/core' 1 + import { Component, OnDestroy, OnInit, signal, WritableSignal } from '@angular/core' 2 2 import { Meta } from '@angular/platform-browser' 3 - import { ActivatedRoute, Router } from '@angular/router' 3 + import { ActivatedRoute } from '@angular/router' 4 4 import { 5 5 faArrowUpRightFromSquare, 6 6 faClockRotateLeft, ··· 17 17 import { LoginService } from 'src/app/services/login.service' 18 18 import { ThemeService } from 'src/app/services/theme.service' 19 19 20 - import { MatDialog } from '@angular/material/dialog' 21 20 import { BlogDetails } from 'src/app/interfaces/blogDetails' 22 21 import { EnvironmentService } from 'src/app/services/environment.service' 23 22 import { SimplifiedUser } from 'src/app/interfaces/simplified-user' ··· 186 185 if (!userHasCustomTheme || userIsSelf) return 187 186 188 187 // Easiest case, they want custom CSS 189 - if (this.settingService.values.useOtherUserCustomThemes) { 188 + if (this.settingService.values().useOtherUserCustomThemes) { 190 189 this.themeService.customCSS.set(blogDetails.id) 191 190 return 192 191 } 193 192 194 193 // Check if we should ask 195 - if (!this.settingService.values.askToUseOtherUserCustomThemes) return 194 + if (!this.settingService.values().askToUseOtherUserCustomThemes) return 196 195 197 196 const res = await this.simpleDialog.createCustomOptionDialog({ 198 197 title: 'dialog.blog.customThemeTitle', ··· 205 204 }) 206 205 207 206 if (res === 'confirm') { 208 - this.settingService.values.useOtherUserCustomThemes = true 207 + this.settingService.values().useOtherUserCustomThemes = true 209 208 this.settingService.forceUpdateValue('useOtherUserCustomThemes') 210 209 this.themeService.customCSS.set(blogDetails.id) 211 210 } 212 211 if (res === 'cancelRemember') { 213 - this.settingService.values.askToUseOtherUserCustomThemes = false 212 + this.settingService.values().askToUseOtherUserCustomThemes = false 214 213 this.settingService.forceUpdateValue('askToUseOtherUserCustomThemes') 215 214 } 216 215 }
+9 -6
packages/frontend/src/app/services/notifications.service.ts
··· 94 94 this.asks.set(res.asks) 95 95 96 96 // Check for any notification changes to play the audio 97 - if (previousCount < this.totalNotifications() && this.settings.values.disableSounds !== true) { 97 + if (previousCount < this.totalNotifications() && this.settings.values().disableSounds !== true) { 98 98 this.audioService.playSound('notification') 99 99 } 100 100 } ··· 177 177 } 178 178 const emoji = notification.emojiReactionId 179 179 ? this.emojiMap.get( 180 - petition.emojiRelations.postEmojiReactions.find((elem) => elem.id == notification.emojiReactionId) 181 - ?.emojiId as string 182 - ) 180 + petition.emojiRelations.postEmojiReactions.find((elem) => elem.id == notification.emojiReactionId) 181 + ?.emojiId as string 182 + ) 183 183 : undefined 184 184 let notificationProcessed: UserNotifications = { 185 185 type: notification.notificationType, 186 - url: notification.notificationType === 'FOLLOW' || 'USERBITE' ? `/blog/${usr.url}` : `/post/${notification.postId}`, 186 + url: 187 + notification.notificationType === 'FOLLOW' || 'USERBITE' 188 + ? `/blog/${usr.url}` 189 + : `/post/${notification.postId}`, 187 190 avatar: usr.avatar, 188 191 userUrl: usr.url, 189 192 userName: usr.name, ··· 194 197 ? emoji.name 195 198 : notification.notificationType === 'EMOJIREACT' 196 199 ? petition.emojiRelations.postEmojiReactions.find((elem) => elem.id == notification.emojiReactionId) 197 - ?.content 200 + ?.content 198 201 : undefined 199 202 } 200 203 return notificationProcessed
+4 -4
packages/frontend/src/app/services/particle.service.ts
··· 13 13 location?: { event?: MouseEvent; scroll?: { x: number; y: number } } 14 14 config?: RecursivePartial<ConfettiOptions> 15 15 }) { 16 - if (this.settings.values.disableConfetti === true) return 16 + if (this.settings.values().disableConfetti === true) return 17 17 18 18 const defaultConfig: ConfettiOptions = { 19 19 zIndex: 1000, 20 20 scalar: 8, 21 - flat: (this.settings.values.flatConfetti as boolean | undefined) ?? false 21 + flat: (this.settings.values().flatConfetti as boolean | undefined) ?? false 22 22 } 23 23 24 24 // Attempt to place the confetti on the clicked location ··· 38 38 const confettiConfig = Object.assign( 39 39 defaultConfig, 40 40 { 41 - particleCount: Math.floor(10 * Number(this.settings.values.confettiMultiplier ?? 1)), 41 + particleCount: Math.floor(10 * Number(this.settings.values().confettiMultiplier ?? 1)), 42 42 spread: 360, 43 43 startVelocity: 20, 44 44 origin: { ··· 57 57 const conf = Object.assign( 58 58 defaultConfig, 59 59 { 60 - particleCount: Math.floor(10 * Number(this.settings.values.confettiMultiplier ?? 1)), 60 + particleCount: Math.floor(10 * Number(this.settings.values().confettiMultiplier ?? 1)), 61 61 spread: 60, 62 62 startVelocity: 60 63 63 },
+32 -24
packages/frontend/src/app/services/settings.service.ts
··· 1 - import { computed, Injectable, Injector, signal } from '@angular/core' 1 + import { computed, Injectable, Injector, signal, WritableSignal } from '@angular/core' 2 2 import { DashboardService } from './dashboard.service' 3 3 import { JwtService } from './jwt.service' 4 4 import { ··· 114 114 dropListData?: DropListData // For type 'list' 115 115 convertFromStorage?: (stored: string) => SettingValueType 116 116 convertToStorage?: (value: SettingValueType) => string 117 + enableIfSetting?: (s: SettingValues) => boolean 117 118 } 118 119 119 120 // Data on each setting to generate form controls ··· 440 441 type: 'checkbox', 441 442 default: false, 442 443 convertFromStorage: (val) => val === '1', 443 - convertToStorage: () => this.convertAsksTo() 444 + convertToStorage: () => this.convertAsksTo(), 445 + enableIfSetting: (s) => s.enableAsks === true 444 446 }, 445 447 displayMentionsOfBlockedUsersFromOtherUsers: { 446 448 key: 'displayMentionsOfBlockedUsersFromOtherUsers', ··· 592 594 [Annoyance.none]: 'settings.confirmOpenCwAnnoyanceOptions.none', 593 595 [Annoyance.timeout]: 'settings.confirmOpenCwAnnoyanceOptions.timeout', 594 596 [Annoyance.fifteen]: 'settings.confirmOpenCwAnnoyanceOptions.fifteen' 595 - } 597 + }, 598 + enableIfSetting: (s) => s.confirmOpenCw === true 596 599 } 597 600 } 598 601 // Generates settings sidebar links and gives the settings-loader pages their data through values ··· 690 693 { type: 'header', value: 'settings.header.cwBehavior' }, 691 694 { type: 'key', value: 'disableCW' }, 692 695 { type: 'key', value: 'confirmOpenCw' }, 693 - { type: 'key', value: 'confirmOpenCwAnnoyance' }, 696 + { 697 + type: 'key', 698 + value: 'confirmOpenCwAnnoyance' 699 + }, 694 700 { type: 'key', value: 'disableNSFWFilter' }, 695 701 { type: 'key', value: 'hideNoDescriptionMedia' }, 696 702 { type: 'key', value: 'disableForceAltText' } ··· 765 771 ] 766 772 } 767 773 ] 768 - public values: SettingValues 774 + public values: WritableSignal<SettingValues> 769 775 770 776 public fediAttachments: FediAttachment[] = [{ name: '', value: '' }] // Only shown before load completes 771 777 public avatar: File | undefined // Only set when updating ··· 785 791 private jwtService: JwtService 786 792 ) { 787 793 // Set defaults from local storage over global defaults 788 - this.values = Object.assign(this.getDefaultSettings(), this.getLocalStorageValues()) 794 + // Uses an evil hack on the equal property to allow for "deep" checks (manually calling update) to notify dependents 795 + // Svelte has this by default :( 796 + this.values = signal(Object.assign(this.getDefaultSettings(), this.getLocalStorageValues()), { equal: () => false }) 789 797 790 798 // Load blog details 791 799 const userBlog = this.jwtService.getTokenData() 792 800 if (userBlog) { 793 801 this.dashboardService.getBlogDetails(userBlog.url, true).then((blogDetails) => { 794 - this.values.name = blogDetails.name 795 - this.values.description = blogDetails.descriptionMarkdown 796 - this.values.manuallyAcceptsFollows = blogDetails.manuallyAcceptsFollows 797 - this.values.hideFollows = blogDetails.hideFollows 798 - this.values.hideProfileNotLoggedIn = blogDetails.hideProfileNotLoggedIn 799 - this.values.disableEmailNotifications = blogDetails.disableEmailNotifications 802 + this.values().name = blogDetails.name 803 + this.values().description = blogDetails.descriptionMarkdown 804 + this.values().manuallyAcceptsFollows = blogDetails.manuallyAcceptsFollows 805 + this.values().hideFollows = blogDetails.hideFollows 806 + this.values().hideProfileNotLoggedIn = blogDetails.hideProfileNotLoggedIn 807 + this.values().disableEmailNotifications = blogDetails.disableEmailNotifications 800 808 801 809 const rawAttachments = blogDetails.publicOptions?.find( 802 810 (elem) => elem.optionName === 'fediverse.public.attachment' ··· 816 824 817 825 // Update settings when logging in (and notify everyone) 818 826 loginService.loggedIn.pipe(filter((logged) => logged)).subscribe(() => { 819 - this.values = Object.assign(this.getDefaultSettings(), this.getLocalStorageValues()) 827 + this.values.set(Object.assign(this.getDefaultSettings(), this.getLocalStorageValues())) 820 828 this.settingsLoadedFromLogin.next() 821 829 }) 822 830 ··· 824 832 fromEvent(window, 'storage') 825 833 .pipe(debounceTime(500)) 826 834 .subscribe(() => { 827 - this.values = Object.assign(this.getDefaultSettings(), this.getLocalStorageValues()) 835 + this.values.set(Object.assign(this.getDefaultSettings(), this.getLocalStorageValues())) 828 836 }) 829 837 } 830 838 ··· 886 894 const payload = { 887 895 avatar: this.avatar, 888 896 headerImage: this.headerImage, 889 - name: this.values.name, 890 - description: this.values.description, 891 - manuallyAcceptsFollows: this.values.manuallyAcceptsFollows, 892 - hideFollows: this.values.hideFollows, 893 - hideProfileNotLoggedIn: this.values.hideProfileNotLoggedIn, 894 - disableEmailNotifications: this.values.disableEmailNotifications, 897 + name: this.values().name, 898 + description: this.values().description, 899 + manuallyAcceptsFollows: this.values().manuallyAcceptsFollows, 900 + hideFollows: this.values().hideFollows, 901 + hideProfileNotLoggedIn: this.values().hideProfileNotLoggedIn, 902 + disableEmailNotifications: this.values().disableEmailNotifications, 895 903 options: JSON.stringify(options) 896 904 } 897 905 ··· 927 935 } 928 936 929 937 private getSettingValueAsString(key: SettingKey): string { 930 - const value = this.values[key] 938 + const value = this.values()[key] 931 939 if (value === undefined) return '' 932 940 if (this.data[key].convertToStorage) { 933 941 return this.data[key].convertToStorage(value) ··· 970 978 if (updateLocalStorage) { 971 979 keyList.forEach((key) => { 972 980 const localStorageKey = this.data[key].localStorageKey 973 - const newValue = this.values[key] 981 + const newValue = this.values()[key] 974 982 if (localStorageKey && newValue !== undefined) { 975 983 localStorage.setItem(localStorageKey, this.getSettingValueAsString(key)) 976 984 } ··· 1054 1062 } 1055 1063 1056 1064 convertAsksTo(): string { 1057 - if (this.values.enableAsks && this.values.enableAnonymousAsks) { 1065 + if (this.values().enableAsks && this.values().enableAnonymousAsks) { 1058 1066 return '1' 1059 - } else if (this.values.enableAsks) { 1067 + } else if (this.values().enableAsks) { 1060 1068 return '2' 1061 1069 } else { 1062 1070 return '3'
+10 -7
packages/frontend/src/app/services/theme.service.ts
··· 207 207 } 208 208 209 209 setup() { 210 - const theme = this.settingService.values.theme 210 + const theme = this.settingService.values().theme 211 211 if (typeof theme === 'string' && isTheme(theme)) { 212 212 this.setTheme(theme) 213 213 } 214 214 215 - const darkLightMode = this.settingService.values.lightDarkMode 215 + const darkLightMode = this.settingService.values().lightDarkMode 216 216 if (typeof darkLightMode === 'string' && isLightDarkMode(darkLightMode)) { 217 217 this.setLightDarkMode(darkLightMode) 218 218 } 219 219 220 - const settingAdditionalStyleModes = this.settingService.values.additionalStyleModes 220 + const settingAdditionalStyleModes = this.settingService.values().additionalStyleModes 221 221 if (Array.isArray(settingAdditionalStyleModes) && isAdditionalStyleMode(settingAdditionalStyleModes)) { 222 222 additionalStyleModeVariants.forEach((mode) => { 223 223 const enabled = settingAdditionalStyleModes.includes(mode) ··· 235 235 236 236 public setTheme = async (theme: Theme, doNotSavePreference = false) => { 237 237 this.theme.set(theme) 238 - this.settingService.values.theme = theme 238 + this.settingService.values().theme = theme 239 + this.settingService.values.update((v) => v) 239 240 240 241 // Forced lightDarkMode 241 242 if (themeData[theme]?.compatibility === 'light') await this.setLightDarkMode('light') ··· 246 247 247 248 public setLightDarkMode = async (lightDarkMode: LightDarkMode, doNotSavePreference = false) => { 248 249 this.lightDarkMode.set(lightDarkMode) 249 - this.settingService.values.lightDarkMode = lightDarkMode 250 + this.settingService.values().lightDarkMode = lightDarkMode 251 + this.settingService.values.update((v) => v) 250 252 251 253 document.documentElement.setAttribute('data-theme', lightDarkMode) 252 254 this.settingService.forceUpdateValue('lightDarkMode', !doNotSavePreference) ··· 272 274 .filter(([_, enabled]) => enabled()) 273 275 .map(([val, _]) => val) 274 276 275 - this.settingService.values.additionalStyleModes = modes 277 + this.settingService.values().additionalStyleModes = modes 278 + this.settingService.values.update((v) => v) 276 279 } 277 280 278 281 public async toggleAdditionalStyleMode(mode: AdditionalStyleMode, doNotSavePreference = false) { ··· 297 300 } 298 301 299 302 // Someone else's CSS, check if we want to use it and if it exists 300 - if (this.settingService.values.useOtherUserCustomThemes !== true) return 303 + if (this.settingService.values().useOtherUserCustomThemes !== true) return 301 304 302 305 const themeExists = await this.themeExists(this.customCSS()) 303 306 if (!themeExists) return