commit
3f48cc0981
|
@ -85,7 +85,7 @@
|
||||||
<attribute name="typeRaw" attributeType="String"/>
|
<attribute name="typeRaw" attributeType="String"/>
|
||||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="userID" attributeType="String"/>
|
<attribute name="userID" attributeType="String"/>
|
||||||
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser"/>
|
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="notifications" inverseEntity="MastodonUser"/>
|
||||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="inNotifications" inverseEntity="Status"/>
|
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="inNotifications" inverseEntity="Status"/>
|
||||||
<uniquenessConstraints>
|
<uniquenessConstraints>
|
||||||
<uniquenessConstraint>
|
<uniquenessConstraint>
|
||||||
|
@ -132,6 +132,7 @@
|
||||||
<relationship name="muted" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="mutedBy" inverseEntity="Status"/>
|
<relationship name="muted" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="mutedBy" inverseEntity="Status"/>
|
||||||
<relationship name="muting" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="mutingBy" inverseEntity="MastodonUser"/>
|
<relationship name="muting" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="mutingBy" inverseEntity="MastodonUser"/>
|
||||||
<relationship name="mutingBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muting" inverseEntity="MastodonUser"/>
|
<relationship name="mutingBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muting" inverseEntity="MastodonUser"/>
|
||||||
|
<relationship name="notifications" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonNotification" inverseName="account" inverseEntity="MastodonNotification"/>
|
||||||
<relationship name="pinnedStatus" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="pinnedBy" inverseEntity="Status"/>
|
<relationship name="pinnedStatus" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="pinnedBy" inverseEntity="Status"/>
|
||||||
<relationship name="privateNotes" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="to" inverseEntity="PrivateNote"/>
|
<relationship name="privateNotes" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="to" inverseEntity="PrivateNote"/>
|
||||||
<relationship name="privateNotesTo" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="from" inverseEntity="PrivateNote"/>
|
<relationship name="privateNotesTo" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="from" inverseEntity="PrivateNote"/>
|
||||||
|
@ -181,8 +182,10 @@
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="SearchHistory" representedClassName=".SearchHistory" syncable="YES">
|
<entity name="SearchHistory" representedClassName=".SearchHistory" syncable="YES">
|
||||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="userID" attributeType="String" defaultValueString=""/>
|
||||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="searchHistory" inverseEntity="MastodonUser"/>
|
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="searchHistory" inverseEntity="MastodonUser"/>
|
||||||
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag" inverseName="searchHistory" inverseEntity="Tag"/>
|
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag" inverseName="searchHistory" inverseEntity="Tag"/>
|
||||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="searchHistory" inverseEntity="Status"/>
|
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="searchHistory" inverseEntity="Status"/>
|
||||||
|
@ -281,12 +284,12 @@
|
||||||
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
<element name="HomeTimelineIndex" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="MastodonAuthentication" positionX="0" positionY="0" width="128" height="209"/>
|
<element name="MastodonAuthentication" positionX="0" positionY="0" width="128" height="209"/>
|
||||||
<element name="MastodonNotification" positionX="9" positionY="162" width="128" height="164"/>
|
<element name="MastodonNotification" positionX="9" positionY="162" width="128" height="164"/>
|
||||||
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="719"/>
|
<element name="MastodonUser" positionX="0" positionY="0" width="128" height="734"/>
|
||||||
<element name="Mention" positionX="0" positionY="0" width="128" height="149"/>
|
<element name="Mention" positionX="0" positionY="0" width="128" height="149"/>
|
||||||
<element name="Poll" positionX="0" positionY="0" width="128" height="194"/>
|
<element name="Poll" positionX="0" positionY="0" width="128" height="194"/>
|
||||||
<element name="PollOption" positionX="0" positionY="0" width="128" height="134"/>
|
<element name="PollOption" positionX="0" positionY="0" width="128" height="134"/>
|
||||||
<element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/>
|
<element name="PrivateNote" positionX="0" positionY="0" width="128" height="89"/>
|
||||||
<element name="SearchHistory" positionX="0" positionY="0" width="128" height="119"/>
|
<element name="SearchHistory" positionX="0" positionY="0" width="128" height="149"/>
|
||||||
<element name="Setting" positionX="72" positionY="162" width="128" height="164"/>
|
<element name="Setting" positionX="72" positionY="162" width="128" height="164"/>
|
||||||
<element name="Status" positionX="0" positionY="0" width="128" height="614"/>
|
<element name="Status" positionX="0" positionY="0" width="128" height="614"/>
|
||||||
<element name="Subscription" positionX="81" positionY="171" width="128" height="179"/>
|
<element name="Subscription" positionX="81" positionY="171" width="128" height="179"/>
|
||||||
|
|
|
@ -47,6 +47,7 @@ final public class MastodonUser: NSManagedObject {
|
||||||
|
|
||||||
// one-to-many relationship
|
// one-to-many relationship
|
||||||
@NSManaged public private(set) var statuses: Set<Status>?
|
@NSManaged public private(set) var statuses: Set<Status>?
|
||||||
|
@NSManaged public private(set) var notifications: Set<MastodonNotification>?
|
||||||
|
|
||||||
// many-to-many relationship
|
// many-to-many relationship
|
||||||
@NSManaged public private(set) var favourite: Set<Status>?
|
@NSManaged public private(set) var favourite: Set<Status>?
|
||||||
|
|
|
@ -11,6 +11,8 @@ import CoreData
|
||||||
public final class SearchHistory: NSManagedObject {
|
public final class SearchHistory: NSManagedObject {
|
||||||
public typealias ID = UUID
|
public typealias ID = UUID
|
||||||
@NSManaged public private(set) var identifier: ID
|
@NSManaged public private(set) var identifier: ID
|
||||||
|
@NSManaged public private(set) var domain: String
|
||||||
|
@NSManaged public private(set) var userID: MastodonUser.ID
|
||||||
@NSManaged public private(set) var createAt: Date
|
@NSManaged public private(set) var createAt: Date
|
||||||
@NSManaged public private(set) var updatedAt: Date
|
@NSManaged public private(set) var updatedAt: Date
|
||||||
|
|
||||||
|
@ -37,9 +39,12 @@ extension SearchHistory {
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func insert(
|
public static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property,
|
||||||
account: MastodonUser
|
account: MastodonUser
|
||||||
) -> SearchHistory {
|
) -> SearchHistory {
|
||||||
let searchHistory: SearchHistory = context.insertObject()
|
let searchHistory: SearchHistory = context.insertObject()
|
||||||
|
searchHistory.domain = property.domain
|
||||||
|
searchHistory.userID = property.userID
|
||||||
searchHistory.account = account
|
searchHistory.account = account
|
||||||
return searchHistory
|
return searchHistory
|
||||||
}
|
}
|
||||||
|
@ -47,9 +52,12 @@ extension SearchHistory {
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func insert(
|
public static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property,
|
||||||
hashtag: Tag
|
hashtag: Tag
|
||||||
) -> SearchHistory {
|
) -> SearchHistory {
|
||||||
let searchHistory: SearchHistory = context.insertObject()
|
let searchHistory: SearchHistory = context.insertObject()
|
||||||
|
searchHistory.domain = property.domain
|
||||||
|
searchHistory.userID = property.userID
|
||||||
searchHistory.hashtag = hashtag
|
searchHistory.hashtag = hashtag
|
||||||
return searchHistory
|
return searchHistory
|
||||||
}
|
}
|
||||||
|
@ -57,22 +65,54 @@ extension SearchHistory {
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func insert(
|
public static func insert(
|
||||||
into context: NSManagedObjectContext,
|
into context: NSManagedObjectContext,
|
||||||
|
property: Property,
|
||||||
status: Status
|
status: Status
|
||||||
) -> SearchHistory {
|
) -> SearchHistory {
|
||||||
let searchHistory: SearchHistory = context.insertObject()
|
let searchHistory: SearchHistory = context.insertObject()
|
||||||
|
searchHistory.domain = property.domain
|
||||||
|
searchHistory.userID = property.userID
|
||||||
searchHistory.status = status
|
searchHistory.status = status
|
||||||
return searchHistory
|
return searchHistory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SearchHistory {
|
extension SearchHistory {
|
||||||
func update(updatedAt: Date) {
|
public func update(updatedAt: Date) {
|
||||||
setValue(updatedAt, forKey: #keyPath(SearchHistory.updatedAt))
|
setValue(updatedAt, forKey: #keyPath(SearchHistory.updatedAt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SearchHistory {
|
||||||
|
public struct Property {
|
||||||
|
public let domain: String
|
||||||
|
public let userID: MastodonUser.ID
|
||||||
|
|
||||||
|
public init(domain: String, userID: MastodonUser.ID) {
|
||||||
|
self.domain = domain
|
||||||
|
self.userID = userID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension SearchHistory: Managed {
|
extension SearchHistory: Managed {
|
||||||
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
public static var defaultSortDescriptors: [NSSortDescriptor] {
|
||||||
return [NSSortDescriptor(keyPath: \SearchHistory.updatedAt, ascending: false)]
|
return [NSSortDescriptor(keyPath: \SearchHistory.updatedAt, ascending: false)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SearchHistory {
|
||||||
|
static func predicate(domain: String) -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K == %@", #keyPath(SearchHistory.domain), domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func predicate(userID: String) -> NSPredicate {
|
||||||
|
return NSPredicate(format: "%K == %@", #keyPath(SearchHistory.userID), userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func predicate(domain: String, userID: String) -> NSPredicate {
|
||||||
|
return NSCompoundPredicate(andPredicateWithSubpredicates: [
|
||||||
|
predicate(domain: domain),
|
||||||
|
predicate(userID: userID)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4308,7 +4308,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||||
|
@ -4316,7 +4316,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -4335,7 +4335,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||||
|
@ -4343,7 +4343,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -4600,7 +4600,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4608,13 +4608,13 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
@ -4624,7 +4624,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4632,13 +4632,13 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
};
|
};
|
||||||
name = "ASDK - Debug";
|
name = "ASDK - Debug";
|
||||||
};
|
};
|
||||||
|
@ -4648,7 +4648,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4656,13 +4656,13 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
};
|
};
|
||||||
name = "ASDK - Release";
|
name = "ASDK - Release";
|
||||||
};
|
};
|
||||||
|
@ -4672,7 +4672,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4680,13 +4680,13 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.ShareActionExtension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
@ -4761,7 +4761,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||||
|
@ -4769,7 +4769,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -4876,7 +4876,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4884,7 +4884,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -4995,7 +4995,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||||
|
@ -5003,7 +5003,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -5110,7 +5110,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5118,7 +5118,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -5164,7 +5164,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5172,7 +5172,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -5187,7 +5187,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 41;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5195,7 +5195,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.9.1;
|
MARKETING_VERSION = 0.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>21</integer>
|
<integer>22</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -37,12 +37,12 @@
|
||||||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>22</integer>
|
<integer>23</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>23</integer>
|
<integer>21</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|
|
@ -17,6 +17,8 @@ final class SearchHistoryFetchedResultController: NSObject {
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
let fetchedResultsController: NSFetchedResultsController<SearchHistory>
|
let fetchedResultsController: NSFetchedResultsController<SearchHistory>
|
||||||
|
let domain = CurrentValueSubject<String?, Never>(nil)
|
||||||
|
let userID = CurrentValueSubject<Mastodon.Entity.Status.ID?, Never>(nil)
|
||||||
|
|
||||||
// output
|
// output
|
||||||
let objectIDs = CurrentValueSubject<[NSManagedObjectID], Never>([])
|
let objectIDs = CurrentValueSubject<[NSManagedObjectID], Never>([])
|
||||||
|
@ -38,6 +40,23 @@ final class SearchHistoryFetchedResultController: NSObject {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
fetchedResultsController.delegate = self
|
fetchedResultsController.delegate = self
|
||||||
|
|
||||||
|
Publishers.CombineLatest(
|
||||||
|
self.domain.removeDuplicates(),
|
||||||
|
self.userID.removeDuplicates()
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] domain, userID in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let predicates = [SearchHistory.predicate(domain: domain ?? "", userID: userID ?? "")]
|
||||||
|
self.fetchedResultsController.fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
|
||||||
|
do {
|
||||||
|
try self.fetchedResultsController.performFetch()
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,10 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
enum SettingsItem: Hashable {
|
enum SettingsItem {
|
||||||
case appearance(settingObjectID: NSManagedObjectID)
|
case appearance(settingObjectID: NSManagedObjectID)
|
||||||
case notification(settingObjectID: NSManagedObjectID, switchMode: NotificationSwitchMode)
|
case notification(settingObjectID: NSManagedObjectID, switchMode: NotificationSwitchMode)
|
||||||
case preferenceDarkMode(settingObjectID: NSManagedObjectID)
|
case preference(settingObjectID: NSManagedObjectID, preferenceType: PreferenceType)
|
||||||
case preferenceDisableAvatarAnimation(settingObjectID: NSManagedObjectID)
|
|
||||||
case preferenceUsingDefaultBrowser(settingObjectID: NSManagedObjectID)
|
|
||||||
case boringZone(item: Link)
|
case boringZone(item: Link)
|
||||||
case spicyZone(item: Link)
|
case spicyZone(item: Link)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +24,7 @@ extension SettingsItem {
|
||||||
case dark
|
case dark
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NotificationSwitchMode: CaseIterable {
|
enum NotificationSwitchMode: CaseIterable, Hashable {
|
||||||
case favorite
|
case favorite
|
||||||
case follow
|
case follow
|
||||||
case reblog
|
case reblog
|
||||||
|
@ -41,8 +39,22 @@ extension SettingsItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PreferenceType: CaseIterable {
|
||||||
|
case darkMode
|
||||||
|
case disableAvatarAnimation
|
||||||
|
case useDefaultBrowser
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .darkMode: return L10n.Scene.Settings.Section.AppearanceSettings.trueBlackDarkMode
|
||||||
|
case .disableAvatarAnimation: return L10n.Scene.Settings.Section.AppearanceSettings.disableAvatarAnimation
|
||||||
|
case .useDefaultBrowser: return L10n.Scene.Settings.Section.Preference.usingDefaultBrowser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Link: CaseIterable {
|
enum Link: CaseIterable, Hashable {
|
||||||
case accountSettings
|
case accountSettings
|
||||||
case termsOfService
|
case termsOfService
|
||||||
case privacyPolicy
|
case privacyPolicy
|
||||||
|
@ -71,3 +83,27 @@ extension SettingsItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SettingsItem: Hashable {
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
switch self {
|
||||||
|
case .appearance(let settingObjectID):
|
||||||
|
hasher.combine(String(describing: SettingsItem.AppearanceMode.self))
|
||||||
|
hasher.combine(settingObjectID)
|
||||||
|
case .notification(let settingObjectID, let switchMode):
|
||||||
|
hasher.combine(String(describing: SettingsItem.notification.self))
|
||||||
|
hasher.combine(settingObjectID)
|
||||||
|
hasher.combine(switchMode)
|
||||||
|
case .preference(let settingObjectID, let preferenceType):
|
||||||
|
hasher.combine(String(describing: SettingsItem.preference.self))
|
||||||
|
hasher.combine(settingObjectID)
|
||||||
|
hasher.combine(preferenceType)
|
||||||
|
case .boringZone(let link):
|
||||||
|
hasher.combine(String(describing: SettingsItem.boringZone.self))
|
||||||
|
hasher.combine(link)
|
||||||
|
case .spicyZone(let link):
|
||||||
|
hasher.combine(String(describing: SettingsItem.spicyZone.self))
|
||||||
|
hasher.combine(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
// Created by MainasuK Cirno on 2021-4-25.
|
// Created by MainasuK Cirno on 2021-4-25.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import CoreDataStack
|
||||||
|
|
||||||
enum SettingsSection: Hashable {
|
enum SettingsSection: Hashable {
|
||||||
case appearance
|
case appearance
|
||||||
|
@ -24,3 +26,125 @@ enum SettingsSection: Hashable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SettingsSection {
|
||||||
|
static func tableViewDiffableDataSource(
|
||||||
|
for tableView: UITableView,
|
||||||
|
managedObjectContext: NSManagedObjectContext,
|
||||||
|
settingsAppearanceTableViewCellDelegate: SettingsAppearanceTableViewCellDelegate,
|
||||||
|
settingsToggleCellDelegate: SettingsToggleCellDelegate
|
||||||
|
) -> UITableViewDiffableDataSource<SettingsSection, SettingsItem> {
|
||||||
|
UITableViewDiffableDataSource(tableView: tableView) { [
|
||||||
|
weak settingsAppearanceTableViewCellDelegate,
|
||||||
|
weak settingsToggleCellDelegate
|
||||||
|
] tableView, indexPath, item -> UITableViewCell? in
|
||||||
|
switch item {
|
||||||
|
case .appearance(let objectID):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell
|
||||||
|
managedObjectContext.performAndWait {
|
||||||
|
let setting = managedObjectContext.object(with: objectID) as! Setting
|
||||||
|
cell.update(with: setting.appearance)
|
||||||
|
ManagedObjectObserver.observe(object: setting)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink(receiveCompletion: { _ in
|
||||||
|
// do nothing
|
||||||
|
}, receiveValue: { [weak cell] change in
|
||||||
|
guard let cell = cell else { return }
|
||||||
|
guard case .update(let object) = change.changeType,
|
||||||
|
let setting = object as? Setting else { return }
|
||||||
|
cell.update(with: setting.appearance)
|
||||||
|
})
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
}
|
||||||
|
cell.delegate = settingsAppearanceTableViewCellDelegate
|
||||||
|
return cell
|
||||||
|
case .notification(let objectID, let switchMode):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell
|
||||||
|
managedObjectContext.performAndWait {
|
||||||
|
let setting = managedObjectContext.object(with: objectID) as! Setting
|
||||||
|
if let subscription = setting.activeSubscription {
|
||||||
|
SettingsSection.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription)
|
||||||
|
}
|
||||||
|
ManagedObjectObserver.observe(object: setting)
|
||||||
|
.sink(receiveCompletion: { _ in
|
||||||
|
// do nothing
|
||||||
|
}, receiveValue: { [weak cell] change in
|
||||||
|
guard let cell = cell else { return }
|
||||||
|
guard case .update(let object) = change.changeType,
|
||||||
|
let setting = object as? Setting else { return }
|
||||||
|
guard let subscription = setting.activeSubscription else { return }
|
||||||
|
SettingsSection.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription)
|
||||||
|
})
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
}
|
||||||
|
cell.delegate = settingsToggleCellDelegate
|
||||||
|
return cell
|
||||||
|
case .preference(let objectID, _):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell
|
||||||
|
cell.delegate = settingsToggleCellDelegate
|
||||||
|
managedObjectContext.performAndWait {
|
||||||
|
let setting = managedObjectContext.object(with: objectID) as! Setting
|
||||||
|
SettingsSection.configureSettingToggle(cell: cell, item: item, setting: setting)
|
||||||
|
|
||||||
|
ManagedObjectObserver.observe(object: setting)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink(receiveCompletion: { _ in
|
||||||
|
// do nothing
|
||||||
|
}, receiveValue: { [weak cell] change in
|
||||||
|
guard let cell = cell else { return }
|
||||||
|
guard case .update(let object) = change.changeType,
|
||||||
|
let setting = object as? Setting else { return }
|
||||||
|
SettingsSection.configureSettingToggle(cell: cell, item: item, setting: setting)
|
||||||
|
})
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
case .boringZone(let item),
|
||||||
|
.spicyZone(let item):
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsLinkTableViewCell.self), for: indexPath) as! SettingsLinkTableViewCell
|
||||||
|
cell.update(with: item)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SettingsSection {
|
||||||
|
|
||||||
|
static func configureSettingToggle(
|
||||||
|
cell: SettingsToggleTableViewCell,
|
||||||
|
item: SettingsItem,
|
||||||
|
setting: Setting
|
||||||
|
) {
|
||||||
|
guard case let .preference(_, preferenceType) = item else { return }
|
||||||
|
|
||||||
|
cell.textLabel?.text = preferenceType.title
|
||||||
|
|
||||||
|
switch preferenceType {
|
||||||
|
case .darkMode:
|
||||||
|
cell.switchButton.isOn = setting.preferredTrueBlackDarkMode
|
||||||
|
case .disableAvatarAnimation:
|
||||||
|
cell.switchButton.isOn = setting.preferredStaticAvatar
|
||||||
|
case .useDefaultBrowser:
|
||||||
|
cell.switchButton.isOn = setting.preferredUsingDefaultBrowser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func configureSettingToggle(
|
||||||
|
cell: SettingsToggleTableViewCell,
|
||||||
|
switchMode: SettingsItem.NotificationSwitchMode,
|
||||||
|
subscription: NotificationSubscription
|
||||||
|
) {
|
||||||
|
cell.textLabel?.text = switchMode.title
|
||||||
|
|
||||||
|
let enabled: Bool?
|
||||||
|
switch switchMode {
|
||||||
|
case .favorite: enabled = subscription.alert.favourite
|
||||||
|
case .follow: enabled = subscription.alert.follow
|
||||||
|
case .reblog: enabled = subscription.alert.reblog
|
||||||
|
case .mention: enabled = subscription.alert.mention
|
||||||
|
}
|
||||||
|
cell.update(enabled: enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ internal enum Asset {
|
||||||
internal enum Colors {
|
internal enum Colors {
|
||||||
internal enum Border {
|
internal enum Border {
|
||||||
internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll")
|
internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll")
|
||||||
internal static let notificationStatus = ColorAsset(name: "Colors/Border/notification.status")
|
|
||||||
internal static let searchCard = ColorAsset(name: "Colors/Border/searchCard")
|
internal static let searchCard = ColorAsset(name: "Colors/Border/searchCard")
|
||||||
internal static let status = ColorAsset(name: "Colors/Border/status")
|
internal static let status = ColorAsset(name: "Colors/Border/status")
|
||||||
}
|
}
|
||||||
|
@ -65,9 +64,6 @@ internal enum Asset {
|
||||||
internal enum Slider {
|
internal enum Slider {
|
||||||
internal static let track = ColorAsset(name: "Colors/Slider/track")
|
internal static let track = ColorAsset(name: "Colors/Slider/track")
|
||||||
}
|
}
|
||||||
internal enum TabBar {
|
|
||||||
internal static let itemInactive = ColorAsset(name: "Colors/TabBar/item.inactive")
|
|
||||||
}
|
|
||||||
internal enum TextField {
|
internal enum TextField {
|
||||||
internal static let background = ColorAsset(name: "Colors/TextField/background")
|
internal static let background = ColorAsset(name: "Colors/TextField/background")
|
||||||
internal static let invalid = ColorAsset(name: "Colors/TextField/invalid")
|
internal static let invalid = ColorAsset(name: "Colors/TextField/invalid")
|
||||||
|
@ -135,6 +131,7 @@ internal enum Asset {
|
||||||
internal static let tableViewCellSelectionBackground = ColorAsset(name: "Theme/Mastodon/table.view.cell.selection.background")
|
internal static let tableViewCellSelectionBackground = ColorAsset(name: "Theme/Mastodon/table.view.cell.selection.background")
|
||||||
internal static let tertiarySystemBackground = ColorAsset(name: "Theme/Mastodon/tertiary.system.background")
|
internal static let tertiarySystemBackground = ColorAsset(name: "Theme/Mastodon/tertiary.system.background")
|
||||||
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Theme/Mastodon/tertiary.system.grouped.background")
|
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Theme/Mastodon/tertiary.system.grouped.background")
|
||||||
|
internal static let notificationStatusBorderColor = ColorAsset(name: "Theme/Mastodon/notification.status.border.color")
|
||||||
internal static let separator = ColorAsset(name: "Theme/Mastodon/separator")
|
internal static let separator = ColorAsset(name: "Theme/Mastodon/separator")
|
||||||
internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/Mastodon/tab.bar.item.inactive.icon.color")
|
internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/Mastodon/tab.bar.item.inactive.icon.color")
|
||||||
}
|
}
|
||||||
|
@ -153,6 +150,7 @@ internal enum Asset {
|
||||||
internal static let tableViewCellSelectionBackground = ColorAsset(name: "Theme/system/table.view.cell.selection.background")
|
internal static let tableViewCellSelectionBackground = ColorAsset(name: "Theme/system/table.view.cell.selection.background")
|
||||||
internal static let tertiarySystemBackground = ColorAsset(name: "Theme/system/tertiary.system.background")
|
internal static let tertiarySystemBackground = ColorAsset(name: "Theme/system/tertiary.system.background")
|
||||||
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Theme/system/tertiary.system.grouped.background")
|
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Theme/system/tertiary.system.grouped.background")
|
||||||
|
internal static let notificationStatusBorderColor = ColorAsset(name: "Theme/system/notification.status.border.color")
|
||||||
internal static let separator = ColorAsset(name: "Theme/system/separator")
|
internal static let separator = ColorAsset(name: "Theme/system/separator")
|
||||||
internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/system/tab.bar.item.inactive.icon.color")
|
internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/system/tab.bar.item.inactive.icon.color")
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public enum MastodonStatusContent {
|
||||||
|
|
||||||
public static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult {
|
public static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult {
|
||||||
let document: String = {
|
let document: String = {
|
||||||
var content = content
|
var content = content.replacingOccurrences(of: "</p>", with: "</p>\r\n")
|
||||||
for (shortcode, url) in emojiDict {
|
for (shortcode, url) in emojiDict {
|
||||||
let emojiNode = "<span class=\"emoji\" href=\"\(url.absoluteString)\">\(shortcode)</span>"
|
let emojiNode = "<span class=\"emoji\" href=\"\(url.absoluteString)\">\(shortcode)</span>"
|
||||||
let pattern = ":\(shortcode):"
|
let pattern = ":\(shortcode):"
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
},
|
|
||||||
"properties" : {
|
|
||||||
"provides-namespace" : true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,9 +5,9 @@
|
||||||
"color-space" : "srgb",
|
"color-space" : "srgb",
|
||||||
"components" : {
|
"components" : {
|
||||||
"alpha" : "1.000",
|
"alpha" : "1.000",
|
||||||
"blue" : "232",
|
"blue" : "0.910",
|
||||||
"green" : "225",
|
"green" : "0.882",
|
||||||
"red" : "217"
|
"red" : "0.851"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
|
@ -23,9 +23,9 @@
|
||||||
"color-space" : "srgb",
|
"color-space" : "srgb",
|
||||||
"components" : {
|
"components" : {
|
||||||
"alpha" : "1.000",
|
"alpha" : "1.000",
|
||||||
"blue" : "110",
|
"blue" : "0.431",
|
||||||
"green" : "87",
|
"green" : "0.341",
|
||||||
"red" : "79"
|
"red" : "0.310"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
|
@ -5,9 +5,9 @@
|
||||||
"color-space" : "srgb",
|
"color-space" : "srgb",
|
||||||
"components" : {
|
"components" : {
|
||||||
"alpha" : "1.000",
|
"alpha" : "1.000",
|
||||||
"blue" : "140",
|
"blue" : "0.910",
|
||||||
"green" : "130",
|
"green" : "0.882",
|
||||||
"red" : "110"
|
"red" : "0.851"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
|
@ -23,9 +23,9 @@
|
||||||
"color-space" : "srgb",
|
"color-space" : "srgb",
|
||||||
"components" : {
|
"components" : {
|
||||||
"alpha" : "1.000",
|
"alpha" : "1.000",
|
||||||
"blue" : "200",
|
"blue" : "0.431",
|
||||||
"green" : "174",
|
"green" : "0.341",
|
||||||
"red" : "155"
|
"red" : "0.310"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
|
@ -90,7 +90,7 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||||
view.layer.cornerRadius = 6
|
view.layer.cornerRadius = 6
|
||||||
view.layer.cornerCurve = .continuous
|
view.layer.cornerCurve = .continuous
|
||||||
view.layer.borderWidth = 2
|
view.layer.borderWidth = 2
|
||||||
view.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor
|
view.layer.borderColor = ThemeService.shared.currentTheme.value.notificationStatusBorderColor.cgColor
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
let statusView = StatusView()
|
let statusView = StatusView()
|
||||||
|
@ -272,9 +272,7 @@ extension NotificationStatusTableViewCell {
|
||||||
extension NotificationStatusTableViewCell {
|
extension NotificationStatusTableViewCell {
|
||||||
|
|
||||||
private func setupBackgroundColor(theme: Theme) {
|
private func setupBackgroundColor(theme: Theme) {
|
||||||
// actionImageView.layer.borderColor = theme.systemBackgroundColor.cgColor
|
statusContainerView.layer.borderColor = theme.notificationStatusBorderColor.resolvedColor(with: traitCollection).cgColor
|
||||||
// avatarImageView.layer.borderColor = Asset.Theme.Mastodon.systemBackground.color.cgColor
|
|
||||||
statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor
|
|
||||||
statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in
|
statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in
|
||||||
return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor
|
return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,6 +25,15 @@ final class SearchHistoryViewModel {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.searchHistoryFetchedResultController = SearchHistoryFetchedResultController(managedObjectContext: context.managedObjectContext)
|
self.searchHistoryFetchedResultController = SearchHistoryFetchedResultController(managedObjectContext: context.managedObjectContext)
|
||||||
|
|
||||||
|
context.authenticationService.activeMastodonAuthenticationBox
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] box in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.searchHistoryFetchedResultController.domain.value = box?.domain
|
||||||
|
self.searchHistoryFetchedResultController.userID.value = box?.userID
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// may block main queue by large dataset
|
// may block main queue by large dataset
|
||||||
searchHistoryFetchedResultController.objectIDs
|
searchHistoryFetchedResultController.objectIDs
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
|
@ -81,6 +90,9 @@ extension SearchHistoryViewModel {
|
||||||
|
|
||||||
extension SearchHistoryViewModel {
|
extension SearchHistoryViewModel {
|
||||||
func persistSearchHistory(for item: SearchHistoryItem) {
|
func persistSearchHistory(for item: SearchHistoryItem) {
|
||||||
|
guard let box = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
|
let property = SearchHistory.Property(domain: box.domain, userID: box.userID)
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
case .account(let objectID):
|
case .account(let objectID):
|
||||||
let managedObjectContext = context.backgroundManagedObjectContext
|
let managedObjectContext = context.backgroundManagedObjectContext
|
||||||
|
@ -89,7 +101,7 @@ extension SearchHistoryViewModel {
|
||||||
if let searchHistory = user.searchHistory {
|
if let searchHistory = user.searchHistory {
|
||||||
searchHistory.update(updatedAt: Date())
|
searchHistory.update(updatedAt: Date())
|
||||||
} else {
|
} else {
|
||||||
SearchHistory.insert(into: managedObjectContext, account: user)
|
SearchHistory.insert(into: managedObjectContext, property: property, account: user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sink { result in
|
.sink { result in
|
||||||
|
@ -104,7 +116,7 @@ extension SearchHistoryViewModel {
|
||||||
if let searchHistory = hashtag.searchHistory {
|
if let searchHistory = hashtag.searchHistory {
|
||||||
searchHistory.update(updatedAt: Date())
|
searchHistory.update(updatedAt: Date())
|
||||||
} else {
|
} else {
|
||||||
SearchHistory.insert(into: managedObjectContext, hashtag: hashtag)
|
SearchHistory.insert(into: managedObjectContext, property: property, hashtag: hashtag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sink { result in
|
.sink { result in
|
||||||
|
|
|
@ -142,6 +142,7 @@ extension SearchResultViewModel {
|
||||||
extension SearchResultViewModel {
|
extension SearchResultViewModel {
|
||||||
func persistSearchHistory(for item: SearchResultItem) {
|
func persistSearchHistory(for item: SearchResultItem) {
|
||||||
guard let box = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
guard let box = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
||||||
|
let property = SearchHistory.Property(domain: box.domain, userID: box.userID)
|
||||||
let domain = box.domain
|
let domain = box.domain
|
||||||
|
|
||||||
switch item {
|
switch item {
|
||||||
|
@ -160,7 +161,7 @@ extension SearchResultViewModel {
|
||||||
if let searchHistory = user.searchHistory {
|
if let searchHistory = user.searchHistory {
|
||||||
searchHistory.update(updatedAt: Date())
|
searchHistory.update(updatedAt: Date())
|
||||||
} else {
|
} else {
|
||||||
SearchHistory.insert(into: managedObjectContext, account: user)
|
SearchHistory.insert(into: managedObjectContext, property: property, account: user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sink { result in
|
.sink { result in
|
||||||
|
@ -178,7 +179,7 @@ extension SearchResultViewModel {
|
||||||
if let searchHistory = hashtag.searchHistory {
|
if let searchHistory = hashtag.searchHistory {
|
||||||
searchHistory.update(updatedAt: Date())
|
searchHistory.update(updatedAt: Date())
|
||||||
} else {
|
} else {
|
||||||
SearchHistory.insert(into: managedObjectContext, hashtag: hashtag)
|
SearchHistory.insert(into: managedObjectContext, property: property, hashtag: hashtag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sink { result in
|
.sink { result in
|
||||||
|
|
|
@ -358,13 +358,10 @@ extension SettingsViewController: UITableViewDelegate {
|
||||||
case .appearance:
|
case .appearance:
|
||||||
// do nothing
|
// do nothing
|
||||||
break
|
break
|
||||||
case .preferenceDarkMode, .preferenceDisableAvatarAnimation:
|
|
||||||
// do nothing
|
|
||||||
break
|
|
||||||
case .notification:
|
case .notification:
|
||||||
// do nothing
|
// do nothing
|
||||||
break
|
break
|
||||||
case .preferenceUsingDefaultBrowser:
|
case .preference:
|
||||||
// do nothing
|
// do nothing
|
||||||
break
|
break
|
||||||
case .boringZone(let link), .spicyZone(let link):
|
case .boringZone(let link), .spicyZone(let link):
|
||||||
|
@ -476,48 +473,30 @@ extension SettingsViewController: SettingsToggleCellDelegate {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
case .preferenceDarkMode(let settingObjectID):
|
case .preference(let settingObjectID, let preferenceType):
|
||||||
let managedObjectContext = context.backgroundManagedObjectContext
|
let managedObjectContext = context.backgroundManagedObjectContext
|
||||||
managedObjectContext.performChanges {
|
managedObjectContext.performChanges {
|
||||||
let setting = managedObjectContext.object(with: settingObjectID) as! Setting
|
let setting = managedObjectContext.object(with: settingObjectID) as! Setting
|
||||||
setting.update(preferredTrueBlackDarkMode: isOn)
|
switch preferenceType {
|
||||||
}
|
case .darkMode:
|
||||||
.sink { result in
|
setting.update(preferredTrueBlackDarkMode: isOn)
|
||||||
switch result {
|
case .disableAvatarAnimation:
|
||||||
case .success:
|
setting.update(preferredStaticAvatar: isOn)
|
||||||
ThemeService.shared.set(themeName: isOn ? .system : .mastodon)
|
case .useDefaultBrowser:
|
||||||
case .failure(let error):
|
setting.update(preferredUsingDefaultBrowser: isOn)
|
||||||
assertionFailure(error.localizedDescription)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
|
||||||
case .preferenceDisableAvatarAnimation(let settingObjectID):
|
|
||||||
let managedObjectContext = context.backgroundManagedObjectContext
|
|
||||||
managedObjectContext.performChanges {
|
|
||||||
let setting = managedObjectContext.object(with: settingObjectID) as! Setting
|
|
||||||
setting.update(preferredStaticAvatar: isOn)
|
|
||||||
}
|
|
||||||
.sink { result in
|
.sink { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
UserDefaults.shared.preferredStaticAvatar = isOn
|
switch preferenceType {
|
||||||
case .failure(let error):
|
case .darkMode:
|
||||||
assertionFailure(error.localizedDescription)
|
ThemeService.shared.set(themeName: isOn ? .system : .mastodon)
|
||||||
break
|
case .disableAvatarAnimation:
|
||||||
}
|
UserDefaults.shared.preferredStaticAvatar = isOn
|
||||||
}
|
case .useDefaultBrowser:
|
||||||
.store(in: &disposeBag)
|
UserDefaults.shared.preferredUsingDefaultBrowser = isOn
|
||||||
case .preferenceUsingDefaultBrowser(let settingObjectID):
|
}
|
||||||
let managedObjectContext = context.backgroundManagedObjectContext
|
|
||||||
managedObjectContext.performChanges {
|
|
||||||
let setting = managedObjectContext.object(with: settingObjectID) as! Setting
|
|
||||||
setting.update(preferredUsingDefaultBrowser: isOn)
|
|
||||||
}
|
|
||||||
.sink { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
UserDefaults.shared.preferredUsingDefaultBrowser = isOn
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
assertionFailure(error.localizedDescription)
|
assertionFailure(error.localizedDescription)
|
||||||
break
|
break
|
||||||
|
|
|
@ -122,9 +122,9 @@ extension SettingsViewModel {
|
||||||
// preference
|
// preference
|
||||||
snapshot.appendSections([.preference])
|
snapshot.appendSections([.preference])
|
||||||
let preferenceItems: [SettingsItem] = [
|
let preferenceItems: [SettingsItem] = [
|
||||||
.preferenceDarkMode(settingObjectID: setting.objectID),
|
.preference(settingObjectID: setting.objectID, preferenceType: .darkMode),
|
||||||
.preferenceDisableAvatarAnimation(settingObjectID: setting.objectID),
|
.preference(settingObjectID: setting.objectID, preferenceType: .disableAvatarAnimation),
|
||||||
.preferenceUsingDefaultBrowser(settingObjectID: setting.objectID),
|
.preference(settingObjectID: setting.objectID, preferenceType: .useDefaultBrowser),
|
||||||
]
|
]
|
||||||
snapshot.appendItems(preferenceItems,toSection: .preference)
|
snapshot.appendItems(preferenceItems,toSection: .preference)
|
||||||
|
|
||||||
|
@ -163,123 +163,12 @@ extension SettingsViewModel {
|
||||||
settingsAppearanceTableViewCellDelegate: SettingsAppearanceTableViewCellDelegate,
|
settingsAppearanceTableViewCellDelegate: SettingsAppearanceTableViewCellDelegate,
|
||||||
settingsToggleCellDelegate: SettingsToggleCellDelegate
|
settingsToggleCellDelegate: SettingsToggleCellDelegate
|
||||||
) {
|
) {
|
||||||
dataSource = UITableViewDiffableDataSource(tableView: tableView) { [
|
dataSource = SettingsSection.tableViewDiffableDataSource(
|
||||||
weak self,
|
for: tableView,
|
||||||
weak settingsAppearanceTableViewCellDelegate,
|
managedObjectContext: context.managedObjectContext,
|
||||||
weak settingsToggleCellDelegate
|
settingsAppearanceTableViewCellDelegate: settingsAppearanceTableViewCellDelegate,
|
||||||
] tableView, indexPath, item -> UITableViewCell? in
|
settingsToggleCellDelegate: settingsToggleCellDelegate
|
||||||
guard let self = self else { return nil }
|
)
|
||||||
switch item {
|
|
||||||
case .appearance(let objectID):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell
|
|
||||||
self.context.managedObjectContext.performAndWait {
|
|
||||||
let setting = self.context.managedObjectContext.object(with: objectID) as! Setting
|
|
||||||
cell.update(with: setting.appearance)
|
|
||||||
ManagedObjectObserver.observe(object: setting)
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink(receiveCompletion: { _ in
|
|
||||||
// do nothing
|
|
||||||
}, receiveValue: { [weak cell] change in
|
|
||||||
guard let cell = cell else { return }
|
|
||||||
guard case .update(let object) = change.changeType,
|
|
||||||
let setting = object as? Setting else { return }
|
|
||||||
cell.update(with: setting.appearance)
|
|
||||||
})
|
|
||||||
.store(in: &cell.disposeBag)
|
|
||||||
}
|
|
||||||
cell.delegate = settingsAppearanceTableViewCellDelegate
|
|
||||||
return cell
|
|
||||||
case .preferenceDarkMode(let objectID),
|
|
||||||
.preferenceDisableAvatarAnimation(let objectID),
|
|
||||||
.preferenceUsingDefaultBrowser(let objectID):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell
|
|
||||||
cell.delegate = settingsToggleCellDelegate
|
|
||||||
self.context.managedObjectContext.performAndWait {
|
|
||||||
let setting = self.context.managedObjectContext.object(with: objectID) as! Setting
|
|
||||||
SettingsViewModel.configureSettingToggle(cell: cell, item: item, setting: setting)
|
|
||||||
|
|
||||||
ManagedObjectObserver.observe(object: setting)
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink(receiveCompletion: { _ in
|
|
||||||
// do nothing
|
|
||||||
}, receiveValue: { [weak cell] change in
|
|
||||||
guard let cell = cell else { return }
|
|
||||||
guard case .update(let object) = change.changeType,
|
|
||||||
let setting = object as? Setting else { return }
|
|
||||||
SettingsViewModel.configureSettingToggle(cell: cell, item: item, setting: setting)
|
|
||||||
})
|
|
||||||
.store(in: &cell.disposeBag)
|
|
||||||
}
|
|
||||||
return cell
|
|
||||||
case .notification(let objectID, let switchMode):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsToggleTableViewCell.self), for: indexPath) as! SettingsToggleTableViewCell
|
|
||||||
self.context.managedObjectContext.performAndWait {
|
|
||||||
let setting = self.context.managedObjectContext.object(with: objectID) as! Setting
|
|
||||||
if let subscription = setting.activeSubscription {
|
|
||||||
SettingsViewModel.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription)
|
|
||||||
}
|
|
||||||
ManagedObjectObserver.observe(object: setting)
|
|
||||||
.sink(receiveCompletion: { _ in
|
|
||||||
// do nothing
|
|
||||||
}, receiveValue: { [weak cell] change in
|
|
||||||
guard let cell = cell else { return }
|
|
||||||
guard case .update(let object) = change.changeType,
|
|
||||||
let setting = object as? Setting else { return }
|
|
||||||
guard let subscription = setting.activeSubscription else { return }
|
|
||||||
SettingsViewModel.configureSettingToggle(cell: cell, switchMode: switchMode, subscription: subscription)
|
|
||||||
})
|
|
||||||
.store(in: &cell.disposeBag)
|
|
||||||
}
|
|
||||||
cell.delegate = settingsToggleCellDelegate
|
|
||||||
return cell
|
|
||||||
case .boringZone(let item), .spicyZone(let item):
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsLinkTableViewCell.self), for: indexPath) as! SettingsLinkTableViewCell
|
|
||||||
cell.update(with: item)
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processDataSource(self.setting.value)
|
processDataSource(self.setting.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SettingsViewModel {
|
|
||||||
|
|
||||||
static func configureSettingToggle(
|
|
||||||
cell: SettingsToggleTableViewCell,
|
|
||||||
item: SettingsItem,
|
|
||||||
setting: Setting
|
|
||||||
) {
|
|
||||||
switch item {
|
|
||||||
case .preferenceDarkMode:
|
|
||||||
cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.trueBlackDarkMode
|
|
||||||
cell.switchButton.isOn = setting.preferredTrueBlackDarkMode
|
|
||||||
case .preferenceDisableAvatarAnimation:
|
|
||||||
cell.textLabel?.text = L10n.Scene.Settings.Section.AppearanceSettings.disableAvatarAnimation
|
|
||||||
cell.switchButton.isOn = setting.preferredStaticAvatar
|
|
||||||
case .preferenceUsingDefaultBrowser:
|
|
||||||
cell.textLabel?.text = L10n.Scene.Settings.Section.Preference.usingDefaultBrowser
|
|
||||||
cell.switchButton.isOn = setting.preferredUsingDefaultBrowser
|
|
||||||
default:
|
|
||||||
assertionFailure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func configureSettingToggle(
|
|
||||||
cell: SettingsToggleTableViewCell,
|
|
||||||
switchMode: SettingsItem.NotificationSwitchMode,
|
|
||||||
subscription: NotificationSubscription
|
|
||||||
) {
|
|
||||||
cell.textLabel?.text = switchMode.title
|
|
||||||
|
|
||||||
let enabled: Bool?
|
|
||||||
switch switchMode {
|
|
||||||
case .favorite: enabled = subscription.alert.favourite
|
|
||||||
case .follow: enabled = subscription.alert.follow
|
|
||||||
case .reblog: enabled = subscription.alert.reblog
|
|
||||||
case .mention: enabled = subscription.alert.mention
|
|
||||||
}
|
|
||||||
cell.update(enabled: enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,6 +22,12 @@ class SettingsToggleTableViewCell: UITableViewCell {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
weak var delegate: SettingsToggleCellDelegate?
|
weak var delegate: SettingsToggleCellDelegate?
|
||||||
|
|
||||||
|
override func prepareForReuse() {
|
||||||
|
super.prepareForReuse()
|
||||||
|
|
||||||
|
disposeBag.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
|
|
@ -34,4 +34,5 @@ struct MastodonTheme: Theme {
|
||||||
let contentWarningOverlayBackgroundColor = Asset.Theme.Mastodon.contentWarningOverlayBackground.color
|
let contentWarningOverlayBackgroundColor = Asset.Theme.Mastodon.contentWarningOverlayBackground.color
|
||||||
let profileFieldCollectionViewBackgroundColor = Asset.Theme.Mastodon.profileFieldCollectionViewBackground.color
|
let profileFieldCollectionViewBackgroundColor = Asset.Theme.Mastodon.profileFieldCollectionViewBackground.color
|
||||||
let composeToolbarBackgroundColor = Asset.Theme.Mastodon.composeToolbarBackground.color
|
let composeToolbarBackgroundColor = Asset.Theme.Mastodon.composeToolbarBackground.color
|
||||||
|
let notificationStatusBorderColor = Asset.Theme.System.notificationStatusBorderColor.color
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,4 +34,5 @@ struct SystemTheme: Theme {
|
||||||
let contentWarningOverlayBackgroundColor = Asset.Theme.System.contentWarningOverlayBackground.color
|
let contentWarningOverlayBackgroundColor = Asset.Theme.System.contentWarningOverlayBackground.color
|
||||||
let profileFieldCollectionViewBackgroundColor = Asset.Theme.System.profileFieldCollectionViewBackground.color
|
let profileFieldCollectionViewBackgroundColor = Asset.Theme.System.profileFieldCollectionViewBackground.color
|
||||||
let composeToolbarBackgroundColor = Asset.Theme.System.composeToolbarBackground.color
|
let composeToolbarBackgroundColor = Asset.Theme.System.composeToolbarBackground.color
|
||||||
|
let notificationStatusBorderColor = Asset.Theme.System.notificationStatusBorderColor.color
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ public protocol Theme {
|
||||||
var contentWarningOverlayBackgroundColor: UIColor { get }
|
var contentWarningOverlayBackgroundColor: UIColor { get }
|
||||||
var profileFieldCollectionViewBackgroundColor: UIColor { get }
|
var profileFieldCollectionViewBackgroundColor: UIColor { get }
|
||||||
var composeToolbarBackgroundColor: UIColor { get }
|
var composeToolbarBackgroundColor: UIColor { get }
|
||||||
|
var notificationStatusBorderColor: UIColor { get }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue