Add opposite collapse keywords preference
This commit is contained in:
parent
eb4e6e32f7
commit
4ac76ab672
|
@ -231,6 +231,7 @@
|
||||||
D6B053AB23BD2F1400A066FA /* AssetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A923BD2F1400A066FA /* AssetCollectionViewCell.swift */; };
|
D6B053AB23BD2F1400A066FA /* AssetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053A923BD2F1400A066FA /* AssetCollectionViewCell.swift */; };
|
||||||
D6B053AC23BD2F1400A066FA /* AssetCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6B053AA23BD2F1400A066FA /* AssetCollectionViewCell.xib */; };
|
D6B053AC23BD2F1400A066FA /* AssetCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6B053AA23BD2F1400A066FA /* AssetCollectionViewCell.xib */; };
|
||||||
D6B053AE23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053AD23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift */; };
|
D6B053AE23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B053AD23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift */; };
|
||||||
|
D6B17255254F88B800128392 /* OppositeCollapseKeywordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */; };
|
||||||
D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */; };
|
D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */; };
|
||||||
D6B4A4FF2506B81A000C81C1 /* AccountDisplayNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */; };
|
D6B4A4FF2506B81A000C81C1 /* AccountDisplayNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */; };
|
||||||
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; };
|
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; };
|
||||||
|
@ -281,6 +282,7 @@
|
||||||
D6E426B9253382B300C02E1C /* SearchResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E426B8253382B300C02E1C /* SearchResultType.swift */; };
|
D6E426B9253382B300C02E1C /* SearchResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E426B8253382B300C02E1C /* SearchResultType.swift */; };
|
||||||
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26221603F8B006A8599 /* CharacterCounter.swift */; };
|
||||||
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
D6E6F26521604242006A8599 /* CharacterCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E6F26421604242006A8599 /* CharacterCounterTests.swift */; };
|
||||||
|
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */; };
|
||||||
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */; };
|
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */; };
|
||||||
D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBF01623C55E0D00AE061B /* UISceneSession+MastodonController.swift */; };
|
D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBF01623C55E0D00AE061B /* UISceneSession+MastodonController.swift */; };
|
||||||
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */; };
|
D6F0B12B24A3071C001E48C3 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */; };
|
||||||
|
@ -570,6 +572,7 @@
|
||||||
D6B053A923BD2F1400A066FA /* AssetCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6B053A923BD2F1400A066FA /* AssetCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6B053AA23BD2F1400A066FA /* AssetCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AssetCollectionViewCell.xib; sourceTree = "<group>"; };
|
D6B053AA23BD2F1400A066FA /* AssetCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AssetCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||||
D6B053AD23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPickerSheetContainerViewController.swift; sourceTree = "<group>"; };
|
D6B053AD23BD322B00A066FA /* AssetPickerSheetContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPickerSheetContainerViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OppositeCollapseKeywordsView.swift; sourceTree = "<group>"; };
|
||||||
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrayscalifier.swift; sourceTree = "<group>"; };
|
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrayscalifier.swift; sourceTree = "<group>"; };
|
||||||
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayNameLabel.swift; sourceTree = "<group>"; };
|
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDisplayNameLabel.swift; sourceTree = "<group>"; };
|
||||||
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = "<group>"; };
|
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -626,6 +629,7 @@
|
||||||
D6E4885C24A2890C0011C13E /* Tusker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tusker.entitlements; sourceTree = "<group>"; };
|
D6E4885C24A2890C0011C13E /* Tusker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tusker.entitlements; sourceTree = "<group>"; };
|
||||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounter.swift; sourceTree = "<group>"; };
|
||||||
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
D6E6F26421604242006A8599 /* CharacterCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCounterTests.swift; sourceTree = "<group>"; };
|
||||||
|
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusableTextField.swift; sourceTree = "<group>"; };
|
||||||
D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Scenes.swift"; sourceTree = "<group>"; };
|
D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Scenes.swift"; sourceTree = "<group>"; };
|
||||||
D6EBF01623C55E0D00AE061B /* UISceneSession+MastodonController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISceneSession+MastodonController.swift"; sourceTree = "<group>"; };
|
D6EBF01623C55E0D00AE061B /* UISceneSession+MastodonController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISceneSession+MastodonController.swift"; sourceTree = "<group>"; };
|
||||||
D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
|
D6F0B12A24A3071C001E48C3 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1049,6 +1053,7 @@
|
||||||
04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
|
04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
|
||||||
04586B4222B301470021BD04 /* AppearancePrefsView.swift */,
|
04586B4222B301470021BD04 /* AppearancePrefsView.swift */,
|
||||||
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
|
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
|
||||||
|
D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */,
|
||||||
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */,
|
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */,
|
||||||
D68015412401A74600D6103B /* MediaPrefsView.swift */,
|
D68015412401A74600D6103B /* MediaPrefsView.swift */,
|
||||||
D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */,
|
D6BC9DB2232D4C07002CA326 /* WellnessPrefsView.swift */,
|
||||||
|
@ -1303,6 +1308,7 @@
|
||||||
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */,
|
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */,
|
||||||
D6E426802532814100C02E1C /* MaybeLazyStack.swift */,
|
D6E426802532814100C02E1C /* MaybeLazyStack.swift */,
|
||||||
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
|
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
|
||||||
|
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
|
||||||
D67C57A721E2649B00C3118B /* Account Detail */,
|
D67C57A721E2649B00C3118B /* Account Detail */,
|
||||||
D626494023C122C800612E6E /* Asset Picker */,
|
D626494023C122C800612E6E /* Asset Picker */,
|
||||||
D61959D0241E842400A37B8E /* Draft Cell */,
|
D61959D0241E842400A37B8E /* Draft Cell */,
|
||||||
|
@ -1870,6 +1876,7 @@
|
||||||
D677284C24ECBE9100C732D3 /* ComposeAvatarImageView.swift in Sources */,
|
D677284C24ECBE9100C732D3 /* ComposeAvatarImageView.swift in Sources */,
|
||||||
D6BC9DDA232D8BE5002CA326 /* SearchResultsViewController.swift in Sources */,
|
D6BC9DDA232D8BE5002CA326 /* SearchResultsViewController.swift in Sources */,
|
||||||
D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */,
|
D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */,
|
||||||
|
D6B17255254F88B800128392 /* OppositeCollapseKeywordsView.swift in Sources */,
|
||||||
D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */,
|
D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */,
|
||||||
D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */,
|
D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */,
|
||||||
D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */,
|
D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */,
|
||||||
|
@ -1878,6 +1885,7 @@
|
||||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
||||||
D627943223A5466600D38C68 /* SelectableTableViewCell.swift in Sources */,
|
D627943223A5466600D38C68 /* SelectableTableViewCell.swift in Sources */,
|
||||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||||
|
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */,
|
||||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
||||||
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */,
|
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */,
|
||||||
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */,
|
||||||
|
|
|
@ -23,8 +23,23 @@ extension StatusState {
|
||||||
|
|
||||||
let contentWarningCollapsible = !status.spoilerText.isEmpty
|
let contentWarningCollapsible = !status.spoilerText.isEmpty
|
||||||
|
|
||||||
|
let collapseDueToContentWarning: Bool?
|
||||||
|
if contentWarningCollapsible {
|
||||||
|
let lowercased = status.spoilerText.lowercased()
|
||||||
|
let opposite = Preferences.shared.oppositeCollapseKeywords.contains { lowercased.contains($0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()) }
|
||||||
|
|
||||||
|
if Preferences.shared.expandAllContentWarnings {
|
||||||
|
collapseDueToContentWarning = opposite
|
||||||
|
} else {
|
||||||
|
collapseDueToContentWarning = !opposite
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collapseDueToContentWarning = nil
|
||||||
|
}
|
||||||
|
|
||||||
self.collapsible = contentWarningCollapsible || longEnoughToCollapse
|
self.collapsible = contentWarningCollapsible || longEnoughToCollapse
|
||||||
self.collapsed = longEnoughToCollapse || (!Preferences.shared.expandAllContentWarnings && contentWarningCollapsible)
|
// use ?? instead of || because the content warnig pref takes priority over length
|
||||||
|
self.collapsed = collapseDueToContentWarning ?? longEnoughToCollapse
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,16 +55,13 @@ class Preferences: Codable, ObservableObject {
|
||||||
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
|
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
|
||||||
self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
|
self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
|
||||||
self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode)
|
self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode)
|
||||||
if container.contains(.expandAllContentWarnings) {
|
self.expandAllContentWarnings = try container.decodeIfPresent(Bool.self, forKey: .expandAllContentWarnings) ?? false
|
||||||
self.expandAllContentWarnings = try container.decode(Bool.self, forKey: .expandAllContentWarnings)
|
self.collapseLongPosts = try container.decodeIfPresent(Bool.self, forKey: .collapseLongPosts) ?? true
|
||||||
}
|
self.oppositeCollapseKeywords = try container.decodeIfPresent([String].self, forKey: .oppositeCollapseKeywords) ?? []
|
||||||
if container.contains(.collapseLongPosts) {
|
|
||||||
self.collapseLongPosts = try container.decode(Bool.self, forKey: .collapseLongPosts)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.showFavoriteAndReblogCounts = try container.decode(Bool.self, forKey: .showFavoriteAndReblogCounts)
|
self.showFavoriteAndReblogCounts = try container.decode(Bool.self, forKey: .showFavoriteAndReblogCounts)
|
||||||
self.defaultNotificationsMode = try container.decode(NotificationsMode.self, forKey: .defaultNotificationsType)
|
self.defaultNotificationsMode = try container.decode(NotificationsMode.self, forKey: .defaultNotificationsType)
|
||||||
self.grayscaleImages = try container.decode(Bool.self, forKey: .grayscaleImages)
|
self.grayscaleImages = try container.decodeIfPresent(Bool.self, forKey: .grayscaleImages) ?? false
|
||||||
|
|
||||||
self.silentActions = try container.decode([String: Permission].self, forKey: .silentActions)
|
self.silentActions = try container.decode([String: Permission].self, forKey: .silentActions)
|
||||||
self.statusContentType = try container.decode(StatusContentType.self, forKey: .statusContentType)
|
self.statusContentType = try container.decode(StatusContentType.self, forKey: .statusContentType)
|
||||||
|
@ -93,6 +90,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode)
|
try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode)
|
||||||
try container.encode(expandAllContentWarnings, forKey: .expandAllContentWarnings)
|
try container.encode(expandAllContentWarnings, forKey: .expandAllContentWarnings)
|
||||||
try container.encode(collapseLongPosts, forKey: .collapseLongPosts)
|
try container.encode(collapseLongPosts, forKey: .collapseLongPosts)
|
||||||
|
try container.encode(oppositeCollapseKeywords, forKey: .oppositeCollapseKeywords)
|
||||||
|
|
||||||
try container.encode(showFavoriteAndReblogCounts, forKey: .showFavoriteAndReblogCounts)
|
try container.encode(showFavoriteAndReblogCounts, forKey: .showFavoriteAndReblogCounts)
|
||||||
try container.encode(defaultNotificationsMode, forKey: .defaultNotificationsType)
|
try container.encode(defaultNotificationsMode, forKey: .defaultNotificationsType)
|
||||||
|
@ -126,6 +124,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
@Published var inAppSafariAutomaticReaderMode = false
|
@Published var inAppSafariAutomaticReaderMode = false
|
||||||
@Published var expandAllContentWarnings = false
|
@Published var expandAllContentWarnings = false
|
||||||
@Published var collapseLongPosts = true
|
@Published var collapseLongPosts = true
|
||||||
|
@Published var oppositeCollapseKeywords: [String] = []
|
||||||
|
|
||||||
// MARK: Digital Wellness
|
// MARK: Digital Wellness
|
||||||
@Published var showFavoriteAndReblogCounts = true
|
@Published var showFavoriteAndReblogCounts = true
|
||||||
|
@ -157,6 +156,7 @@ class Preferences: Codable, ObservableObject {
|
||||||
case inAppSafariAutomaticReaderMode
|
case inAppSafariAutomaticReaderMode
|
||||||
case expandAllContentWarnings
|
case expandAllContentWarnings
|
||||||
case collapseLongPosts
|
case collapseLongPosts
|
||||||
|
case oppositeCollapseKeywords
|
||||||
|
|
||||||
case showFavoriteAndReblogCounts
|
case showFavoriteAndReblogCounts
|
||||||
case defaultNotificationsType
|
case defaultNotificationsType
|
||||||
|
|
|
@ -36,12 +36,16 @@ struct BehaviorPrefsView: View {
|
||||||
|
|
||||||
var contentWarningsSection: some View {
|
var contentWarningsSection: some View {
|
||||||
Section(header: Text("Content Warnings")) {
|
Section(header: Text("Content Warnings")) {
|
||||||
|
Toggle(isOn: $preferences.collapseLongPosts) {
|
||||||
|
Text("Collapse Long Posts")
|
||||||
|
}
|
||||||
|
|
||||||
Toggle(isOn: $preferences.expandAllContentWarnings) {
|
Toggle(isOn: $preferences.expandAllContentWarnings) {
|
||||||
Text("Expand All Content Warnings")
|
Text("Expand All Content Warnings")
|
||||||
}
|
}
|
||||||
|
|
||||||
Toggle(isOn: $preferences.collapseLongPosts) {
|
NavigationLink(destination: OppositeCollapseKeywordsView()) {
|
||||||
Text("Collapse Long Posts")
|
Text(preferences.expandAllContentWarnings ? "Collapse Posts with Keywords in CWs" : "Expand Posts with Keywords in CWs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// OppositeCollapseKeywordsView.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 11/1/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct OppositeCollapseKeywordsView: View {
|
||||||
|
@ObservedObject private var preferences = Preferences.shared
|
||||||
|
// Can't use the raw [String] for keywords, because we need a fixed ID (within the lifetime of this view) for ForEach
|
||||||
|
@State private var keywords: [Keyword] = Preferences.shared.oppositeCollapseKeywords.map(Keyword.init) {
|
||||||
|
didSet {
|
||||||
|
preferences.oppositeCollapseKeywords = keywords.map(\.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@State private var valueToAdd = ""
|
||||||
|
@State private var makeAddFieldFirstResponder = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
// the background from the grouped ListStyle clips to the safe area, so when the keyboard is hiding/showing
|
||||||
|
// the color behind it can be seen, which looks odd
|
||||||
|
Color(UIColor.secondarySystemBackground)
|
||||||
|
.edgesIgnoringSafeArea(.bottom)
|
||||||
|
|
||||||
|
List {
|
||||||
|
Section(footer: Text("A post matches if its content warning contains the text of a keyword, ignoring case.")) {
|
||||||
|
ForEach(keywords) { (keyword) in
|
||||||
|
Row(keyword: keyword) {
|
||||||
|
keywords.removeAll(where: { $0.id == keyword.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDelete(perform: self.removeKeywords)
|
||||||
|
|
||||||
|
FocusableTextField(placeholder: "Add Keyword", text: $valueToAdd, becomeFirstResponder: $makeAddFieldFirstResponder, onCommit: self.addKeyword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animation(.default)
|
||||||
|
.listStyle(GroupedListStyle())
|
||||||
|
}
|
||||||
|
.onAppear(perform: updateAppearance)
|
||||||
|
.navigationBarTitle(preferences.expandAllContentWarnings ? "Collapse Post CW Keywords" : "Expand Post CW Keywords")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateAppearance() {
|
||||||
|
UIScrollView.appearance(whenContainedInInstancesOf: [PreferencesNavigationController.self]).keyboardDismissMode = .interactive
|
||||||
|
}
|
||||||
|
|
||||||
|
private func commitExisting(at index: Int) -> () -> Void {
|
||||||
|
return {
|
||||||
|
if keywords[index].value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
|
keywords.remove(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
makeAddFieldFirstResponder = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeKeywords(_ indices: IndexSet) {
|
||||||
|
keywords.remove(atOffsets: indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addKeyword() {
|
||||||
|
guard !valueToAdd.isEmpty else { return }
|
||||||
|
keywords.append(Keyword(valueToAdd))
|
||||||
|
valueToAdd = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension OppositeCollapseKeywordsView {
|
||||||
|
// Class for wrapping keywords that provides a fixed id SwiftUI's ForEach can use
|
||||||
|
class Keyword: ObservableObject, Identifiable {
|
||||||
|
let id = UUID()
|
||||||
|
@Published var value: String
|
||||||
|
|
||||||
|
init(_ value: String) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension OppositeCollapseKeywordsView {
|
||||||
|
// Use a separate View for the row so it can use @ObservableObject to get a binding for the keyword's value
|
||||||
|
struct Row: View {
|
||||||
|
@ObservedObject var keyword: Keyword
|
||||||
|
let removeKeyword: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
FocusableTextField(placeholder: "Keyword", text: $keyword.value) {
|
||||||
|
if keyword.value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
|
removeKeyword()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OppositeCollapseKeywordsView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
OppositeCollapseKeywordsView()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// FocusableTextField.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 11/2/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct FocusableTextField: UIViewRepresentable {
|
||||||
|
typealias UIViewType = UITextField
|
||||||
|
|
||||||
|
let placeholder: String
|
||||||
|
let text: Binding<String>
|
||||||
|
let becomeFirstResponder: Binding<Bool>?
|
||||||
|
let onCommit: (() -> Void)?
|
||||||
|
|
||||||
|
init(placeholder: String, text: Binding<String>, becomeFirstResponder: Binding<Bool>? = nil, onCommit: (() -> Void)? = nil) {
|
||||||
|
self.placeholder = placeholder
|
||||||
|
self.text = text
|
||||||
|
self.becomeFirstResponder = becomeFirstResponder
|
||||||
|
self.onCommit = onCommit
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> UITextField {
|
||||||
|
let field = UITextField()
|
||||||
|
field.delegate = context.coordinator
|
||||||
|
field.addTarget(context.coordinator, action: #selector(Coordinator.didChange(_:)), for: .editingChanged)
|
||||||
|
field.addTarget(context.coordinator, action: #selector(Coordinator.didEnd(_:)), for: .primaryActionTriggered)
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: UITextField, context: Context) {
|
||||||
|
uiView.placeholder = placeholder
|
||||||
|
uiView.text = text.wrappedValue
|
||||||
|
|
||||||
|
context.coordinator.text = text
|
||||||
|
context.coordinator.onCommit = onCommit
|
||||||
|
|
||||||
|
if becomeFirstResponder?.wrappedValue == true {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
uiView.becomeFirstResponder()
|
||||||
|
becomeFirstResponder?.wrappedValue = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
return Coordinator(text: text)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coordinator: NSObject, UITextFieldDelegate {
|
||||||
|
var text: Binding<String>
|
||||||
|
var onCommit: (() -> Void)?
|
||||||
|
|
||||||
|
init(text: Binding<String>) {
|
||||||
|
self.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func didChange(_ textField: UITextField) {
|
||||||
|
text.wrappedValue = textField.text ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func didEnd(_ textField: UITextField) {
|
||||||
|
onCommit?()
|
||||||
|
}
|
||||||
|
|
||||||
|
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||||
|
onCommit?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue