Hide statuses without alt text
This commit is contained in:
parent
51b05ae128
commit
6a8bc9ef64
|
@ -20,6 +20,11 @@ public extension Status {
|
|||
return getAllImageMediaAttachments().isEmpty == false
|
||||
}
|
||||
|
||||
func statusContainsAltText() -> Bool {
|
||||
let mediaAttachments = self.getAllImageMediaAttachments()
|
||||
return mediaAttachments.contains(where: { $0.description?.isEmpty == false })
|
||||
}
|
||||
|
||||
func getAllImageMediaAttachments() -> [MediaAttachment] {
|
||||
if let reblog = self.reblog {
|
||||
// If status is rebloged the we have to check if orginal status contains image.
|
||||
|
|
|
@ -35,6 +35,7 @@ extension ApplicationSettings {
|
|||
@NSManaged public var warnAboutMissingAlt: Bool
|
||||
@NSManaged public var showGridOnUserProfile: Bool
|
||||
@NSManaged public var showReboostedStatuses: Bool
|
||||
@NSManaged public var hideStatusesWithoutAlt: Bool
|
||||
|
||||
@NSManaged public var customNavigationMenuItem1: Int32
|
||||
@NSManaged public var customNavigationMenuItem2: Int32
|
||||
|
|
|
@ -60,6 +60,7 @@ class ApplicationSettingsHandler {
|
|||
applicationState.warnAboutMissingAlt = defaultSettings.warnAboutMissingAlt
|
||||
applicationState.showGridOnUserProfile = defaultSettings.showGridOnUserProfile
|
||||
applicationState.showReboostedStatuses = defaultSettings.showReboostedStatuses
|
||||
applicationState.hideStatusesWithoutAlt = defaultSettings.hideStatusesWithoutAlt
|
||||
|
||||
if let menuPosition = MenuPosition(rawValue: Int(defaultSettings.menuPosition)) {
|
||||
applicationState.menuPosition = menuPosition
|
||||
|
@ -204,6 +205,12 @@ class ApplicationSettingsHandler {
|
|||
CoreDataHandler.shared.save()
|
||||
}
|
||||
|
||||
func set(hideStatusesWithoutAlt: Bool) {
|
||||
let defaultSettings = self.get()
|
||||
defaultSettings.hideStatusesWithoutAlt = hideStatusesWithoutAlt
|
||||
CoreDataHandler.shared.save()
|
||||
}
|
||||
|
||||
private func createApplicationSettingsEntity(viewContext: NSManagedObjectContext? = nil) -> ApplicationSettings {
|
||||
let context = viewContext ?? CoreDataHandler.shared.container.viewContext
|
||||
return ApplicationSettings(context: context)
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Vernissage-018.xcdatamodel</string>
|
||||
<string>Vernissage-019.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22222" systemVersion="23A344" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AccountData" representedClassName="AccountData" syncable="YES">
|
||||
<attribute name="accessToken" optional="YES" attributeType="String"/>
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="avatarData" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="clientId" attributeType="String"/>
|
||||
<attribute name="clientSecret" attributeType="String"/>
|
||||
<attribute name="clientVapidKey" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="String"/>
|
||||
<attribute name="displayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="followersCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="followingCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="header" optional="YES" attributeType="URI"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="lastSeenStatusId" optional="YES" attributeType="String"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="refreshToken" optional="YES" attributeType="String"/>
|
||||
<attribute name="serverUrl" attributeType="URI"/>
|
||||
<attribute name="statusesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="accountRelationships" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="AccountRelationship" inverseName="pixelfedAccount" inverseEntity="AccountRelationship"/>
|
||||
<relationship name="statuses" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="StatusData" inverseName="pixelfedAccount" inverseEntity="StatusData"/>
|
||||
<relationship name="viewedStatuses" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="ViewedStatus" inverseName="pixelfedAccount" inverseEntity="ViewedStatus"/>
|
||||
</entity>
|
||||
<entity name="AccountRelationship" representedClassName="AccountRelationship" syncable="YES">
|
||||
<attribute name="accountId" attributeType="String"/>
|
||||
<attribute name="boostedStatusesMuted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="pixelfedAccount" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AccountData" inverseName="accountRelationships" inverseEntity="AccountData"/>
|
||||
</entity>
|
||||
<entity name="ApplicationSettings" representedClassName="ApplicationSettings" syncable="YES">
|
||||
<attribute name="activeIcon" attributeType="String" defaultValueString="Default"/>
|
||||
<attribute name="avatarShape" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
|
||||
<attribute name="currentAccount" optional="YES" attributeType="String"/>
|
||||
<attribute name="customNavigationMenuItem1" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
|
||||
<attribute name="customNavigationMenuItem2" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="YES"/>
|
||||
<attribute name="customNavigationMenuItem3" attributeType="Integer 32" defaultValueString="5" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticAnimationEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticButtonPressEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticNotificationEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticRefreshEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hapticTabSelectionEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="hideStatusesWithoutAlt" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastRefreshTokens" attributeType="Date" defaultDateTimeInterval="694256400" usesScalarValueType="NO"/>
|
||||
<attribute name="menuPosition" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
|
||||
<attribute name="showAltIconOnTimeline" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showAvatarsOnTimeline" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showFavouritesOnTimeline" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showGridOnUserProfile" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showPhotoDescription" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showReboostedStatuses" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="showSensitive" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="theme" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="tintColor" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="YES"/>
|
||||
<attribute name="warnAboutMissingAlt" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
</entity>
|
||||
<entity name="AttachmentData" representedClassName="AttachmentData" syncable="YES">
|
||||
<attribute name="blurhash" optional="YES" attributeType="String"/>
|
||||
<attribute name="data" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES"/>
|
||||
<attribute name="exifCamera" optional="YES" attributeType="String"/>
|
||||
<attribute name="exifCreatedDate" optional="YES" attributeType="String"/>
|
||||
<attribute name="exifExposure" optional="YES" attributeType="String"/>
|
||||
<attribute name="exifLens" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="metaImageHeight" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metaImageWidth" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="order" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="previewUrl" optional="YES" attributeType="URI"/>
|
||||
<attribute name="remoteUrl" optional="YES" attributeType="URI"/>
|
||||
<attribute name="statusId" attributeType="String"/>
|
||||
<attribute name="text" optional="YES" attributeType="String"/>
|
||||
<attribute name="type" attributeType="String"/>
|
||||
<attribute name="url" attributeType="URI"/>
|
||||
<relationship name="statusRelation" maxCount="1" deletionRule="Nullify" destinationEntity="StatusData" inverseName="attachmentsRelation" inverseEntity="StatusData"/>
|
||||
</entity>
|
||||
<entity name="StatusData" representedClassName="StatusData" syncable="YES">
|
||||
<attribute name="accountAvatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="accountDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="accountId" attributeType="String"/>
|
||||
<attribute name="accountUsername" optional="YES" attributeType="String"/>
|
||||
<attribute name="applicationName" optional="YES" attributeType="String"/>
|
||||
<attribute name="applicationWebsite" optional="YES" attributeType="URI"/>
|
||||
<attribute name="bookmarked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="content" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="String"/>
|
||||
<attribute name="favourited" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="favouritesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="inReplyToAccount" optional="YES" attributeType="String"/>
|
||||
<attribute name="inReplyToId" optional="YES" attributeType="String"/>
|
||||
<attribute name="muted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="pinned" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="reblogged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="rebloggedAccountAvatar" optional="YES" attributeType="URI"/>
|
||||
<attribute name="rebloggedAccountDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebloggedAccountId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebloggedAccountUsername" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebloggedStatusId" optional="YES" attributeType="String"/>
|
||||
<attribute name="reblogsCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="repliesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sensitive" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="spoilerText" optional="YES" attributeType="String"/>
|
||||
<attribute name="uri" attributeType="String"/>
|
||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||
<attribute name="visibility" attributeType="String"/>
|
||||
<relationship name="attachmentsRelation" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="AttachmentData" inverseName="statusRelation" inverseEntity="AttachmentData"/>
|
||||
<relationship name="pixelfedAccount" maxCount="1" deletionRule="Nullify" destinationEntity="AccountData" inverseName="statuses" inverseEntity="AccountData"/>
|
||||
</entity>
|
||||
<entity name="ViewedStatus" representedClassName="ViewedStatus" syncable="YES">
|
||||
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="reblogId" optional="YES" attributeType="String"/>
|
||||
<relationship name="pixelfedAccount" maxCount="1" deletionRule="Nullify" destinationEntity="AccountData" inverseName="viewedStatuses" inverseEntity="AccountData"/>
|
||||
</entity>
|
||||
</model>
|
|
@ -110,6 +110,9 @@ public class ApplicationState: ObservableObject {
|
|||
/// Show reboosted statuses on home timeline.
|
||||
@Published public var showReboostedStatuses = false
|
||||
|
||||
/// Hide statuses without ALT text.
|
||||
@Published public var hideStatusesWithoutAlt = false
|
||||
|
||||
public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) {
|
||||
self.account = accountModel
|
||||
self.lastSeenStatusId = lastSeenStatusId
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"userProfile.title.blocked" = "Blocked";
|
||||
"userProfile.title.enableBoosts" = "Enable boosts";
|
||||
"userProfile.title.disableBoosts" = "Disable boosts";
|
||||
"userProfile.title.boostedStatusesMuted" = "Boosts muted";
|
||||
|
||||
// Mark: Notifications view.
|
||||
"notifications.navigationBar.title" = "Notifications";
|
||||
|
@ -244,6 +245,8 @@
|
|||
"settings.title.warnAboutMissingAltDescription" = "A warning about missing ALT texts will be displayed before publishing new post.";
|
||||
"settings.title.enableReboostOnTimeline" = "Show boosted statuses";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Boosted statuses will be visible on your home timeline.";
|
||||
"settings.title.hideStatusesWithoutAlt" = "Hide statuses without ALT text";
|
||||
"settings.title.hideStatusesWithoutAltDescription" = "Statuses without ALT text will not be visible on your home timeline.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Sign in to Pixelfed";
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"userProfile.title.blocked" = "Blokeatuta";
|
||||
"userProfile.title.enableBoosts" = "Enable boosts";
|
||||
"userProfile.title.disableBoosts" = "Disable boosts";
|
||||
"userProfile.title.boostedStatusesMuted" = "Boosts muted";
|
||||
|
||||
// Mark: Notifications view.
|
||||
"notifications.navigationBar.title" = "Jakinarazpenak";
|
||||
|
@ -244,6 +245,8 @@
|
|||
"settings.title.warnAboutMissingAltDescription" = "Irudiren batek deskribapenik ez badu, argitaratu baino lehen abisua erakutsiko da.";
|
||||
"settings.title.enableReboostOnTimeline" = "Erakutsi bultzatutako egoerak";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Besteek bultzatu dituzten egoerak zure denbora-lerroan erakutsiko dira.";
|
||||
"settings.title.hideStatusesWithoutAlt" = "Hide statuses without ALT text";
|
||||
"settings.title.hideStatusesWithoutAltDescription" = "Statuses without ALT text will not be visible on your home timeline.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Hasi saioa Pixelfed-en";
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"userProfile.title.blocked" = "Bloquer";
|
||||
"userProfile.title.enableBoosts" = "Enable boosts";
|
||||
"userProfile.title.disableBoosts" = "Disable boosts";
|
||||
"userProfile.title.boostedStatusesMuted" = "Boosts muted";
|
||||
|
||||
// Mark: Notifications view.
|
||||
"notifications.navigationBar.title" = "Notifications";
|
||||
|
@ -244,6 +245,8 @@
|
|||
"settings.title.warnAboutMissingAltDescription" = "Un avertissement concernant les textes ALT manquants sera affiché avant la publication d'un nouveau message.";
|
||||
"settings.title.enableReboostOnTimeline" = "Show boosted statuses";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Boosted statuses will be visible on your home timeline.";
|
||||
"settings.title.hideStatusesWithoutAlt" = "Hide statuses without ALT text";
|
||||
"settings.title.hideStatusesWithoutAltDescription" = "Statuses without ALT text will not be visible on your home timeline.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Se connecter à Pixelfed";
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"userProfile.title.blocked" = "Zablokowany";
|
||||
"userProfile.title.enableBoosts" = "Wyświetl podbicia";
|
||||
"userProfile.title.disableBoosts" = "Ukryj podbicia";
|
||||
"userProfile.title.boostedStatusesMuted" = "Podbicia ukryte";
|
||||
|
||||
// Mark: Notifications view.
|
||||
"notifications.navigationBar.title" = "Powiadomienia";
|
||||
|
@ -244,6 +245,8 @@
|
|||
"settings.title.warnAboutMissingAltDescription" = "Ostrzeżenie o brakujących tekstach ALT będzie wyświetlane przed opublikowaniem nowego statusu.";
|
||||
"settings.title.enableReboostOnTimeline" = "Wyświetl podbite statusy";
|
||||
"settings.title.enableReboostOnTimelineDescription" = "Podbite statusy będą widoczne na twojej osi czasu.";
|
||||
"settings.title.hideStatusesWithoutAlt" = "Ukryj statusy bez tekstu ALT";
|
||||
"settings.title.hideStatusesWithoutAltDescription" = "Statusy bez tekstu ALT nie będą wyświetlane na twojej osi czasu.";
|
||||
|
||||
// Mark: Signin view.
|
||||
"signin.navigationBar.title" = "Zaloguj się do Pixelfed";
|
||||
|
|
|
@ -344,6 +344,7 @@
|
|||
F870EE5129F1645C00A2D43B /* MainNavigationOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainNavigationOptions.swift; sourceTree = "<group>"; };
|
||||
F871F21C29EF0D7000A351EF /* NavigationMenuItemDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationMenuItemDetails.swift; sourceTree = "<group>"; };
|
||||
F871F21F29EF0FEC00A351EF /* Vernissage-011.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-011.xcdatamodel"; sourceTree = "<group>"; };
|
||||
F87425F62AD5402700A119D7 /* Vernissage-019.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-019.xcdatamodel"; sourceTree = "<group>"; };
|
||||
F8742FC329990AFB00E9642B /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = "<group>"; };
|
||||
F8764186298ABB520057D362 /* ViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewState.swift; sourceTree = "<group>"; };
|
||||
F876418C298AE5020057D362 /* PaginableStatusesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginableStatusesView.swift; sourceTree = "<group>"; };
|
||||
|
@ -1373,7 +1374,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 304;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||
|
@ -1404,7 +1405,7 @@
|
|||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 304;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||
|
@ -1434,7 +1435,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 304;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageShare/Info.plist;
|
||||
|
@ -1463,7 +1464,7 @@
|
|||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 304;
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = VernissageShare/Info.plist;
|
||||
|
@ -1617,7 +1618,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 304;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
@ -1660,7 +1661,7 @@
|
|||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 304;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
@ -1857,6 +1858,7 @@
|
|||
F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
F87425F62AD5402700A119D7 /* Vernissage-019.xcdatamodel */,
|
||||
F8E7ADF82AD44AFD0038FFFD /* Vernissage-018.xcdatamodel */,
|
||||
F8D8E0D22ACC89CB00AA1374 /* Vernissage-017.xcdatamodel */,
|
||||
F8BD04192ACC2280004B8E2C /* Vernissage-016.xcdatamodel */,
|
||||
|
@ -1877,7 +1879,7 @@
|
|||
F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */,
|
||||
F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */,
|
||||
);
|
||||
currentVersion = F8E7ADF82AD44AFD0038FFFD /* Vernissage-018.xcdatamodel */;
|
||||
currentVersion = F87425F62AD5402700A119D7 /* Vernissage-019.xcdatamodel */;
|
||||
path = Vernissage.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
|
|
@ -24,7 +24,7 @@ public class HomeTimelineService {
|
|||
private let semaphore = AsyncSemaphore(value: 1)
|
||||
|
||||
@MainActor
|
||||
public func loadOnBottom(for account: AccountModel, includeReblogs: Bool) async throws -> Int {
|
||||
public func loadOnBottom(for account: AccountModel, includeReblogs: Bool, hideStatusesWithoutAlt: Bool) async throws -> Int {
|
||||
// Load data from API and operate on CoreData on background context.
|
||||
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
|
||||
|
||||
|
@ -36,7 +36,11 @@ public class HomeTimelineService {
|
|||
}
|
||||
|
||||
// Load data on bottom of the list.
|
||||
let allStatusesFromApi = try await self.load(for: account, includeReblogs: includeReblogs, on: backgroundContext, maxId: oldestStatus.id)
|
||||
let allStatusesFromApi = try await self.load(for: account,
|
||||
includeReblogs: includeReblogs,
|
||||
hideStatusesWithoutAlt: hideStatusesWithoutAlt,
|
||||
on: backgroundContext,
|
||||
maxId: oldestStatus.id)
|
||||
|
||||
// Save data into database.
|
||||
CoreDataHandler.shared.save(viewContext: backgroundContext)
|
||||
|
@ -49,7 +53,7 @@ public class HomeTimelineService {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
public func refreshTimeline(for account: AccountModel, includeReblogs: Bool, updateLastSeenStatus: Bool = false) async throws -> String? {
|
||||
public func refreshTimeline(for account: AccountModel, includeReblogs: Bool, hideStatusesWithoutAlt: Bool, updateLastSeenStatus: Bool = false) async throws -> String? {
|
||||
await semaphore.wait()
|
||||
defer { semaphore.signal() }
|
||||
|
||||
|
@ -62,7 +66,10 @@ public class HomeTimelineService {
|
|||
|
||||
// Refresh/load home timeline (refreshing on top downloads always first 40 items).
|
||||
// When Apple introduce good way to show new items without scroll to top then we can change that method.
|
||||
let allStatusesFromApi = try await self.refresh(for: account, includeReblogs: includeReblogs, on: backgroundContext)
|
||||
let allStatusesFromApi = try await self.refresh(for: account,
|
||||
includeReblogs: includeReblogs,
|
||||
hideStatusesWithoutAlt: hideStatusesWithoutAlt,
|
||||
on: backgroundContext)
|
||||
|
||||
// Update last seen status.
|
||||
if let lastSeenStatusId, updateLastSeenStatus == true {
|
||||
|
@ -95,7 +102,7 @@ public class HomeTimelineService {
|
|||
CoreDataHandler.shared.save()
|
||||
}
|
||||
|
||||
public func amountOfNewStatuses(for account: AccountModel, includeReblogs: Bool) async -> Int {
|
||||
public func amountOfNewStatuses(for account: AccountModel, includeReblogs: Bool, hideStatusesWithoutAlt: Bool) async -> Int {
|
||||
await semaphore.wait()
|
||||
defer { semaphore.signal() }
|
||||
|
||||
|
@ -131,6 +138,11 @@ public class HomeTimelineService {
|
|||
let statusesWithImagesOnly = downloadedStatuses.getStatusesWithImagesOnly()
|
||||
|
||||
for status in statusesWithImagesOnly {
|
||||
// We have to hide statuses without ALT text.
|
||||
if hideStatusesWithoutAlt && status.statusContainsAltText() == false {
|
||||
continue
|
||||
}
|
||||
|
||||
// We shouldn't add statuses that are boosted by muted accounts.
|
||||
if AccountRelationshipHandler.shared.isBoostedStatusesMuted(accountId: account.id, status: status, viewContext: backgroundContext) {
|
||||
continue
|
||||
|
@ -190,9 +202,12 @@ public class HomeTimelineService {
|
|||
return statusData
|
||||
}
|
||||
|
||||
private func refresh(for account: AccountModel, includeReblogs: Bool, on backgroundContext: NSManagedObjectContext) async throws -> [Status] {
|
||||
private func refresh(for account: AccountModel, includeReblogs: Bool, hideStatusesWithoutAlt: Bool, on backgroundContext: NSManagedObjectContext) async throws -> [Status] {
|
||||
// Retrieve statuses from API.
|
||||
let statuses = try await self.getUniqueStatusesForHomeTimeline(account: account, includeReblogs: includeReblogs, on: backgroundContext)
|
||||
let statuses = try await self.getUniqueStatusesForHomeTimeline(account: account,
|
||||
includeReblogs: includeReblogs,
|
||||
hideStatusesWithoutAlt: hideStatusesWithoutAlt,
|
||||
on: backgroundContext)
|
||||
|
||||
// Update all existing statuses in database.
|
||||
for status in statuses {
|
||||
|
@ -246,11 +261,16 @@ public class HomeTimelineService {
|
|||
|
||||
private func load(for account: AccountModel,
|
||||
includeReblogs: Bool,
|
||||
hideStatusesWithoutAlt: Bool,
|
||||
on backgroundContext: NSManagedObjectContext,
|
||||
maxId: String? = nil
|
||||
) async throws -> [Status] {
|
||||
// Retrieve statuses from API.
|
||||
let statuses = try await self.getUniqueStatusesForHomeTimeline(account: account, maxId: maxId, includeReblogs: includeReblogs, on: backgroundContext)
|
||||
let statuses = try await self.getUniqueStatusesForHomeTimeline(account: account,
|
||||
maxId: maxId,
|
||||
includeReblogs: includeReblogs,
|
||||
hideStatusesWithoutAlt: hideStatusesWithoutAlt,
|
||||
on: backgroundContext)
|
||||
|
||||
// Save statuses in database.
|
||||
try await self.add(statuses, for: account, on: backgroundContext)
|
||||
|
@ -348,7 +368,11 @@ public class HomeTimelineService {
|
|||
return ViewedStatusHandler.shared.hasBeenAlreadyOnTimeline(accountId: accountId, status: status, viewContext: backgroundContext)
|
||||
}
|
||||
|
||||
private func getUniqueStatusesForHomeTimeline(account: AccountModel, maxId: EntityId? = nil, includeReblogs: Bool? = nil, on backgroundContext: NSManagedObjectContext) async throws -> [Status] {
|
||||
private func getUniqueStatusesForHomeTimeline(account: AccountModel,
|
||||
maxId: EntityId? = nil,
|
||||
includeReblogs: Bool? = nil,
|
||||
hideStatusesWithoutAlt: Bool = false,
|
||||
on backgroundContext: NSManagedObjectContext) async throws -> [Status] {
|
||||
guard let accessToken = account.accessToken else {
|
||||
return []
|
||||
}
|
||||
|
@ -371,6 +395,11 @@ public class HomeTimelineService {
|
|||
let statusesWithImagesOnly = downloadedStatuses.getStatusesWithImagesOnly()
|
||||
|
||||
for status in statusesWithImagesOnly {
|
||||
// We have to hide statuses without ALT text.
|
||||
if hideStatusesWithoutAlt && status.statusContainsAltText() == false {
|
||||
continue
|
||||
}
|
||||
|
||||
// We shouldn't add statuses that are boosted by muted accounts.
|
||||
if AccountRelationshipHandler.shared.isBoostedStatusesMuted(accountId: account.id, status: status, viewContext: backgroundContext) {
|
||||
continue
|
||||
|
|
|
@ -205,7 +205,8 @@ struct VernissageApp: App {
|
|||
private func calculateNewPhotosInBackground() async {
|
||||
if let account = self.applicationState.account {
|
||||
self.applicationState.amountOfNewStatuses = await HomeTimelineService.shared.amountOfNewStatuses(for: account,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses)
|
||||
includeReblogs: self.applicationState.showReboostedStatuses,
|
||||
hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,12 @@ struct HomeFeedView: View {
|
|||
.task {
|
||||
do {
|
||||
if let account = self.applicationState.account {
|
||||
let newStatusesCount = try await HomeTimelineService.shared.loadOnBottom(for: account, includeReblogs: self.applicationState.showReboostedStatuses)
|
||||
let newStatusesCount = try await HomeTimelineService.shared.loadOnBottom(
|
||||
for: account,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses,
|
||||
hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt
|
||||
)
|
||||
|
||||
if newStatusesCount == 0 {
|
||||
allItemsLoaded = true
|
||||
}
|
||||
|
@ -103,7 +108,10 @@ struct HomeFeedView: View {
|
|||
private func refreshData() async {
|
||||
do {
|
||||
if let account = self.applicationState.account {
|
||||
let lastSeenStatusId = try await HomeTimelineService.shared.refreshTimeline(for: account, includeReblogs: self.applicationState.showReboostedStatuses, updateLastSeenStatus: true)
|
||||
let lastSeenStatusId = try await HomeTimelineService.shared.refreshTimeline(for: account,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses,
|
||||
hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt,
|
||||
updateLastSeenStatus: true)
|
||||
asyncAfter(0.75) {
|
||||
self.applicationState.lastSeenStatusId = lastSeenStatusId
|
||||
self.applicationState.amountOfNewStatuses = 0
|
||||
|
@ -126,7 +134,9 @@ struct HomeFeedView: View {
|
|||
}
|
||||
|
||||
if let account = self.applicationState.account {
|
||||
_ = try await HomeTimelineService.shared.refreshTimeline(for: account, includeReblogs: self.applicationState.showReboostedStatuses)
|
||||
_ = try await HomeTimelineService.shared.refreshTimeline(for: account,
|
||||
includeReblogs: self.applicationState.showReboostedStatuses,
|
||||
hideStatusesWithoutAlt: self.applicationState.hideStatusesWithoutAlt)
|
||||
}
|
||||
|
||||
self.applicationState.amountOfNewStatuses = 0
|
||||
|
|
|
@ -97,6 +97,18 @@ struct MediaSettingsView: View {
|
|||
.onChange(of: self.applicationState.showReboostedStatuses) { newValue in
|
||||
ApplicationSettingsHandler.shared.set(showReboostedStatuses: newValue)
|
||||
}
|
||||
|
||||
Toggle(isOn: $applicationState.hideStatusesWithoutAlt) {
|
||||
VStack(alignment: .leading) {
|
||||
Text("settings.title.hideStatusesWithoutAlt", comment: "Hide statuses without ALT")
|
||||
Text("settings.title.hideStatusesWithoutAltDescription", comment: "Statuses without ALT text will not be visible on your home timeline.")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.customGrayColor)
|
||||
}
|
||||
}
|
||||
.onChange(of: self.applicationState.hideStatusesWithoutAlt) { newValue in
|
||||
ApplicationSettingsHandler.shared.set(hideStatusesWithoutAlt: newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,6 +185,11 @@ struct StatusesView: View {
|
|||
// Get only statuses with images.
|
||||
var inPlaceStatuses: [StatusModel] = []
|
||||
for item in statuses.getStatusesWithImagesOnly() {
|
||||
// We have to hide statuses without ALT text.
|
||||
if self.shouldHideStatusWithoutAlt(status: item) {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have to skip statuses that are boosted from muted accounts.
|
||||
if let accountId = self.applicationState.account?.id, AccountRelationshipHandler.shared.isBoostedStatusesMuted(accountId: accountId, status: item) {
|
||||
continue
|
||||
|
@ -217,6 +222,11 @@ struct StatusesView: View {
|
|||
// Get only statuses with images.
|
||||
var inPlaceStatuses: [StatusModel] = []
|
||||
for item in previousStatuses.getStatusesWithImagesOnly() {
|
||||
// We have to hide statuses without ALT text.
|
||||
if self.shouldHideStatusWithoutAlt(status: item) {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have to skip statuses that are boosted from muted accounts.
|
||||
if let accountId = self.applicationState.account?.id, AccountRelationshipHandler.shared.isBoostedStatusesMuted(accountId: accountId, status: item) {
|
||||
continue
|
||||
|
@ -247,6 +257,11 @@ struct StatusesView: View {
|
|||
// Get only statuses with images.
|
||||
var inPlaceStatuses: [StatusModel] = []
|
||||
for item in statuses.getStatusesWithImagesOnly() {
|
||||
// We have to hide statuses without ALT text.
|
||||
if self.shouldHideStatusWithoutAlt(status: item) {
|
||||
continue
|
||||
}
|
||||
|
||||
// We have to skip statuses that are boosted from muted accounts.
|
||||
if let accountId = self.applicationState.account?.id, AccountRelationshipHandler.shared.isBoostedStatusesMuted(accountId: accountId, status: item) {
|
||||
continue
|
||||
|
@ -366,4 +381,16 @@ struct StatusesView: View {
|
|||
private func prefetch(statusModels: [StatusModel]) {
|
||||
imagePrefetcher.startPrefetching(with: statusModels.getAllImagesUrls())
|
||||
}
|
||||
|
||||
private func shouldHideStatusWithoutAlt(status: Status) -> Bool {
|
||||
if self.applicationState.hideStatusesWithoutAlt == false {
|
||||
return false
|
||||
}
|
||||
|
||||
if self.listType != .home && self.listType != .local && self.listType != .federated {
|
||||
return false
|
||||
}
|
||||
|
||||
return status.statusContainsAltText() == false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ struct UserProfileHeaderView: View {
|
|||
|
||||
@State var account: Account
|
||||
@ObservedObject var relationship = RelationshipModel()
|
||||
@Binding var boostsDisabled: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
|
@ -109,7 +110,7 @@ struct UserProfileHeaderView: View {
|
|||
|
||||
@ViewBuilder
|
||||
private func accountRelationshipPanel() -> some View {
|
||||
if self.relationship.followedBy || self.relationship.muting || self.relationship.blocking {
|
||||
if self.relationship.followedBy || self.relationship.muting || self.relationship.blocking || self.boostsDisabled {
|
||||
HStack(alignment: .top) {
|
||||
if self.relationship.followedBy {
|
||||
TagWidget(value: "userProfile.title.followsYou", color: .secondary, systemImage: "person.crop.circle.badge.checkmark")
|
||||
|
@ -119,12 +120,17 @@ struct UserProfileHeaderView: View {
|
|||
TagWidget(value: "userProfile.title.muted", color: .accentColor, systemImage: "message.and.waveform.fill")
|
||||
}
|
||||
|
||||
if self.boostsDisabled {
|
||||
TagWidget(value: "userProfile.title.boostedStatusesMuted", color: .accentColor, image: "custom.rocket.fill")
|
||||
}
|
||||
|
||||
if self.relationship.blocking {
|
||||
TagWidget(value: "userProfile.title.blocked", color: .dangerColor, systemImage: "hand.raised.fill")
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ struct UserProfileView: View {
|
|||
|
||||
private func accountView(account: Account) -> some View {
|
||||
ScrollView {
|
||||
UserProfileHeaderView(account: account, relationship: relationship)
|
||||
UserProfileHeaderView(account: account, relationship: relationship, boostsDisabled: $boostsDisabled)
|
||||
.id(self.viewId)
|
||||
|
||||
if self.applicationState.account?.id == account.id || self.relationship.haveAccessToPhotos(account: account) {
|
||||
|
|
|
@ -10,11 +10,13 @@ public struct TagWidget: View {
|
|||
private let value: LocalizedStringKey
|
||||
private let color: Color
|
||||
private let systemImage: String?
|
||||
private let image: String?
|
||||
|
||||
public init(value: LocalizedStringKey, color: Color, systemImage: String? = nil) {
|
||||
public init(value: LocalizedStringKey, color: Color, systemImage: String? = nil, image: String? = nil) {
|
||||
self.value = value
|
||||
self.color = color
|
||||
self.systemImage = systemImage
|
||||
self.image = image
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
|
@ -25,6 +27,12 @@ public struct TagWidget: View {
|
|||
.font(.footnote)
|
||||
}
|
||||
|
||||
if let image {
|
||||
Image(image)
|
||||
.foregroundColor(.white)
|
||||
.font(.footnote)
|
||||
}
|
||||
|
||||
Text(self.value, comment: "value")
|
||||
.foregroundColor(.white)
|
||||
.font(.footnote)
|
||||
|
|
Loading…
Reference in New Issue