Merge branch 'release/1.0.3'
This commit is contained in:
commit
04bd283f46
|
@ -0,0 +1,138 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>plural.count.metric_formatted.post</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%@ %#@post_count@</string>
|
||||||
|
<key>post_count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>ld</string>
|
||||||
|
<key>zero</key>
|
||||||
|
<string>posts</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>post</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>posts</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>posts</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>posts</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>plural.count.favorite</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@favorite_count@</string>
|
||||||
|
<key>favorite_count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>ld</string>
|
||||||
|
<key>zero</key>
|
||||||
|
<string>0 favorites</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 favorite</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>%ld favorites</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>%ld favorites</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%ld favorites</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>plural.count.reblog</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@reblog_count@</string>
|
||||||
|
<key>reblog_count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>ld</string>
|
||||||
|
<key>zero</key>
|
||||||
|
<string>0 reblogs</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 reblog</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>%ld reblogs</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>%ld reblogs</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%ld reblogs</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>plural.count.vote</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@vote_count@</string>
|
||||||
|
<key>vote_count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>ld</string>
|
||||||
|
<key>zero</key>
|
||||||
|
<string>0 votes</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 vote</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>%ld votes</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>%ld votes</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%ld votes</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>plural.count.voter</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@voter_count@</string>
|
||||||
|
<key>voter_count</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>ld</string>
|
||||||
|
<key>zero</key>
|
||||||
|
<string>0 voters</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 voter</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>%ld voters</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>%ld voters</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%ld voters</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>plural.people_talking</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@count_people_talking@</string>
|
||||||
|
<key>count_people_talking</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>ld</string>
|
||||||
|
<key>zero</key>
|
||||||
|
<string>0 people talking</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>1 people talking</string>
|
||||||
|
<key>few</key>
|
||||||
|
<string>%ld people talking</string>
|
||||||
|
<key>many</key>
|
||||||
|
<string>%ld people talking</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%ld people talking</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -280,6 +280,7 @@
|
||||||
DB4FFC2C269EC39600D62E92 /* SearchTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4FFC2A269EC39600D62E92 /* SearchTransitionController.swift */; };
|
DB4FFC2C269EC39600D62E92 /* SearchTransitionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4FFC2A269EC39600D62E92 /* SearchTransitionController.swift */; };
|
||||||
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D170262832380062B7A1 /* BlurHashDecode.swift */; };
|
DB51D172262832380062B7A1 /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D170262832380062B7A1 /* BlurHashDecode.swift */; };
|
||||||
DB51D173262832380062B7A1 /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D171262832380062B7A1 /* BlurHashEncode.swift */; };
|
DB51D173262832380062B7A1 /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB51D171262832380062B7A1 /* BlurHashEncode.swift */; };
|
||||||
|
DB552D4F26BBD10C00E481F6 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = DB552D4E26BBD10C00E481F6 /* OrderedCollections */; };
|
||||||
DB564BD0269F2F83001E39A7 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DB564BCE269F2F83001E39A7 /* Localizable.stringsdict */; };
|
DB564BD0269F2F83001E39A7 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DB564BCE269F2F83001E39A7 /* Localizable.stringsdict */; };
|
||||||
DB564BD3269F3B35001E39A7 /* StatusFilterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */; };
|
DB564BD3269F3B35001E39A7 /* StatusFilterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */; };
|
||||||
DB59F0FE25EF5D96001F1DAB /* StatusProvider+UITableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F0FD25EF5D96001F1DAB /* StatusProvider+UITableViewDelegate.swift */; };
|
DB59F0FE25EF5D96001F1DAB /* StatusProvider+UITableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F0FD25EF5D96001F1DAB /* StatusProvider+UITableViewDelegate.swift */; };
|
||||||
|
@ -1311,6 +1312,7 @@
|
||||||
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */,
|
DB6804862637CD4C00430867 /* AppShared.framework in Frameworks */,
|
||||||
DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */,
|
DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */,
|
||||||
DBAC6483267D0B21007FE9FD /* DifferenceKit in Frameworks */,
|
DBAC6483267D0B21007FE9FD /* DifferenceKit in Frameworks */,
|
||||||
|
DB552D4F26BBD10C00E481F6 /* OrderedCollections in Frameworks */,
|
||||||
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */,
|
2D61336925C18A4F00CAE157 /* AlamofireNetworkActivityIndicator in Frameworks */,
|
||||||
DB01E23326A98F0900C3965B /* MastodonMeta in Frameworks */,
|
DB01E23326A98F0900C3965B /* MastodonMeta in Frameworks */,
|
||||||
DBAC64A1267E6D02007FE9FD /* Fuzi in Frameworks */,
|
DBAC64A1267E6D02007FE9FD /* Fuzi in Frameworks */,
|
||||||
|
@ -3081,6 +3083,7 @@
|
||||||
DBC6462A26A1738900B0E31B /* MastodonUI */,
|
DBC6462A26A1738900B0E31B /* MastodonUI */,
|
||||||
DB01E23226A98F0900C3965B /* MastodonMeta */,
|
DB01E23226A98F0900C3965B /* MastodonMeta */,
|
||||||
DB01E23426A98F0900C3965B /* MetaTextKit */,
|
DB01E23426A98F0900C3965B /* MetaTextKit */,
|
||||||
|
DB552D4E26BBD10C00E481F6 /* OrderedCollections */,
|
||||||
);
|
);
|
||||||
productName = Mastodon;
|
productName = Mastodon;
|
||||||
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
|
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
|
||||||
|
@ -3328,6 +3331,7 @@
|
||||||
DBF7A0FA26830C33004176A2 /* XCRemoteSwiftPackageReference "FPSIndicator" */,
|
DBF7A0FA26830C33004176A2 /* XCRemoteSwiftPackageReference "FPSIndicator" */,
|
||||||
DB0E2D2C26833FF600865C3C /* XCRemoteSwiftPackageReference "Nuke-FLAnimatedImage-Plugin" */,
|
DB0E2D2C26833FF600865C3C /* XCRemoteSwiftPackageReference "Nuke-FLAnimatedImage-Plugin" */,
|
||||||
DB01E23126A98F0900C3965B /* XCRemoteSwiftPackageReference "MetaTextKit" */,
|
DB01E23126A98F0900C3965B /* XCRemoteSwiftPackageReference "MetaTextKit" */,
|
||||||
|
DB552D4D26BBD10C00E481F6 /* XCRemoteSwiftPackageReference "swift-collections" */,
|
||||||
);
|
);
|
||||||
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
@ -4482,7 +4486,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
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;
|
||||||
|
@ -4490,7 +4494,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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 = "";
|
||||||
|
@ -4509,7 +4513,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
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;
|
||||||
|
@ -4517,7 +4521,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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 = "";
|
||||||
|
@ -4772,7 +4776,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4780,7 +4784,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -4796,7 +4800,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4804,7 +4808,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -4820,7 +4824,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4828,7 +4832,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -4844,7 +4848,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4852,7 +4856,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.MastodonIntent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
@ -4868,7 +4872,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4876,7 +4880,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -4892,7 +4896,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4900,7 +4904,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -4916,7 +4920,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4924,7 +4928,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -4940,7 +4944,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -4948,7 +4952,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -5030,7 +5034,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
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;
|
||||||
|
@ -5038,7 +5042,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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 = "";
|
||||||
|
@ -5144,7 +5148,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5152,7 +5156,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -5264,7 +5268,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
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;
|
||||||
|
@ -5272,7 +5276,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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 = "";
|
||||||
|
@ -5378,7 +5382,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5386,7 +5390,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -5432,7 +5436,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5440,7 +5444,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -5455,7 +5459,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 = 52;
|
CURRENT_PROJECT_VERSION = 53;
|
||||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -5463,7 +5467,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.2;
|
MARKETING_VERSION = 1.0.3;
|
||||||
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;
|
||||||
|
@ -5644,6 +5648,14 @@
|
||||||
minimumVersion = 4.1.0;
|
minimumVersion = 4.1.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
DB552D4D26BBD10C00E481F6 /* XCRemoteSwiftPackageReference "swift-collections" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/apple/swift-collections.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 0.0.5;
|
||||||
|
};
|
||||||
|
};
|
||||||
DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
||||||
|
@ -5752,6 +5764,11 @@
|
||||||
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;
|
package = DB3D0FF125BAA61700EAA174 /* XCRemoteSwiftPackageReference "AlamofireImage" */;
|
||||||
productName = AlamofireImage;
|
productName = AlamofireImage;
|
||||||
};
|
};
|
||||||
|
DB552D4E26BBD10C00E481F6 /* OrderedCollections */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = DB552D4D26BBD10C00E481F6 /* XCRemoteSwiftPackageReference "swift-collections" */;
|
||||||
|
productName = OrderedCollections;
|
||||||
|
};
|
||||||
DB68050F2637D0F800430867 /* KeychainAccess */ = {
|
DB68050F2637D0F800430867 /* KeychainAccess */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */;
|
package = DB6804722637CC1200430867 /* XCRemoteSwiftPackageReference "KeychainAccess" */;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<key>AppShared.xcscheme_^#shared#^_</key>
|
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>24</integer>
|
<integer>27</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>28</integer>
|
<integer>24</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
|
|
@ -136,6 +136,15 @@
|
||||||
"version": "5.11.1"
|
"version": "5.11.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"package": "swift-collections",
|
||||||
|
"repositoryURL": "https://github.com/apple/swift-collections.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "0959ba76a1d4a98fd11163aa83fd49c25b93bfae",
|
||||||
|
"version": "0.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "swift-nio",
|
"package": "swift-nio",
|
||||||
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
||||||
|
|
|
@ -10,6 +10,9 @@ import CoreDataStack
|
||||||
import Foundation
|
import Foundation
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MetaTextKit
|
||||||
|
import MastodonMeta
|
||||||
|
import Combine
|
||||||
|
|
||||||
enum RecommendAccountSection: Equatable, Hashable {
|
enum RecommendAccountSection: Equatable, Hashable {
|
||||||
case main
|
case main
|
||||||
|
@ -18,18 +21,118 @@ enum RecommendAccountSection: Equatable, Hashable {
|
||||||
extension RecommendAccountSection {
|
extension RecommendAccountSection {
|
||||||
static func collectionViewDiffableDataSource(
|
static func collectionViewDiffableDataSource(
|
||||||
for collectionView: UICollectionView,
|
for collectionView: UICollectionView,
|
||||||
|
dependency: NeedsDependency,
|
||||||
delegate: SearchRecommendAccountsCollectionViewCellDelegate,
|
delegate: SearchRecommendAccountsCollectionViewCellDelegate,
|
||||||
managedObjectContext: NSManagedObjectContext
|
managedObjectContext: NSManagedObjectContext
|
||||||
) -> UICollectionViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID> {
|
) -> UICollectionViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID> {
|
||||||
UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak delegate] collectionView, indexPath, objectID -> UICollectionViewCell? in
|
UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak delegate] collectionView, indexPath, objectID -> UICollectionViewCell? in
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: SearchRecommendAccountsCollectionViewCell.self), for: indexPath) as! SearchRecommendAccountsCollectionViewCell
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: SearchRecommendAccountsCollectionViewCell.self), for: indexPath) as! SearchRecommendAccountsCollectionViewCell
|
||||||
let user = managedObjectContext.object(with: objectID) as! MastodonUser
|
managedObjectContext.performAndWait {
|
||||||
|
let user = managedObjectContext.object(with: objectID) as! MastodonUser
|
||||||
|
configure(cell: cell, user: user, dependency: dependency)
|
||||||
|
}
|
||||||
cell.delegate = delegate
|
cell.delegate = delegate
|
||||||
cell.config(with: user)
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func configure(
|
||||||
|
cell: SearchRecommendAccountsCollectionViewCell,
|
||||||
|
user: MastodonUser,
|
||||||
|
dependency: NeedsDependency
|
||||||
|
) {
|
||||||
|
configureContent(cell: cell, user: user)
|
||||||
|
|
||||||
|
if let currentMastodonUser = dependency.context.authenticationService.activeMastodonAuthentication.value?.user {
|
||||||
|
configureFollowButton(with: user, currentMastodonUser: currentMastodonUser, followButton: cell.followButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
Publishers.CombineLatest(
|
||||||
|
ManagedObjectObserver.observe(object: user).eraseToAnyPublisher().mapError { $0 as Error },
|
||||||
|
dependency.context.authenticationService.activeMastodonAuthentication.setFailureType(to: Error.self)
|
||||||
|
)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { _ in
|
||||||
|
// do nothing
|
||||||
|
} receiveValue: { [weak cell] change, authentication in
|
||||||
|
guard let cell = cell else { return }
|
||||||
|
guard case .update(let object) = change.changeType,
|
||||||
|
let user = object as? MastodonUser else { return }
|
||||||
|
guard let currentMastodonUser = authentication?.user else { return }
|
||||||
|
|
||||||
|
configureFollowButton(with: user, currentMastodonUser: currentMastodonUser, followButton: cell.followButton)
|
||||||
|
}
|
||||||
|
.store(in: &cell.disposeBag)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static func configureContent(
|
||||||
|
cell: SearchRecommendAccountsCollectionViewCell,
|
||||||
|
user: MastodonUser
|
||||||
|
) {
|
||||||
|
do {
|
||||||
|
let mastodonContent = MastodonContent(content: user.displayNameWithFallback, emojis: user.emojiMeta)
|
||||||
|
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
||||||
|
cell.displayNameLabel.configure(content: metaContent)
|
||||||
|
} catch {
|
||||||
|
let metaContent = PlaintextMetaContent(string: user.displayNameWithFallback)
|
||||||
|
cell.displayNameLabel.configure(content: metaContent)
|
||||||
|
}
|
||||||
|
cell.acctLabel.text = "@" + user.acct
|
||||||
|
cell.avatarImageView.af.setImage(
|
||||||
|
withURL: user.avatarImageURLWithFallback(domain: user.domain),
|
||||||
|
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||||
|
imageTransition: .crossDissolve(0.2)
|
||||||
|
)
|
||||||
|
cell.headerImageView.af.setImage(
|
||||||
|
withURL: URL(string: user.header)!,
|
||||||
|
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||||
|
imageTransition: .crossDissolve(0.2)
|
||||||
|
) { [weak cell] _ in
|
||||||
|
// guard let cell = cell else { return }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func configureFollowButton(
|
||||||
|
with mastodonUser: MastodonUser,
|
||||||
|
currentMastodonUser: MastodonUser,
|
||||||
|
followButton: HighlightDimmableButton
|
||||||
|
) {
|
||||||
|
let relationshipActionSet = relationShipActionSet(mastodonUser: mastodonUser, currentMastodonUser: currentMastodonUser)
|
||||||
|
followButton.setTitle(relationshipActionSet.title, for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func relationShipActionSet(
|
||||||
|
mastodonUser: MastodonUser,
|
||||||
|
currentMastodonUser: MastodonUser
|
||||||
|
) -> ProfileViewModel.RelationshipActionOptionSet {
|
||||||
|
var relationshipActionSet = ProfileViewModel.RelationshipActionOptionSet([.follow])
|
||||||
|
let isFollowing = mastodonUser.followingBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
||||||
|
if isFollowing {
|
||||||
|
relationshipActionSet.insert(.following)
|
||||||
|
}
|
||||||
|
|
||||||
|
let isPending = mastodonUser.followRequestedBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
||||||
|
if isPending {
|
||||||
|
relationshipActionSet.insert(.pending)
|
||||||
|
}
|
||||||
|
|
||||||
|
let isBlocking = mastodonUser.blockingBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
||||||
|
if isBlocking {
|
||||||
|
relationshipActionSet.insert(.blocking)
|
||||||
|
}
|
||||||
|
|
||||||
|
let isBlockedBy = currentMastodonUser.blockingBy.flatMap { $0.contains(mastodonUser) } ?? false
|
||||||
|
if isBlockedBy {
|
||||||
|
relationshipActionSet.insert(.blocked)
|
||||||
|
}
|
||||||
|
return relationshipActionSet
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RecommendAccountSection {
|
||||||
|
|
||||||
static func tableViewDiffableDataSource(
|
static func tableViewDiffableDataSource(
|
||||||
for tableView: UITableView,
|
for tableView: UITableView,
|
||||||
managedObjectContext: NSManagedObjectContext,
|
managedObjectContext: NSManagedObjectContext,
|
||||||
|
|
|
@ -260,7 +260,7 @@ extension StatusProviderFacade {
|
||||||
|
|
||||||
// haptic feedback generator
|
// haptic feedback generator
|
||||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||||
let responseFeedbackGenerator = UIImpactFeedbackGenerator(style: .medium)
|
let responseFeedbackGenerator = UINotificationFeedbackGenerator()
|
||||||
|
|
||||||
status
|
status
|
||||||
.compactMap { status -> (NSManagedObjectID, Mastodon.API.Favorites.FavoriteKind)? in
|
.compactMap { status -> (NSManagedObjectID, Mastodon.API.Favorites.FavoriteKind)? in
|
||||||
|
@ -284,13 +284,13 @@ extension StatusProviderFacade {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.handleEvents { _ in
|
.handleEvents(receiveSubscription: { _ in
|
||||||
generator.prepare()
|
generator.prepare()
|
||||||
responseFeedbackGenerator.prepare()
|
}, receiveOutput: { _, favoriteKind in
|
||||||
} receiveOutput: { _, favoriteKind in
|
|
||||||
generator.impactOccurred()
|
generator.impactOccurred()
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: [Like] update local status like status to: %s", ((#file as NSString).lastPathComponent), #line, #function, favoriteKind == .create ? "like" : "unlike")
|
os_log("%{public}s[%{public}ld], %{public}s: [Like] update local status like status to: %s", ((#file as NSString).lastPathComponent), #line, #function, favoriteKind == .create ? "like" : "unlike")
|
||||||
} receiveCompletion: { completion in
|
}, receiveCompletion: { completion in
|
||||||
|
responseFeedbackGenerator.prepare()
|
||||||
switch completion {
|
switch completion {
|
||||||
case .failure:
|
case .failure:
|
||||||
// TODO: handle error
|
// TODO: handle error
|
||||||
|
@ -298,7 +298,7 @@ extension StatusProviderFacade {
|
||||||
case .finished:
|
case .finished:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
.map { statusID, favoriteKind in
|
.map { statusID, favoriteKind in
|
||||||
return context.apiService.favorite(
|
return context.apiService.favorite(
|
||||||
statusID: statusID,
|
statusID: statusID,
|
||||||
|
@ -309,14 +309,13 @@ extension StatusProviderFacade {
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak provider] completion in
|
.sink { [weak provider] completion in
|
||||||
guard let provider = provider else { return }
|
guard let _ = provider else { return }
|
||||||
if provider.view.window != nil {
|
|
||||||
responseFeedbackGenerator.impactOccurred()
|
|
||||||
}
|
|
||||||
switch completion {
|
switch completion {
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
|
responseFeedbackGenerator.notificationOccurred(.error)
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: [Like] remote like request fail: %{public}s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
os_log("%{public}s[%{public}ld], %{public}s: [Like] remote like request fail: %{public}s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||||
case .finished:
|
case .finished:
|
||||||
|
responseFeedbackGenerator.notificationOccurred(.success)
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: [Like] remote like request success", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log("%{public}s[%{public}ld], %{public}s: [Like] remote like request success", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
}
|
}
|
||||||
} receiveValue: { response in
|
} receiveValue: { response in
|
||||||
|
@ -370,7 +369,7 @@ extension StatusProviderFacade {
|
||||||
|
|
||||||
// haptic feedback generator
|
// haptic feedback generator
|
||||||
let generator = UIImpactFeedbackGenerator(style: .light)
|
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||||
let responseFeedbackGenerator = UIImpactFeedbackGenerator(style: .medium)
|
let responseFeedbackGenerator = UINotificationFeedbackGenerator()
|
||||||
|
|
||||||
status
|
status
|
||||||
.compactMap { status -> (NSManagedObjectID, Mastodon.API.Reblog.ReblogKind)? in
|
.compactMap { status -> (NSManagedObjectID, Mastodon.API.Reblog.ReblogKind)? in
|
||||||
|
@ -394,10 +393,9 @@ extension StatusProviderFacade {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.handleEvents { _ in
|
.handleEvents(receiveSubscription: { _ in
|
||||||
generator.prepare()
|
generator.prepare()
|
||||||
responseFeedbackGenerator.prepare()
|
}, receiveOutput: { _, reblogKind in
|
||||||
} receiveOutput: { _, reblogKind in
|
|
||||||
generator.impactOccurred()
|
generator.impactOccurred()
|
||||||
switch reblogKind {
|
switch reblogKind {
|
||||||
case .reblog:
|
case .reblog:
|
||||||
|
@ -405,7 +403,8 @@ extension StatusProviderFacade {
|
||||||
case .undoReblog:
|
case .undoReblog:
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: [Reblog] update local status reblog status to: %s", ((#file as NSString).lastPathComponent), #line, #function, "unreblog")
|
os_log("%{public}s[%{public}ld], %{public}s: [Reblog] update local status reblog status to: %s", ((#file as NSString).lastPathComponent), #line, #function, "unreblog")
|
||||||
}
|
}
|
||||||
} receiveCompletion: { completion in
|
}, receiveCompletion: { completion in
|
||||||
|
responseFeedbackGenerator.prepare()
|
||||||
switch completion {
|
switch completion {
|
||||||
case .failure:
|
case .failure:
|
||||||
// TODO: handle error
|
// TODO: handle error
|
||||||
|
@ -413,7 +412,7 @@ extension StatusProviderFacade {
|
||||||
case .finished:
|
case .finished:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
.map { statusID, reblogKind in
|
.map { statusID, reblogKind in
|
||||||
return context.apiService.reblog(
|
return context.apiService.reblog(
|
||||||
statusID: statusID,
|
statusID: statusID,
|
||||||
|
@ -424,14 +423,13 @@ extension StatusProviderFacade {
|
||||||
.switchToLatest()
|
.switchToLatest()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak provider] completion in
|
.sink { [weak provider] completion in
|
||||||
guard let provider = provider else { return }
|
guard let _ = provider else { return }
|
||||||
if provider.view.window != nil {
|
|
||||||
responseFeedbackGenerator.impactOccurred()
|
|
||||||
}
|
|
||||||
switch completion {
|
switch completion {
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
|
responseFeedbackGenerator.notificationOccurred(.error)
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: [Reblog] remote reblog request fail: %{public}s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
os_log("%{public}s[%{public}ld], %{public}s: [Reblog] remote reblog request fail: %{public}s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||||
case .finished:
|
case .finished:
|
||||||
|
responseFeedbackGenerator.notificationOccurred(.success)
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: [Reblog] remote reblog request success", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log("%{public}s[%{public}ld], %{public}s: [Reblog] remote reblog request success", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
}
|
}
|
||||||
} receiveValue: { response in
|
} receiveValue: { response in
|
||||||
|
|
|
@ -55,13 +55,7 @@ extension MastodonPickServerViewModel.LoadIndexedServerState {
|
||||||
} receiveValue: { [weak self] response in
|
} receiveValue: { [weak self] response in
|
||||||
guard let _ = self else { return }
|
guard let _ = self else { return }
|
||||||
stateMachine.enter(Idle.self)
|
stateMachine.enter(Idle.self)
|
||||||
|
viewModel.indexedServers.value = response.value
|
||||||
// ignore approval required servers
|
|
||||||
var servers = response.value
|
|
||||||
if viewModel.mode == .signUp {
|
|
||||||
servers = servers.filter { !$0.approvalRequired }
|
|
||||||
}
|
|
||||||
viewModel.indexedServers.value = servers
|
|
||||||
}
|
}
|
||||||
.store(in: &viewModel.disposeBag)
|
.store(in: &viewModel.disposeBag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Combine
|
||||||
import GameplayKit
|
import GameplayKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
|
import OrderedCollections
|
||||||
|
|
||||||
class MastodonPickServerViewModel: NSObject {
|
class MastodonPickServerViewModel: NSObject {
|
||||||
|
|
||||||
|
@ -167,7 +168,44 @@ extension MastodonPickServerViewModel {
|
||||||
searchText.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main).removeDuplicates()
|
searchText.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main).removeDuplicates()
|
||||||
)
|
)
|
||||||
.map { indexedServers, selectCategoryItem, searchText -> [Mastodon.Entity.Server] in
|
.map { indexedServers, selectCategoryItem, searchText -> [Mastodon.Entity.Server] in
|
||||||
// Filter the indexed servers from joinmastodon.org
|
// ignore approval required servers when sign-up
|
||||||
|
var indexedServers = indexedServers
|
||||||
|
if self.mode == .signUp {
|
||||||
|
indexedServers = indexedServers.filter { !$0.approvalRequired }
|
||||||
|
}
|
||||||
|
|
||||||
|
// group by language user preferred language first. Then sort by `totalUsers`
|
||||||
|
var languageToServersMapping = OrderedDictionary<String, [Mastodon.Entity.Server]>()
|
||||||
|
for language in Locale.preferredLanguages {
|
||||||
|
let local = Locale(identifier: language)
|
||||||
|
guard let languageCode = local.languageCode else { continue }
|
||||||
|
// skip if key duplicate
|
||||||
|
guard !languageToServersMapping.keys.contains(languageCode) else { continue }
|
||||||
|
// append to dict
|
||||||
|
languageToServersMapping[languageCode] = indexedServers
|
||||||
|
.filter { $0.language.lowercased() == languageCode.lowercased() }
|
||||||
|
.sorted(by: { $0.totalUsers > $1.totalUsers })
|
||||||
|
}
|
||||||
|
// sort remains servers by `totalUsers`
|
||||||
|
let remainsServers = indexedServers
|
||||||
|
.filter { server in
|
||||||
|
return !languageToServersMapping.contains { _, servers in servers.contains(server) }
|
||||||
|
}
|
||||||
|
.sorted(by: { $0.totalUsers > $1.totalUsers })
|
||||||
|
|
||||||
|
var _indexedServers: [Mastodon.Entity.Server] = []
|
||||||
|
for key in languageToServersMapping.keys {
|
||||||
|
_indexedServers.append(contentsOf: languageToServersMapping[key] ?? [])
|
||||||
|
}
|
||||||
|
_indexedServers.append(contentsOf: remainsServers)
|
||||||
|
|
||||||
|
if _indexedServers.count == indexedServers.count {
|
||||||
|
indexedServers = _indexedServers
|
||||||
|
} else {
|
||||||
|
assertionFailure("should not change dataset size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the indexed servers by category or search text
|
||||||
switch selectCategoryItem {
|
switch selectCategoryItem {
|
||||||
case .all:
|
case .all:
|
||||||
return MastodonPickServerViewModel.filterServers(servers: indexedServers, category: nil, searchText: searchText)
|
return MastodonPickServerViewModel.filterServers(servers: indexedServers, category: nil, searchText: searchText)
|
||||||
|
|
|
@ -87,7 +87,7 @@ class PickServerCell: UITableViewCell {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let expandButton: UIButton = {
|
let expandButton: UIButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = HitTestExpandedButton(type: .custom)
|
||||||
button.setImage(UIImage(systemName: "chevron.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 13)), for: .normal)
|
button.setImage(UIImage(systemName: "chevron.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 13)), for: .normal)
|
||||||
button.setTitle(L10n.Scene.ServerPicker.Button.seeMore, for: .normal)
|
button.setTitle(L10n.Scene.ServerPicker.Button.seeMore, for: .normal)
|
||||||
button.setTitleColor(Asset.Colors.brandBlue.color, for: .normal)
|
button.setTitleColor(Asset.Colors.brandBlue.color, for: .normal)
|
||||||
|
|
|
@ -195,23 +195,34 @@ extension ProfileHeaderViewController {
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
Publishers.CombineLatest4(
|
let profileNote = Publishers.CombineLatest3(
|
||||||
viewModel.isEditing.removeDuplicates(),
|
viewModel.isEditing.removeDuplicates(),
|
||||||
viewModel.displayProfileInfo.note.removeDuplicates(),
|
viewModel.displayProfileInfo.note.removeDuplicates(),
|
||||||
viewModel.editProfileInfo.note.removeDuplicates(),
|
viewModel.editProfileInfoDidInitialized
|
||||||
|
)
|
||||||
|
.map { isEditing, displayNote, _ -> String? in
|
||||||
|
if isEditing {
|
||||||
|
return self.viewModel.editProfileInfo.note.value
|
||||||
|
} else {
|
||||||
|
return displayNote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
Publishers.CombineLatest3(
|
||||||
|
viewModel.isEditing.removeDuplicates(),
|
||||||
|
profileNote.removeDuplicates(),
|
||||||
viewModel.emojiMeta.removeDuplicates()
|
viewModel.emojiMeta.removeDuplicates()
|
||||||
)
|
)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] isEditing, note, editingNote, emojiMeta in
|
.sink { [weak self] isEditing, note, emojiMeta in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
self.profileHeaderView.bioMetaText.textView.isEditable = isEditing
|
self.profileHeaderView.bioMetaText.textView.isEditable = isEditing
|
||||||
|
|
||||||
if isEditing {
|
if isEditing {
|
||||||
if self.profileHeaderView.bioMetaText.backedString != note {
|
let metaContent = PlaintextMetaContent(string: note ?? "")
|
||||||
let metaContent = PlaintextMetaContent(string: editingNote ?? "")
|
self.profileHeaderView.bioMetaText.configure(content: metaContent)
|
||||||
self.profileHeaderView.bioMetaText.configure(content: metaContent)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let mastodonContent = MastodonContent(content: note ?? "", emojis: emojiMeta)
|
let mastodonContent = MastodonContent(content: note ?? "", emojis: emojiMeta)
|
||||||
do {
|
do {
|
||||||
|
@ -224,6 +235,7 @@ extension ProfileHeaderViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
profileHeaderView.bioMetaText.delegate = self
|
profileHeaderView.bioMetaText.delegate = self
|
||||||
|
|
||||||
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: profileHeaderView.nameTextField)
|
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: profileHeaderView.nameTextField)
|
||||||
|
@ -461,9 +473,15 @@ extension ProfileHeaderViewController {
|
||||||
extension ProfileHeaderViewController: MetaTextDelegate {
|
extension ProfileHeaderViewController: MetaTextDelegate {
|
||||||
func metaText(_ metaText: MetaText, processEditing textStorage: MetaTextStorage) -> MetaContent? {
|
func metaText(_ metaText: MetaText, processEditing textStorage: MetaTextStorage) -> MetaContent? {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: text: %s", ((#file as NSString).lastPathComponent), #line, #function, metaText.backedString)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: text: %s", ((#file as NSString).lastPathComponent), #line, #function, metaText.backedString)
|
||||||
assert(metaText.textView === profileHeaderView.bioMetaText.textView)
|
|
||||||
if metaText.textView === profileHeaderView.bioMetaText.textView {
|
switch metaText {
|
||||||
|
case profileHeaderView.bioMetaText:
|
||||||
|
guard viewModel.isEditing.value else { break }
|
||||||
viewModel.editProfileInfo.note.value = metaText.backedString
|
viewModel.editProfileInfo.note.value = metaText.backedString
|
||||||
|
let metaContent = PlaintextMetaContent(string: metaText.backedString)
|
||||||
|
return metaContent
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -31,6 +31,7 @@ final class ProfileHeaderViewModel {
|
||||||
// output
|
// output
|
||||||
let displayProfileInfo = ProfileInfo()
|
let displayProfileInfo = ProfileInfo()
|
||||||
let editProfileInfo = ProfileInfo()
|
let editProfileInfo = ProfileInfo()
|
||||||
|
let editProfileInfoDidInitialized = CurrentValueSubject<Void, Never>(Void()) // needs trigger initial event
|
||||||
let isTitleViewDisplaying = CurrentValueSubject<Bool, Never>(false)
|
let isTitleViewDisplaying = CurrentValueSubject<Bool, Never>(false)
|
||||||
var fieldDiffableDataSource: UICollectionViewDiffableDataSource<ProfileFieldSection, ProfileFieldItem>!
|
var fieldDiffableDataSource: UICollectionViewDiffableDataSource<ProfileFieldSection, ProfileFieldItem>!
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ final class ProfileHeaderViewModel {
|
||||||
self.editProfileInfo.fields.value = account?.source?.fields?.compactMap { field in
|
self.editProfileInfo.fields.value = account?.source?.fields?.compactMap { field in
|
||||||
ProfileFieldItem.FieldValue(name: field.name, value: field.value)
|
ProfileFieldItem.FieldValue(name: field.name, value: field.value)
|
||||||
} ?? []
|
} ?? []
|
||||||
|
self.editProfileInfoDidInitialized.send()
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// Created by sxiaojian on 2021/4/1.
|
// Created by sxiaojian on 2021/4/1.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
import Combine
|
import Combine
|
||||||
import CoreDataStack
|
import CoreDataStack
|
||||||
import Foundation
|
import Foundation
|
||||||
|
@ -14,12 +15,12 @@ import MetaTextKit
|
||||||
import MastodonMeta
|
import MastodonMeta
|
||||||
|
|
||||||
protocol SearchRecommendAccountsCollectionViewCellDelegate: NSObject {
|
protocol SearchRecommendAccountsCollectionViewCellDelegate: NSObject {
|
||||||
func followButtonDidPressed(clickedUser: MastodonUser)
|
func searchRecommendAccountsCollectionViewCell(_ cell: SearchRecommendAccountsCollectionViewCell, followButtonDidPressed button: UIButton)
|
||||||
|
|
||||||
func configFollowButton(with mastodonUser: MastodonUser, followButton: HighlightDimmableButton)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchRecommendAccountsCollectionViewCell: UICollectionViewCell {
|
class SearchRecommendAccountsCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
let logger = Logger(subsystem: "SearchRecommendAccountsCollectionViewCell", category: "UI")
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
|
|
||||||
weak var delegate: SearchRecommendAccountsCollectionViewCellDelegate?
|
weak var delegate: SearchRecommendAccountsCollectionViewCellDelegate?
|
||||||
|
@ -72,7 +73,6 @@ class SearchRecommendAccountsCollectionViewCell: UICollectionViewCell {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
headerImageView.af.cancelImageRequest()
|
headerImageView.af.cancelImageRequest()
|
||||||
avatarImageView.af.cancelImageRequest()
|
avatarImageView.af.cancelImageRequest()
|
||||||
visualEffectView.removeFromSuperview()
|
|
||||||
disposeBag.removeAll()
|
disposeBag.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +117,15 @@ extension SearchRecommendAccountsCollectionViewCell {
|
||||||
headerImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
|
headerImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
headerImageView.addSubview(visualEffectView)
|
||||||
|
visualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
visualEffectView.topAnchor.constraint(equalTo: headerImageView.topAnchor),
|
||||||
|
visualEffectView.leadingAnchor.constraint(equalTo: headerImageView.leadingAnchor),
|
||||||
|
visualEffectView.trailingAnchor.constraint(equalTo: headerImageView.trailingAnchor),
|
||||||
|
visualEffectView.bottomAnchor.constraint(equalTo: headerImageView.bottomAnchor)
|
||||||
|
])
|
||||||
|
|
||||||
let containerStackView = UIStackView()
|
let containerStackView = UIStackView()
|
||||||
containerStackView.axis = .vertical
|
containerStackView.axis = .vertical
|
||||||
containerStackView.distribution = .fill
|
containerStackView.distribution = .fill
|
||||||
|
@ -156,48 +165,16 @@ extension SearchRecommendAccountsCollectionViewCell {
|
||||||
followButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 24)
|
followButton.heightAnchor.constraint(greaterThanOrEqualToConstant: 24)
|
||||||
])
|
])
|
||||||
containerStackView.addArrangedSubview(followButton)
|
containerStackView.addArrangedSubview(followButton)
|
||||||
|
|
||||||
|
followButton.addTarget(self, action: #selector(SearchRecommendAccountsCollectionViewCell.followButtonDidPressed(_:)), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
func config(with mastodonUser: MastodonUser) {
|
}
|
||||||
do {
|
|
||||||
let mastodonContent = MastodonContent(content: mastodonUser.displayNameWithFallback, emojis: mastodonUser.emojiMeta)
|
extension SearchRecommendAccountsCollectionViewCell {
|
||||||
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
@objc private func followButtonDidPressed(_ sender: UIButton) {
|
||||||
displayNameLabel.configure(content: metaContent)
|
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||||
} catch {
|
delegate?.searchRecommendAccountsCollectionViewCell(self, followButtonDidPressed: sender)
|
||||||
let metaContent = PlaintextMetaContent(string: mastodonUser.displayNameWithFallback)
|
|
||||||
displayNameLabel.configure(content: metaContent)
|
|
||||||
}
|
|
||||||
acctLabel.text = "@" + mastodonUser.acct
|
|
||||||
avatarImageView.af.setImage(
|
|
||||||
withURL: URL(string: mastodonUser.avatar)!,
|
|
||||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
|
||||||
imageTransition: .crossDissolve(0.2)
|
|
||||||
)
|
|
||||||
headerImageView.af.setImage(
|
|
||||||
withURL: URL(string: mastodonUser.header)!,
|
|
||||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
|
||||||
imageTransition: .crossDissolve(0.2)
|
|
||||||
) { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.headerImageView.addSubview(self.visualEffectView)
|
|
||||||
self.visualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
self.visualEffectView.topAnchor.constraint(equalTo: self.headerImageView.topAnchor),
|
|
||||||
self.visualEffectView.leadingAnchor.constraint(equalTo: self.headerImageView.leadingAnchor),
|
|
||||||
self.visualEffectView.trailingAnchor.constraint(equalTo: self.headerImageView.trailingAnchor),
|
|
||||||
self.visualEffectView.bottomAnchor.constraint(equalTo: self.headerImageView.bottomAnchor)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
delegate?.configFollowButton(with: mastodonUser, followButton: followButton)
|
|
||||||
followButton.publisher(for: .touchUpInside)
|
|
||||||
.sink { [weak self] _ in
|
|
||||||
self?.followButtonDidPressed(mastodonUser: mastodonUser)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func followButtonDidPressed(mastodonUser: MastodonUser) {
|
|
||||||
delegate?.followButtonDidPressed(clickedUser: mastodonUser)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,16 +26,30 @@ extension SearchViewController: UserProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegate {
|
extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegate {
|
||||||
func followButtonDidPressed(clickedUser: MastodonUser) {
|
func searchRecommendAccountsCollectionViewCell(_ cell: SearchRecommendAccountsCollectionViewCell, followButtonDidPressed button: UIButton) {
|
||||||
|
guard let diffableDataSource = viewModel.accountDiffableDataSource else { return }
|
||||||
|
guard let indexPath = accountsCollectionView.indexPath(for: cell),
|
||||||
|
let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
|
|
||||||
|
context.managedObjectContext.performAndWait {
|
||||||
|
guard let user = try? context.managedObjectContext.existingObject(with: item) as? MastodonUser else { return }
|
||||||
|
self.toggleFriendship(for: user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleFriendship(for mastodonUser: MastodonUser) {
|
||||||
guard let currentMastodonUser = viewModel.currentMastodonUser.value else {
|
guard let currentMastodonUser = viewModel.currentMastodonUser.value else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let relationshipAction = relationShipActionSet(mastodonUser: clickedUser, currentMastodonUser: currentMastodonUser).highPriorityAction(except: .editOptions) else { return }
|
guard let relationshipAction = RecommendAccountSection.relationShipActionSet(
|
||||||
|
mastodonUser: mastodonUser,
|
||||||
|
currentMastodonUser: currentMastodonUser).highPriorityAction(except: .editOptions)
|
||||||
|
else { return }
|
||||||
switch relationshipAction {
|
switch relationshipAction {
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case .follow, .following:
|
case .follow, .following:
|
||||||
UserProviderFacade.toggleUserFollowRelationship(provider: self, mastodonUser: clickedUser)
|
UserProviderFacade.toggleUserFollowRelationship(provider: self, mastodonUser: mastodonUser)
|
||||||
.sink { _ in
|
.sink { _ in
|
||||||
// error handling
|
// error handling
|
||||||
} receiveValue: { _ in
|
} receiveValue: { _ in
|
||||||
|
@ -45,7 +59,7 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat
|
||||||
case .pending:
|
case .pending:
|
||||||
break
|
break
|
||||||
case .muting:
|
case .muting:
|
||||||
let name = clickedUser.displayNameWithFallback
|
let name = mastodonUser.displayNameWithFallback
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title,
|
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title,
|
||||||
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.message(name),
|
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.message(name),
|
||||||
|
@ -53,7 +67,7 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat
|
||||||
)
|
)
|
||||||
let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unmute, style: .default) { [weak self] _ in
|
let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unmute, style: .default) { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
UserProviderFacade.toggleUserMuteRelationship(provider: self, mastodonUser: clickedUser)
|
UserProviderFacade.toggleUserMuteRelationship(provider: self, mastodonUser: mastodonUser)
|
||||||
.sink { _ in
|
.sink { _ in
|
||||||
// do nothing
|
// do nothing
|
||||||
} receiveValue: { _ in
|
} receiveValue: { _ in
|
||||||
|
@ -66,7 +80,7 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat
|
||||||
alertController.addAction(cancelAction)
|
alertController.addAction(cancelAction)
|
||||||
present(alertController, animated: true, completion: nil)
|
present(alertController, animated: true, completion: nil)
|
||||||
case .blocking:
|
case .blocking:
|
||||||
let name = clickedUser.displayNameWithFallback
|
let name = mastodonUser.displayNameWithFallback
|
||||||
let alertController = UIAlertController(
|
let alertController = UIAlertController(
|
||||||
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.title,
|
title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.title,
|
||||||
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.message(name),
|
message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.message(name),
|
||||||
|
@ -74,7 +88,7 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat
|
||||||
)
|
)
|
||||||
let unblockAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unblock, style: .default) { [weak self] _ in
|
let unblockAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unblock, style: .default) { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
UserProviderFacade.toggleUserBlockRelationship(provider: self, mastodonUser: clickedUser)
|
UserProviderFacade.toggleUserBlockRelationship(provider: self, mastodonUser: mastodonUser)
|
||||||
.sink { _ in
|
.sink { _ in
|
||||||
// do nothing
|
// do nothing
|
||||||
} receiveValue: { _ in
|
} receiveValue: { _ in
|
||||||
|
@ -93,50 +107,4 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func configFollowButton(with mastodonUser: MastodonUser, followButton: HighlightDimmableButton) {
|
|
||||||
guard let currentMastodonUser = viewModel.currentMastodonUser.value else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_configFollowButton(with: mastodonUser, currentMastodonUser: currentMastodonUser, followButton: followButton)
|
|
||||||
ManagedObjectObserver.observe(object: currentMastodonUser)
|
|
||||||
.sink { _ in
|
|
||||||
|
|
||||||
} receiveValue: { change in
|
|
||||||
guard case .update(let object) = change.changeType,
|
|
||||||
let newUser = object as? MastodonUser else { return }
|
|
||||||
self._configFollowButton(with: mastodonUser, currentMastodonUser: newUser, followButton: followButton)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SearchViewController {
|
|
||||||
func _configFollowButton(with mastodonUser: MastodonUser, currentMastodonUser: MastodonUser, followButton: HighlightDimmableButton) {
|
|
||||||
let relationshipActionSet = relationShipActionSet(mastodonUser: mastodonUser, currentMastodonUser: currentMastodonUser)
|
|
||||||
followButton.setTitle(relationshipActionSet.title, for: .normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func relationShipActionSet(mastodonUser: MastodonUser, currentMastodonUser: MastodonUser) -> ProfileViewModel.RelationshipActionOptionSet {
|
|
||||||
var relationshipActionSet = ProfileViewModel.RelationshipActionOptionSet([.follow])
|
|
||||||
let isFollowing = mastodonUser.followingBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
|
||||||
if isFollowing {
|
|
||||||
relationshipActionSet.insert(.following)
|
|
||||||
}
|
|
||||||
|
|
||||||
let isPending = mastodonUser.followRequestedBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
|
||||||
if isPending {
|
|
||||||
relationshipActionSet.insert(.pending)
|
|
||||||
}
|
|
||||||
|
|
||||||
let isBlocking = mastodonUser.blockingBy.flatMap { $0.contains(currentMastodonUser) } ?? false
|
|
||||||
if isBlocking {
|
|
||||||
relationshipActionSet.insert(.blocking)
|
|
||||||
}
|
|
||||||
|
|
||||||
let isBlockedBy = currentMastodonUser.blockingBy.flatMap { $0.contains(mastodonUser) } ?? false
|
|
||||||
if isBlockedBy {
|
|
||||||
relationshipActionSet.insert(.blocked)
|
|
||||||
}
|
|
||||||
return relationshipActionSet
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
||||||
var searchTransitionController = SearchTransitionController()
|
var searchTransitionController = SearchTransitionController()
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
private(set) lazy var viewModel = SearchViewModel(context: context, coordinator: coordinator)
|
private(set) lazy var viewModel = SearchViewModel(context: context)
|
||||||
|
|
||||||
// recommend
|
// recommend
|
||||||
let scrollView: UIScrollView = {
|
let scrollView: UIScrollView = {
|
||||||
|
@ -167,7 +167,12 @@ extension SearchViewController {
|
||||||
|
|
||||||
private func setupDataSource() {
|
private func setupDataSource() {
|
||||||
viewModel.hashtagDiffableDataSource = RecommendHashTagSection.collectionViewDiffableDataSource(for: hashtagCollectionView)
|
viewModel.hashtagDiffableDataSource = RecommendHashTagSection.collectionViewDiffableDataSource(for: hashtagCollectionView)
|
||||||
viewModel.accountDiffableDataSource = RecommendAccountSection.collectionViewDiffableDataSource(for: accountsCollectionView, delegate: self, managedObjectContext: context.managedObjectContext)
|
viewModel.accountDiffableDataSource = RecommendAccountSection.collectionViewDiffableDataSource(
|
||||||
|
for: accountsCollectionView,
|
||||||
|
dependency: self,
|
||||||
|
delegate: self,
|
||||||
|
managedObjectContext: context.managedObjectContext
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,24 +19,25 @@ final class SearchViewModel: NSObject {
|
||||||
|
|
||||||
// input
|
// input
|
||||||
let context: AppContext
|
let context: AppContext
|
||||||
weak var coordinator: SceneCoordinator!
|
|
||||||
|
|
||||||
let currentMastodonUser = CurrentValueSubject<MastodonUser?, Never>(nil)
|
|
||||||
let viewDidAppeared = PassthroughSubject<Void, Never>()
|
let viewDidAppeared = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
// output
|
// output
|
||||||
|
let currentMastodonUser = CurrentValueSubject<MastodonUser?, Never>(nil)
|
||||||
|
|
||||||
// var recommendHashTags = [Mastodon.Entity.Tag]()
|
|
||||||
var recommendAccounts = [NSManagedObjectID]()
|
var recommendAccounts = [NSManagedObjectID]()
|
||||||
var recommendAccountsFallback = PassthroughSubject<Void, Never>()
|
var recommendAccountsFallback = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
var hashtagDiffableDataSource: UICollectionViewDiffableDataSource<RecommendHashTagSection, Mastodon.Entity.Tag>?
|
var hashtagDiffableDataSource: UICollectionViewDiffableDataSource<RecommendHashTagSection, Mastodon.Entity.Tag>?
|
||||||
var accountDiffableDataSource: UICollectionViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID>?
|
var accountDiffableDataSource: UICollectionViewDiffableDataSource<RecommendAccountSection, NSManagedObjectID>?
|
||||||
|
|
||||||
init(context: AppContext, coordinator: SceneCoordinator) {
|
init(context: AppContext) {
|
||||||
self.coordinator = coordinator
|
|
||||||
self.context = context
|
self.context = context
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
context.authenticationService.activeMastodonAuthentication
|
||||||
|
.map { $0?.user }
|
||||||
|
.assign(to: \.value, on: currentMastodonUser)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
Publishers.CombineLatest(
|
||||||
context.authenticationService.activeMastodonAuthenticationBox,
|
context.authenticationService.activeMastodonAuthenticationBox,
|
||||||
|
|
|
@ -64,18 +64,15 @@ extension SearchRecommendCollectionHeader {
|
||||||
])
|
])
|
||||||
|
|
||||||
let horizontalStackView = UIStackView()
|
let horizontalStackView = UIStackView()
|
||||||
|
horizontalStackView.spacing = 8
|
||||||
horizontalStackView.axis = .horizontal
|
horizontalStackView.axis = .horizontal
|
||||||
horizontalStackView.alignment = .center
|
horizontalStackView.alignment = .center
|
||||||
horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
horizontalStackView.distribution = .fill
|
horizontalStackView.distribution = .fill
|
||||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
titleLabel.setContentHuggingPriority(.defaultLow - 1, for: .horizontal)
|
titleLabel.setContentHuggingPriority(.defaultLow - 1, for: .horizontal)
|
||||||
horizontalStackView.addArrangedSubview(titleLabel)
|
horizontalStackView.addArrangedSubview(titleLabel)
|
||||||
seeAllButton.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
horizontalStackView.addArrangedSubview(seeAllButton)
|
horizontalStackView.addArrangedSubview(seeAllButton)
|
||||||
|
|
||||||
containerStackView.addArrangedSubview(horizontalStackView)
|
containerStackView.addArrangedSubview(horizontalStackView)
|
||||||
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
containerStackView.addArrangedSubview(descriptionLabel)
|
containerStackView.addArrangedSubview(descriptionLabel)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,6 +207,7 @@ extension MosaicImageViewContainer {
|
||||||
|
|
||||||
func setupImageViews(count: Int, maxSize: CGSize) -> [ConfigurableMosaic] {
|
func setupImageViews(count: Int, maxSize: CGSize) -> [ConfigurableMosaic] {
|
||||||
reset()
|
reset()
|
||||||
|
let count = min(4, max(0, count))
|
||||||
guard count > 1 else {
|
guard count > 1 else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,8 @@ extension ActionToolbarContainer {
|
||||||
button.expandEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
|
button.expandEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
|
||||||
button.setInsets(forContentPadding: .zero, imageTitlePadding: style.buttonTitleImagePadding)
|
button.setInsets(forContentPadding: .zero, imageTitlePadding: style.buttonTitleImagePadding)
|
||||||
}
|
}
|
||||||
|
// add more expand for menu button
|
||||||
|
moreButton.expandEdgeInsets = UIEdgeInsets(top: -10, left: -20, bottom: -10, right: -20)
|
||||||
|
|
||||||
let replyImage = UIImage(systemName: "arrowshape.turn.up.left.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .ultraLight))!.withRenderingMode(.alwaysTemplate)
|
let replyImage = UIImage(systemName: "arrowshape.turn.up.left.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .ultraLight))!.withRenderingMode(.alwaysTemplate)
|
||||||
let reblogImage = UIImage(systemName: "arrow.2.squarepath", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .bold))!.withRenderingMode(.alwaysTemplate)
|
let reblogImage = UIImage(systemName: "arrow.2.squarepath", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .bold))!.withRenderingMode(.alwaysTemplate)
|
||||||
|
|
|
@ -119,10 +119,10 @@ final class VideoPlayerViewModel {
|
||||||
case .unknown, .buffering, .readyToPlay:
|
case .unknown, .buffering, .readyToPlay:
|
||||||
break
|
break
|
||||||
case .playing:
|
case .playing:
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
|
try? AVAudioSession.sharedInstance().setCategory(.playback)
|
||||||
try? AVAudioSession.sharedInstance().setActive(true)
|
try? AVAudioSession.sharedInstance().setActive(true)
|
||||||
case .paused, .stopped, .failed:
|
case .paused, .stopped, .failed:
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.ambient)
|
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient) // reset to default
|
||||||
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,10 @@ final class AudioPlaybackService: NSObject {
|
||||||
case .unknown, .buffering, .readyToPlay:
|
case .unknown, .buffering, .readyToPlay:
|
||||||
break
|
break
|
||||||
case .playing:
|
case .playing:
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
|
try? AVAudioSession.sharedInstance().setCategory(.playback)
|
||||||
try? AVAudioSession.sharedInstance().setActive(true)
|
try? AVAudioSession.sharedInstance().setActive(true)
|
||||||
case .paused, .stopped, .failed:
|
case .paused, .stopped, .failed:
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.ambient)
|
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient) // reset to default
|
||||||
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@ extension VideoPlaybackService {
|
||||||
} else {
|
} else {
|
||||||
if latestPlayingVideoPlayerViewModel === playerViewModel {
|
if latestPlayingVideoPlayerViewModel === playerViewModel {
|
||||||
latestPlayingVideoPlayerViewModel = nil
|
latestPlayingVideoPlayerViewModel = nil
|
||||||
// try? AVAudioSession.sharedInstance().setCategory(.soloAmbient, mode: .default)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ The app is compatible with [toot-relay](https://github.com/DagAgren/toot-relay)
|
||||||
- [Nuke](https://github.com/kean/Nuke)
|
- [Nuke](https://github.com/kean/Nuke)
|
||||||
- [Pageboy](https://github.com/uias/Pageboy#the-basics)
|
- [Pageboy](https://github.com/uias/Pageboy#the-basics)
|
||||||
- [SDWebImage](https://github.com/SDWebImage/SDWebImage)
|
- [SDWebImage](https://github.com/SDWebImage/SDWebImage)
|
||||||
|
- [swift-collections](https://github.com/apple/swift-collections)
|
||||||
- [swift-nio](https://github.com/apple/swift-nio)
|
- [swift-nio](https://github.com/apple/swift-nio)
|
||||||
- [SwiftGen](https://github.com/SwiftGen/SwiftGen)
|
- [SwiftGen](https://github.com/SwiftGen/SwiftGen)
|
||||||
- [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect)
|
- [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect)
|
||||||
|
|
|
@ -27,6 +27,11 @@ files: [
|
||||||
"translation" : "/Localization/StringsConvertor/input/%locale_with_underscore%/%original_file_name%",
|
"translation" : "/Localization/StringsConvertor/input/%locale_with_underscore%/%original_file_name%",
|
||||||
"update_option" : "update_as_unapproved",
|
"update_option" : "update_as_unapproved",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source" : "/Localization/Localizable.stringsdict",
|
||||||
|
"translation" : "/Localization/StringsConvertor/input/%locale_with_underscore%/%original_file_name%",
|
||||||
|
"update_option" : "update_as_unapproved",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source" : "/MastodonIntent/en.lproj/Intents.strings",
|
"source" : "/MastodonIntent/en.lproj/Intents.strings",
|
||||||
"translation" : "/Localization/StringsConvertor/Intents/input/%locale_with_underscore%/%original_file_name%",
|
"translation" : "/Localization/StringsConvertor/Intents/input/%locale_with_underscore%/%original_file_name%",
|
||||||
|
|
Loading…
Reference in New Issue