AppearanceSettingsView.swift (10221B)
1 // 2 // TextFormattingSettings.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-04-05. 6 // 7 8 import SwiftUI 9 10 fileprivate let CACHE_CLEAR_BUTTON_RESET_TIME_IN_SECONDS: Double = 60 11 fileprivate let MINIMUM_CACHE_CLEAR_BUTTON_DELAY_IN_SECONDS: Double = 1 12 13 /// A simple type to keep track of the cache clearing state 14 fileprivate enum CacheClearingState { 15 case not_cleared 16 case clearing 17 case cleared 18 } 19 20 struct ResizedEventPreview: View { 21 let damus_state: DamusState 22 @ObservedObject var settings: UserSettingsStore 23 24 var body: some View { 25 EventView(damus: damus_state, event: test_note, pubkey: test_note.pubkey, options: [.wide, .no_action_bar]) 26 } 27 } 28 29 struct AppearanceSettingsView: View { 30 let damus_state: DamusState 31 @ObservedObject var settings: UserSettingsStore 32 @Environment(\.dismiss) var dismiss 33 @State fileprivate var cache_clearing_state: CacheClearingState = .not_cleared 34 @State var showing_cache_clear_alert: Bool = false 35 36 @State var showing_enable_animation_alert: Bool = false 37 @State var enable_animation_toggle_is_user_initiated: Bool = true 38 39 var FontSize: some View { 40 VStack(alignment: .leading) { 41 Slider(value: $settings.font_size, in: 0.5...2.0, step: 0.1) 42 .padding() 43 44 // Sample text to show how the font size would look 45 ResizedEventPreview(damus_state: damus_state, settings: settings) 46 47 } 48 } 49 50 var body: some View { 51 Form { 52 Section(NSLocalizedString("Font Size", comment: "Section label for font size settings.")) { 53 FontSize 54 } 55 56 // MARK: - Text Truncation 57 Section(header: Text(NSLocalizedString("Text Truncation", comment: "Section header for damus text truncation user configuration"))) { 58 Toggle(NSLocalizedString("Truncate timeline text", comment: "Setting to truncate text in timeline"), isOn: $settings.truncate_timeline_text) 59 .toggleStyle(.switch) 60 Toggle(NSLocalizedString("Truncate notification mention text", comment: "Setting to truncate text in mention notifications"), isOn: $settings.truncate_mention_text) 61 .toggleStyle(.switch) 62 } 63 64 Section(header: Text("User Statuses", comment: "Section header for user profile status settings.")) { 65 Toggle(NSLocalizedString("Show general statuses", comment: "Settings toggle for enabling general user statuses"), isOn: $settings.show_general_statuses) 66 .toggleStyle(.switch) 67 68 Toggle(NSLocalizedString("Show music statuses", comment: "Settings toggle for enabling now playing music statuses"), isOn: $settings.show_music_statuses) 69 .toggleStyle(.switch) 70 } 71 72 // MARK: - Accessibility 73 Section(header: Text(NSLocalizedString("Accessibility", comment: "Section header for accessibility settings"))) { 74 Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $settings.left_handed) 75 .toggleStyle(.switch) 76 } 77 78 // MARK: - Images 79 Section(NSLocalizedString("Images", comment: "Section title for images configuration.")) { 80 self.EnableAnimationsToggle 81 Toggle(NSLocalizedString("Blur images", comment: "Setting to blur images"), isOn: $settings.blur_images) 82 .toggleStyle(.switch) 83 84 Toggle(NSLocalizedString("Media previews", comment: "Setting to show media"), isOn: $settings.media_previews) 85 .toggleStyle(.switch) 86 87 Picker(NSLocalizedString("Image uploader", comment: "Prompt selection of user's image uploader"), 88 selection: $settings.default_media_uploader) { 89 ForEach(MediaUploader.allCases, id: \.self) { uploader in 90 Text(uploader.model.displayName) 91 .tag(uploader.model.tag) 92 } 93 } 94 95 self.ClearCacheButton 96 } 97 98 // MARK: - Content filters and moderation 99 Section( 100 header: Text(NSLocalizedString("Content filters", comment: "Section title for content filtering/moderation configuration.")), 101 footer: Text(NSLocalizedString("Notes with the #nsfw tag usually contains adult content or other \"Not safe for work\" content", comment: "Section footer clarifying what #nsfw (not safe for work) tags mean")) 102 ) { 103 Toggle(NSLocalizedString("Hide notes with #nsfw tags", comment: "Setting to hide notes with the #nsfw (not safe for work) tags"), isOn: $settings.hide_nsfw_tagged_content) 104 .toggleStyle(.switch) 105 } 106 107 // MARK: - Profiles 108 Section( 109 header: Text(NSLocalizedString("Profiles", comment: "Section title for profile view configuration.")), 110 footer: Text(NSLocalizedString("Profile action sheets allow you to follow, zap, or DM profiles more quickly without having to view their full profile", comment: "Section footer clarifying what the profile action sheet feature does")) 111 ) { 112 Toggle(NSLocalizedString("Show profile action sheets", comment: "Setting to show profile action sheets when clicking on a user's profile picture"), isOn: $settings.show_profile_action_sheet_on_pfp_click) 113 .toggleStyle(.switch) 114 } 115 116 117 } 118 .navigationTitle(NSLocalizedString("Appearance", comment: "Navigation title for text and appearance settings.")) 119 .onReceive(handle_notify(.switched_timeline)) { _ in 120 dismiss() 121 } 122 } 123 124 func clear_cache_button_action() { 125 cache_clearing_state = .clearing 126 127 let group = DispatchGroup() 128 129 group.enter() 130 DamusCacheManager.shared.clear_cache(damus_state: self.damus_state, completion: { 131 group.leave() 132 }) 133 134 // Make clear cache button take at least a second or so to avoid issues with labor perception bias (https://growth.design/case-studies/labor-perception-bias) 135 group.enter() 136 DispatchQueue.main.asyncAfter(deadline: .now() + MINIMUM_CACHE_CLEAR_BUTTON_DELAY_IN_SECONDS) { 137 group.leave() 138 } 139 140 group.notify(queue: .main) { 141 cache_clearing_state = .cleared 142 DispatchQueue.main.asyncAfter(deadline: .now() + CACHE_CLEAR_BUTTON_RESET_TIME_IN_SECONDS) { 143 cache_clearing_state = .not_cleared 144 } 145 } 146 } 147 148 var EnableAnimationsToggle: some View { 149 Toggle(NSLocalizedString("Animations", comment: "Toggle to enable or disable image animation"), isOn: $settings.enable_animation) 150 .toggleStyle(.switch) 151 .onChange(of: settings.enable_animation) { _ in 152 if self.enable_animation_toggle_is_user_initiated { 153 self.showing_enable_animation_alert = true 154 } 155 else { 156 self.enable_animation_toggle_is_user_initiated = true 157 } 158 } 159 .alert(isPresented: $showing_enable_animation_alert) { 160 Alert(title: Text(NSLocalizedString("Confirmation", comment: "Confirmation dialog title")), 161 message: Text(NSLocalizedString("Changing this setting will cause the cache to be cleared. This will free space, but images may take longer to load again. Are you sure you want to proceed?", comment: "Message explaining consequences of changing the 'enable animation' setting")), 162 primaryButton: .default(Text(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) { 163 self.clear_cache_button_action() 164 }, 165 secondaryButton: .cancel() { 166 // Toggle back if user cancels action 167 self.enable_animation_toggle_is_user_initiated = false 168 settings.enable_animation.toggle() 169 } 170 ) 171 } 172 } 173 174 var ClearCacheButton: some View { 175 Button(action: { self.showing_cache_clear_alert = true }, label: { 176 HStack(spacing: 6) { 177 switch cache_clearing_state { 178 case .not_cleared: 179 Text(NSLocalizedString("Clear Cache", comment: "Button to clear image cache.")) 180 case .clearing: 181 ProgressView() 182 Text(NSLocalizedString("Clearing Cache", comment: "Loading message indicating that the cache is being cleared.")) 183 case .cleared: 184 Image(systemName: "checkmark.circle.fill") 185 .foregroundColor(.green) 186 Text(NSLocalizedString("Cache has been cleared", comment: "Message indicating that the cache was successfully cleared.")) 187 } 188 } 189 }) 190 .disabled(self.cache_clearing_state != .not_cleared) 191 .alert(isPresented: $showing_cache_clear_alert) { 192 Alert(title: Text(NSLocalizedString("Confirmation", comment: "Confirmation dialog title")), 193 message: Text(NSLocalizedString("Are you sure you want to clear the cache? This will free space, but images may take longer to load again.", comment: "Message explaining what it means to clear the cache, asking if user wants to proceed.")), 194 primaryButton: .default(Text(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) { 195 self.clear_cache_button_action() 196 }, 197 secondaryButton: .cancel()) 198 } 199 } 200 } 201 202 203 struct TextFormattingSettings_Previews: PreviewProvider { 204 static var previews: some View { 205 AppearanceSettingsView(damus_state: test_damus_state, settings: UserSettingsStore()) 206 } 207 }