Merge branch 'release/0.8.5' into main

This commit is contained in:
CMK 2021-07-06 18:38:19 +08:00
commit b7de9dd224
40 changed files with 775 additions and 603 deletions

View File

@ -129,14 +129,6 @@
"media_content_warning": "Tap anywhere to reveal",
"poll": {
"vote": "Vote",
"vote_count": {
"single": "%d vote",
"multiple": "%d votes"
},
"voter_count": {
"single": "%d voter",
"multiple": "%d voters"
},
"time_left": "%s left",
"closed": "Closed"
},
@ -200,12 +192,6 @@
"count_favorites": "%s favorites"
}
}
},
"countable": {
"photo": {
"single": "photo",
"multiple": "photos"
}
}
},
"scene": {
@ -386,8 +372,6 @@
"direct": "Only people I mention"
},
"auto_complete": {
"single_people_talking": "%ld people talking",
"multiple_people_talking": "%ld people talking",
"space_to_add": "Space to add"
},
"accessibility": {
@ -411,7 +395,6 @@
}
},
"profile": {
"subtitle": "%s posts",
"dashboard": {
"posts": "posts",
"following": "following",
@ -499,15 +482,7 @@
},
"thread": {
"back_title": "Post",
"title": "Post from %s",
"reblog": {
"single": "%s reblog",
"multiple": "%s reblogs"
},
"favorite": {
"single": "%s favorite",
"multiple": "%s favorites"
}
"title": "Post from %s"
},
"settings": {
"title": "Settings",
@ -548,6 +523,9 @@
"signout": "Sign Out"
}
},
"footer": {
"mastodon_description": "Mastodon is open source software. You can contribute or report issues on GitHub at %s (%s)"
},
"keyboard": {
"close_settings_window": "Close Settings Window"
}

View File

@ -55,7 +55,6 @@
2D34D9DB261494120081BFC0 /* APIService+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9DA261494120081BFC0 /* APIService+Search.swift */; };
2D34D9E226149C920081BFC0 /* SearchRecommendTagsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D34D9E126149C920081BFC0 /* SearchRecommendTagsCollectionViewCell.swift */; };
2D35237A26256D920031AF25 /* NotificationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D35237926256D920031AF25 /* NotificationSection.swift */; };
2D35238126256F690031AF25 /* NotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D35238026256F690031AF25 /* NotificationTableViewCell.swift */; };
2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */; };
2D364F7825E66D8300204FDC /* MastodonResendEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */; };
2D38F1C625CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */; };
@ -242,6 +241,8 @@
DB427DED25BAA00100D1B89D /* MastodonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DEC25BAA00100D1B89D /* MastodonTests.swift */; };
DB427DF825BAA00100D1B89D /* MastodonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB427DF725BAA00100D1B89D /* MastodonUITests.swift */; };
DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44384E25E8C1FA008912A2 /* CALayer.swift */; };
DB443CD22694326A00159B29 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DB443CD0269415D200159B29 /* Localizable.stringsdict */; };
DB443CD42694627B00159B29 /* AppearanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB443CD32694627B00159B29 /* AppearanceView.swift */; };
DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */; };
DB447681260B3ED600B66B82 /* CustomEmojiPickerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */; };
DB44768B260B3F2100B66B82 /* CustomEmojiPickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */; };
@ -665,7 +666,6 @@
2D34D9DA261494120081BFC0 /* APIService+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Search.swift"; sourceTree = "<group>"; };
2D34D9E126149C920081BFC0 /* SearchRecommendTagsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRecommendTagsCollectionViewCell.swift; sourceTree = "<group>"; };
2D35237926256D920031AF25 /* NotificationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSection.swift; sourceTree = "<group>"; };
2D35238026256F690031AF25 /* NotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTableViewCell.swift; sourceTree = "<group>"; };
2D364F7125E66D7500204FDC /* MastodonResendEmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewController.swift; sourceTree = "<group>"; };
2D364F7725E66D8300204FDC /* MastodonResendEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModel.swift; sourceTree = "<group>"; };
2D38F1C525CD37F400561493 /* ContentOffsetAdjustableTimelineViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentOffsetAdjustableTimelineViewControllerDelegate.swift; sourceTree = "<group>"; };
@ -875,6 +875,9 @@
DB427DF725BAA00100D1B89D /* MastodonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUITests.swift; sourceTree = "<group>"; };
DB427DF925BAA00100D1B89D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DB44384E25E8C1FA008912A2 /* CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayer.swift; sourceTree = "<group>"; };
DB443CCF269415D200159B29 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DB443CD1269415D800159B29 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DB443CD32694627B00159B29 /* AppearanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceView.swift; sourceTree = "<group>"; };
DB44767A260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerInputView.swift; sourceTree = "<group>"; };
DB447680260B3ED600B66B82 /* CustomEmojiPickerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerSection.swift; sourceTree = "<group>"; };
DB44768A260B3F2100B66B82 /* CustomEmojiPickerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEmojiPickerItem.swift; sourceTree = "<group>"; };
@ -1359,7 +1362,6 @@
2D35237F26256F470031AF25 /* TableViewCell */ = {
isa = PBXGroup;
children = (
2D35238026256F690031AF25 /* NotificationTableViewCell.swift */,
2D24E11C2626D8B100A59D4F /* NotificationStatusTableViewCell.swift */,
);
path = TableViewCell;
@ -1722,6 +1724,7 @@
children = (
5B90C458262599800002E742 /* Cell */,
5B90C45C262599800002E742 /* SettingsSectionHeader.swift */,
DB443CD32694627B00159B29 /* AppearanceView.swift */,
);
path = View;
sourceTree = "<group>";
@ -1831,6 +1834,7 @@
164F0EBB267D4FE400249499 /* BoopSound.caf */,
DB427DDE25BAA00100D1B89D /* Assets.xcassets */,
DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */,
DB443CD0269415D200159B29 /* Localizable.stringsdict */,
DB3D100F25BAA75E00EAA174 /* Localizable.strings */,
DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */,
);
@ -2933,6 +2937,7 @@
files = (
164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */,
DB427DE225BAA00100D1B89D /* LaunchScreen.storyboard in Resources */,
DB443CD22694326A00159B29 /* Localizable.stringsdict in Resources */,
DB3D100D25BAA75E00EAA174 /* Localizable.strings in Resources */,
DB427DDF25BAA00100D1B89D /* Assets.xcassets in Resources */,
DB427DDD25BAA00100D1B89D /* Main.storyboard in Resources */,
@ -3198,6 +3203,7 @@
2D571B2F26004EC000540450 /* NavigationBarProgressView.swift in Sources */,
DBD376AA2692EA4F007FEC24 /* Theme.swift in Sources */,
0FAA101225E105390017CCDE /* PrimaryActionButton.swift in Sources */,
DB443CD42694627B00159B29 /* AppearanceView.swift in Sources */,
DB8AF53025C13561002E6C99 /* AppContext.swift in Sources */,
DB92CF7225E7BB98002C1017 /* PollOptionTableViewCell.swift in Sources */,
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */,
@ -3273,7 +3279,6 @@
DBF9814A265E24F500E4BA07 /* ProfileFieldCollectionViewHeaderFooterView.swift in Sources */,
2D939AB525EDD8A90076FA61 /* String.swift in Sources */,
DB4481B925EE289600BEFB67 /* UITableView.swift in Sources */,
2D35238126256F690031AF25 /* NotificationTableViewCell.swift in Sources */,
DBE3CDBB261C427900430CC6 /* TimelineHeaderTableViewCell.swift in Sources */,
DBCBCBFF2680AE98000F5B51 /* AsyncHomeTimelineViewModel.swift in Sources */,
0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */,
@ -3749,6 +3754,16 @@
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
DB443CD0269415D200159B29 /* Localizable.stringsdict */ = {
isa = PBXVariantGroup;
children = (
DB443CCF269415D200159B29 /* en */,
DB443CD1269415D800159B29 /* ar */,
);
name = Localizable.stringsdict;
path = /Users/mainasuk/Developer/Mastodon/Mastodon/Resources;
sourceTree = "<absolute>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@ -3879,7 +3894,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist;
@ -3887,7 +3902,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3906,7 +3921,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist;
@ -3914,7 +3929,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -4234,7 +4249,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist;
@ -4242,7 +4257,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -4348,7 +4363,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -4356,7 +4371,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -4467,7 +4482,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = Mastodon/Info.plist;
@ -4475,7 +4490,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -4581,7 +4596,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -4589,7 +4604,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -4635,7 +4650,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -4643,7 +4658,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -4658,7 +4673,7 @@
buildSettings = {
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 29;
CURRENT_PROJECT_VERSION = 30;
DEVELOPMENT_TEAM = 5Z4GVSS33P;
INFOPLIST_FILE = NotificationService/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -4666,7 +4681,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 0.8.4;
MARKETING_VERSION = 0.8.5;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -4814,7 +4829,7 @@
repositoryURL = "https://github.com/TwidereProject/MetaTextView.git";
requirement = {
kind = exactVersion;
version = 1.2.4;
version = 1.2.5;
};
};
DB0E2D2C26833FF600865C3C /* XCRemoteSwiftPackageReference "Nuke-FLAnimatedImage-Plugin" */ = {

View File

@ -12,7 +12,7 @@
<key>CoreDataStack.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>21</integer>
<integer>20</integer>
</dict>
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
<dict>
@ -37,7 +37,7 @@
<key>NotificationService.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>20</integer>
<integer>21</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -114,8 +114,8 @@
"repositoryURL": "https://github.com/TwidereProject/MetaTextView.git",
"state": {
"branch": null,
"revision": "28e53130d16f12e0eeb479d83b77a0a718ef2088",
"version": "1.2.4"
"revision": "9ba4027ed0a88185ce95bb1773620c2ceaa9f3bb",
"version": "1.2.5"
}
},
{

View File

@ -55,11 +55,7 @@ extension AutoCompleteSection {
.prefix(2)
.compactMap { Int($0.accounts) }
.reduce(0, +)
if count > 1 {
return L10n.Scene.Compose.AutoComplete.multiplePeopleTalking(count)
} else {
return L10n.Scene.Compose.AutoComplete.singlePeopleTalking(count)
}
return L10n.Plural.peopleTalking(count)
}()
cell.avatarImageView.isHidden = true
}

View File

@ -46,10 +46,6 @@ extension NotificationSection {
),
into: cell.avatarImageView
)
cell.avatarImageView.gesture().sink { [weak cell] _ in
cell?.delegate?.userAvatarDidPressed(notification: notification)
}
.store(in: &cell.disposeBag)
cell.actionImageView.image = UIImage(
systemName: notification.notificationType.actionImageName,
withConfiguration: UIImage.SymbolConfiguration(

View File

@ -479,21 +479,13 @@ extension StatusSection {
cell.threadMetaView.dateLabel.accessibilityLabel = DateFormatter.localizedString(from: status.createdAt, dateStyle: .medium, timeStyle: .short)
let reblogCountTitle: String = {
let count = status.reblogsCount.intValue
if count > 1 {
return L10n.Scene.Thread.Reblog.multiple(String(count))
} else {
return L10n.Scene.Thread.Reblog.single(String(count))
}
return L10n.Plural.Count.reblog(count)
}()
cell.threadMetaView.reblogButton.setTitle(reblogCountTitle, for: .normal)
let favoriteCountTitle: String = {
let count = status.favouritesCount.intValue
if count > 1 {
return L10n.Scene.Thread.Favorite.multiple(String(count))
} else {
return L10n.Scene.Thread.Favorite.single(String(count))
}
return L10n.Plural.Count.favorite(count)
}()
cell.threadMetaView.favoriteButton.setTitle(favoriteCountTitle, for: .normal)
@ -832,18 +824,10 @@ extension StatusSection {
cell.statusView.pollVoteCountLabel.text = {
if poll.multiple {
let count = poll.votersCount?.intValue ?? 0
if count > 1 {
return L10n.Common.Controls.Status.Poll.VoterCount.single(count)
} else {
return L10n.Common.Controls.Status.Poll.VoterCount.multiple(count)
}
return L10n.Plural.Count.voter(count)
} else {
let count = poll.votesCount.intValue
if count > 1 {
return L10n.Common.Controls.Status.Poll.VoteCount.single(count)
} else {
return L10n.Common.Controls.Status.Poll.VoteCount.multiple(count)
}
return L10n.Plural.Count.vote(count)
}
}()
if poll.expired {

View File

@ -299,26 +299,6 @@ internal enum L10n {
}
/// Vote
internal static let vote = L10n.tr("Localizable", "Common.Controls.Status.Poll.Vote")
internal enum VoteCount {
/// %d votes
internal static func multiple(_ p1: Int) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.Poll.VoteCount.Multiple", p1)
}
/// %d vote
internal static func single(_ p1: Int) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.Poll.VoteCount.Single", p1)
}
}
internal enum VoterCount {
/// %d voters
internal static func multiple(_ p1: Int) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.Poll.VoterCount.Multiple", p1)
}
/// %d voter
internal static func single(_ p1: Int) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.Poll.VoterCount.Single", p1)
}
}
}
internal enum Tag {
/// Email
@ -400,14 +380,6 @@ internal enum L10n {
}
}
}
internal enum Countable {
internal enum Photo {
/// photos
internal static let multiple = L10n.tr("Localizable", "Common.Countable.Photo.Multiple")
/// photo
internal static let single = L10n.tr("Localizable", "Common.Countable.Photo.Single")
}
}
}
internal enum Scene {
@ -459,14 +431,6 @@ internal enum L10n {
internal static let video = L10n.tr("Localizable", "Scene.Compose.Attachment.Video")
}
internal enum AutoComplete {
/// %ld people talking
internal static func multiplePeopleTalking(_ p1: Int) -> String {
return L10n.tr("Localizable", "Scene.Compose.AutoComplete.MultiplePeopleTalking", p1)
}
/// %ld people talking
internal static func singlePeopleTalking(_ p1: Int) -> String {
return L10n.tr("Localizable", "Scene.Compose.AutoComplete.SinglePeopleTalking", p1)
}
/// Space to add
internal static let spaceToAdd = L10n.tr("Localizable", "Scene.Compose.AutoComplete.SpaceToAdd")
}
@ -634,10 +598,6 @@ internal enum L10n {
}
}
internal enum Profile {
/// %@ posts
internal static func subtitle(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Profile.Subtitle", String(describing: p1))
}
internal enum Dashboard {
/// followers
internal static let followers = L10n.tr("Localizable", "Scene.Profile.Dashboard.Followers")
@ -950,6 +910,12 @@ internal enum L10n {
internal enum Settings {
/// Settings
internal static let title = L10n.tr("Localizable", "Scene.Settings.Title")
internal enum Footer {
/// Mastodon is open source software. You can contribute or report issues on GitHub at %@ (%@)
internal static func mastodonDescription(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "Scene.Settings.Footer.MastodonDescription", String(describing: p1), String(describing: p2))
}
}
internal enum Keyboard {
/// Close Settings Window
internal static let closeSettingsWindow = L10n.tr("Localizable", "Scene.Settings.Keyboard.CloseSettingsWindow")
@ -1026,32 +992,43 @@ internal enum L10n {
internal static func title(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Thread.Title", String(describing: p1))
}
internal enum Favorite {
/// %@ favorites
internal static func multiple(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Thread.Favorite.Multiple", String(describing: p1))
}
/// %@ favorite
internal static func single(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Thread.Favorite.Single", String(describing: p1))
}
}
internal enum Reblog {
/// %@ reblogs
internal static func multiple(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Thread.Reblog.Multiple", String(describing: p1))
}
/// %@ reblog
internal static func single(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Thread.Reblog.Single", String(describing: p1))
}
}
}
internal enum Welcome {
/// Social networking\nback in your hands.
internal static let slogan = L10n.tr("Localizable", "Scene.Welcome.Slogan")
}
}
internal enum Plural {
/// Plural format key: "%#@count_people_talking@"
internal static func peopleTalking(_ p1: Int) -> String {
return L10n.tr("Localizable", "plural.people_talking", p1)
}
internal enum Count {
/// Plural format key: "%#@favorite_count@"
internal static func favorite(_ p1: Int) -> String {
return L10n.tr("Localizable", "plural.count.favorite", p1)
}
/// Plural format key: "%#@reblog_count@"
internal static func reblog(_ p1: Int) -> String {
return L10n.tr("Localizable", "plural.count.reblog", p1)
}
/// Plural format key: "%#@vote_count@"
internal static func vote(_ p1: Int) -> String {
return L10n.tr("Localizable", "plural.count.vote", p1)
}
/// Plural format key: "%#@voter_count@"
internal static func voter(_ p1: Int) -> String {
return L10n.tr("Localizable", "plural.count.voter", p1)
}
internal enum MetricFormatted {
/// Plural format key: "%@ %#@post_count@"
internal static func post(_ p1: Any, _ p2: Int) -> String {
return L10n.tr("Localizable", "plural.count.metric_formatted.post", String(describing: p1), p2)
}
}
}
}
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "mastodon.with.mail.svg",
"filename" : "c1 1.svg",
"idiom" : "universal"
}
],

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -101,10 +101,6 @@ Please check your internet connection.";
"Common.Controls.Status.Poll.Closed" = "Closed";
"Common.Controls.Status.Poll.TimeLeft" = "%@ left";
"Common.Controls.Status.Poll.Vote" = "Vote";
"Common.Controls.Status.Poll.VoteCount.Multiple" = "%d votes";
"Common.Controls.Status.Poll.VoteCount.Single" = "%d vote";
"Common.Controls.Status.Poll.VoterCount.Multiple" = "%d voters";
"Common.Controls.Status.Poll.VoterCount.Single" = "%d voter";
"Common.Controls.Status.ShowPost" = "Show Post";
"Common.Controls.Status.ShowUserProfile" = "Show user profile";
"Common.Controls.Status.Tag.Email" = "Email";
@ -140,8 +136,6 @@ Your account looks like this to them.";
"Common.Controls.Timeline.Loader.ShowMoreReplies" = "Show more replies";
"Common.Controls.Timeline.Timestamp.Now" = "Now";
"Common.Controls.Timeline.Timestamp.TimeAgo" = "%@ ago";
"Common.Countable.Photo.Multiple" = "photos";
"Common.Countable.Photo.Single" = "photo";
"Scene.Compose.Accessibility.AppendAttachment" = "Append attachment";
"Scene.Compose.Accessibility.AppendPoll" = "Append poll";
"Scene.Compose.Accessibility.CustomEmojiPicker" = "Custom emoji picker";
@ -157,8 +151,6 @@ uploaded to Mastodon.";
"Scene.Compose.Attachment.DescriptionVideo" = "Describe whats happening for low vision people...";
"Scene.Compose.Attachment.Photo" = "photo";
"Scene.Compose.Attachment.Video" = "video";
"Scene.Compose.AutoComplete.MultiplePeopleTalking" = "%ld people talking";
"Scene.Compose.AutoComplete.SinglePeopleTalking" = "%ld people talking";
"Scene.Compose.AutoComplete.SpaceToAdd" = "Space to add";
"Scene.Compose.ComposeAction" = "Publish";
"Scene.Compose.ContentInputPlaceholder" = "Type or paste whats on your mind";
@ -235,7 +227,6 @@ tap the link to confirm your account.";
"Scene.Profile.SegmentedControl.Media" = "Media";
"Scene.Profile.SegmentedControl.Posts" = "Posts";
"Scene.Profile.SegmentedControl.Replies" = "Replies";
"Scene.Profile.Subtitle" = "%@ posts";
"Scene.PublicTimeline.Title" = "Public";
"Scene.Register.Error.Item.Agreement" = "Agreement";
"Scene.Register.Error.Item.Email" = "Email";
@ -319,6 +310,7 @@ any server.";
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
"Scene.ServerRules.TermsOfService" = "terms of service";
"Scene.ServerRules.Title" = "Some ground rules.";
"Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can contribute or report issues on GitHub at %@ (%@)";
"Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window";
"Scene.Settings.Section.Appearance.Automatic" = "Automatic";
"Scene.Settings.Section.Appearance.Dark" = "Always Dark";
@ -345,10 +337,6 @@ any server.";
"Scene.SuggestionAccount.FollowExplain" = "When you follow someone, youll see their posts in your home feed.";
"Scene.SuggestionAccount.Title" = "Find People to Follow";
"Scene.Thread.BackTitle" = "Post";
"Scene.Thread.Favorite.Multiple" = "%@ favorites";
"Scene.Thread.Favorite.Single" = "%@ favorite";
"Scene.Thread.Reblog.Multiple" = "%@ reblogs";
"Scene.Thread.Reblog.Single" = "%@ reblog";
"Scene.Thread.Title" = "Post from %@";
"Scene.Welcome.Slogan" = "Social networking
back in your hands.";

View File

@ -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>

View File

@ -101,10 +101,6 @@ Please check your internet connection.";
"Common.Controls.Status.Poll.Closed" = "Closed";
"Common.Controls.Status.Poll.TimeLeft" = "%@ left";
"Common.Controls.Status.Poll.Vote" = "Vote";
"Common.Controls.Status.Poll.VoteCount.Multiple" = "%d votes";
"Common.Controls.Status.Poll.VoteCount.Single" = "%d vote";
"Common.Controls.Status.Poll.VoterCount.Multiple" = "%d voters";
"Common.Controls.Status.Poll.VoterCount.Single" = "%d voter";
"Common.Controls.Status.ShowPost" = "Show Post";
"Common.Controls.Status.ShowUserProfile" = "Show user profile";
"Common.Controls.Status.Tag.Email" = "Email";
@ -140,8 +136,6 @@ Your account looks like this to them.";
"Common.Controls.Timeline.Loader.ShowMoreReplies" = "Show more replies";
"Common.Controls.Timeline.Timestamp.Now" = "Now";
"Common.Controls.Timeline.Timestamp.TimeAgo" = "%@ ago";
"Common.Countable.Photo.Multiple" = "photos";
"Common.Countable.Photo.Single" = "photo";
"Scene.Compose.Accessibility.AppendAttachment" = "Append attachment";
"Scene.Compose.Accessibility.AppendPoll" = "Append poll";
"Scene.Compose.Accessibility.CustomEmojiPicker" = "Custom emoji picker";
@ -157,8 +151,6 @@ uploaded to Mastodon.";
"Scene.Compose.Attachment.DescriptionVideo" = "Describe whats happening for low vision people...";
"Scene.Compose.Attachment.Photo" = "photo";
"Scene.Compose.Attachment.Video" = "video";
"Scene.Compose.AutoComplete.MultiplePeopleTalking" = "%ld people talking";
"Scene.Compose.AutoComplete.SinglePeopleTalking" = "%ld people talking";
"Scene.Compose.AutoComplete.SpaceToAdd" = "Space to add";
"Scene.Compose.ComposeAction" = "Publish";
"Scene.Compose.ContentInputPlaceholder" = "Type or paste whats on your mind";
@ -235,7 +227,6 @@ tap the link to confirm your account.";
"Scene.Profile.SegmentedControl.Media" = "Media";
"Scene.Profile.SegmentedControl.Posts" = "Posts";
"Scene.Profile.SegmentedControl.Replies" = "Replies";
"Scene.Profile.Subtitle" = "%@ posts";
"Scene.PublicTimeline.Title" = "Public";
"Scene.Register.Error.Item.Agreement" = "Agreement";
"Scene.Register.Error.Item.Email" = "Email";
@ -319,6 +310,7 @@ any server.";
"Scene.ServerRules.Subtitle" = "These rules are set by the admins of %@.";
"Scene.ServerRules.TermsOfService" = "terms of service";
"Scene.ServerRules.Title" = "Some ground rules.";
"Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can contribute or report issues on GitHub at %@ (%@)";
"Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window";
"Scene.Settings.Section.Appearance.Automatic" = "Automatic";
"Scene.Settings.Section.Appearance.Dark" = "Always Dark";
@ -345,10 +337,6 @@ any server.";
"Scene.SuggestionAccount.FollowExplain" = "When you follow someone, youll see their posts in your home feed.";
"Scene.SuggestionAccount.Title" = "Find People to Follow";
"Scene.Thread.BackTitle" = "Post";
"Scene.Thread.Favorite.Multiple" = "%@ favorites";
"Scene.Thread.Favorite.Single" = "%@ favorite";
"Scene.Thread.Reblog.Multiple" = "%@ reblogs";
"Scene.Thread.Reblog.Single" = "%@ reblog";
"Scene.Thread.Title" = "Post from %@";
"Scene.Welcome.Slogan" = "Social networking
back in your hands.";

View File

@ -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>

View File

@ -144,13 +144,12 @@ extension ComposeViewController {
self.title = title
}
.store(in: &disposeBag)
view.backgroundColor = ThemeService.shared.currentTheme.value.systemElevatedBackgroundColor
self.setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.view.backgroundColor = theme.systemElevatedBackgroundColor
self.tableView.backgroundColor = theme.systemElevatedBackgroundColor
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
navigationItem.leftBarButtonItem = cancelBarButtonItem
@ -607,6 +606,11 @@ extension ComposeViewController {
// })
}
private func setupBackgroundColor(theme: Theme) {
view.backgroundColor = theme.systemElevatedBackgroundColor
tableView.backgroundColor = theme.systemElevatedBackgroundColor
}
}
extension ComposeViewController {
@ -809,7 +813,11 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
}
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, pollButtonDidPressed sender: UIButton) {
// toggle poll composing state
viewModel.isPollComposing.value.toggle()
// cancel custom picker input
viewModel.isCustomEmojiComposing.value = false
// setup initial poll option if needs
if viewModel.isPollComposing.value, viewModel.pollOptionAttributes.value.isEmpty {
@ -831,6 +839,9 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
}
func composeToolbarView(_ composeToolbarView: ComposeToolbarView, contentWarningButtonDidPressed sender: UIButton) {
// cancel custom picker input
viewModel.isCustomEmojiComposing.value = false
// restore first responder for text editor when content warning dismiss
if viewModel.isContentWarningComposing.value {
if contentWarningEditorTextView()?.isFirstResponder == true {
@ -860,20 +871,45 @@ extension ComposeViewController {
let repliedToCellFrame = viewModel.repliedToCellFrame.value
guard repliedToCellFrame != .zero else { return }
let throttle = viewModel.repliedToCellFrame.value.height - scrollView.adjustedContentInset.top
// print("\(throttle) - \(scrollView.contentOffset.y)")
// try to find some patterns:
// print("""
// repliedToCellFrame: \(viewModel.repliedToCellFrame.value.height)
// scrollView.contentOffset.y: \(scrollView.contentOffset.y)
// scrollView.contentSize.height: \(scrollView.contentSize.height)
// scrollView.frame: \(scrollView.frame)
// scrollView.adjustedContentInset.top: \(scrollView.adjustedContentInset.top)
// scrollView.adjustedContentInset.bottom: \(scrollView.adjustedContentInset.bottom)
// """)
switch viewModel.collectionViewState.value {
case .fold:
if scrollView.contentOffset.y < throttle {
os_log("%{public}s[%{public}ld], %{public}s: fold", ((#file as NSString).lastPathComponent), #line, #function)
guard velocity.y < 0 else { return }
let offsetY = scrollView.contentOffset.y + scrollView.adjustedContentInset.top
if offsetY < -44 {
tableView.contentInset.top = 0
targetContentOffset.pointee = CGPoint(x: 0, y: -scrollView.adjustedContentInset.top)
viewModel.collectionViewState.value = .expand
}
os_log("%{public}s[%{public}ld], %{public}s: fold", ((#file as NSString).lastPathComponent), #line, #function)
case .expand:
if scrollView.contentOffset.y > -44 {
os_log("%{public}s[%{public}ld], %{public}s: expand", ((#file as NSString).lastPathComponent), #line, #function)
guard velocity.y > 0 else { return }
// check if top across
let topOffset = (scrollView.contentOffset.y + scrollView.adjustedContentInset.top) - repliedToCellFrame.height
// check if bottom bounce
let bottomOffsetY = scrollView.contentOffset.y + (scrollView.frame.height - scrollView.adjustedContentInset.bottom)
let bottomOffset = bottomOffsetY - scrollView.contentSize.height
if topOffset > 44 {
// do not interrupt user scrolling
viewModel.collectionViewState.value = .fold
} else if bottomOffset > 44 {
tableView.contentInset.top = -repliedToCellFrame.height
targetContentOffset.pointee = CGPoint(x: 0, y: -repliedToCellFrame.height)
viewModel.collectionViewState.value = .fold
os_log("%{public}s[%{public}ld], %{public}s: expand", ((#file as NSString).lastPathComponent), #line, #function)
}
}
}
@ -910,7 +946,8 @@ extension ComposeViewController: UICollectionViewDelegate {
extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return traitCollection.userInterfaceIdiom == .pad ? .formSheet : .automatic
return .fullScreen
//return traitCollection.userInterfaceIdiom == .pad ? .formSheet : .automatic
}
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {

View File

@ -253,6 +253,8 @@ extension ComposeViewModel: UITableViewDataSource {
cell.statusContentWarningEditorView.alpha = 0
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) {
cell.statusContentWarningEditorView.alpha = 1
tableView.beginUpdates()
tableView.endUpdates()
} completion: { _ in
// do nothing
}

View File

@ -29,7 +29,7 @@ final class ComposeViewModel: NSObject {
let selectedStatusVisibility: CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never>
let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make intial event emit
let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make initial event emit
let repliedToCellFrame = CurrentValueSubject<CGRect, Never>(.zero)
let autoCompleteRetryLayoutTimes = CurrentValueSubject<Int, Never>(0)
let autoCompleteInfo = CurrentValueSubject<ComposeViewController.AutoCompleteInfo?, Never>(nil)

View File

@ -13,7 +13,7 @@ final class StatusContentWarningEditorView: UIView {
// default hidden
let containerBackgroundView: UIView = {
let view = UIView()
view.backgroundColor = Asset.Colors.Background.secondarySystemBackground.color
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
return view
}()
@ -57,29 +57,43 @@ extension StatusContentWarningEditorView {
containerBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 1024),
containerBackgroundView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
iconImageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(iconImageView)
let containerStackView = UIStackView()
containerStackView.axis = .horizontal
containerStackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(containerStackView)
NSLayoutConstraint.activate([
iconImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
iconImageView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
iconImageView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.defaultHigh), // center alignment to avatar
])
iconImageView.setContentHuggingPriority(.required - 2, for: .horizontal)
textView.translatesAutoresizingMaskIntoConstraints = false
addSubview(textView)
NSLayoutConstraint.activate([
textView.centerYAnchor.constraint(equalTo: centerYAnchor),
textView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 6).priority(.required - 1),
textView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: StatusView.avatarToLabelSpacing - 4), // align to name label. minus magic 4pt to remove addition inset
textView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
bottomAnchor.constraint(greaterThanOrEqualTo: textView.bottomAnchor, constant: 6).priority(.required - 1),
//textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh),
containerStackView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
containerStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
containerStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 6),
])
textView.setContentHuggingPriority(.required - 1, for: .vertical)
textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
containerStackView.addArrangedSubview(iconImageView)
iconImageView.setContentHuggingPriority(.required - 1, for: .horizontal)
containerStackView.addArrangedSubview(textView)
// iconImageView.translatesAutoresizingMaskIntoConstraints = false
// addSubview(iconImageView)
// NSLayoutConstraint.activate([
// iconImageView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
// iconImageView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.defaultHigh), // center alignment to avatar
// ])
// iconImageView.setContentHuggingPriority(.required - 2, for: .horizontal)
//
// textView.translatesAutoresizingMaskIntoConstraints = false
// addSubview(textView)
// NSLayoutConstraint.activate([
// textView.centerYAnchor.constraint(equalTo: centerYAnchor),
// textView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
// textView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: StatusView.avatarToLabelSpacing - 4), // align to name label. minus magic 4pt to remove addition inset
// textView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
// bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: 6),
// textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh),
// ])
//
// textView.setContentHuggingPriority(.required - 1, for: .vertical)
// textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
}
}

View File

@ -58,7 +58,8 @@ extension HashtagTimelineViewController {
title = "#\(viewModel.hashtag)"
titleView.update(title: viewModel.hashtag, subtitle: nil, emojiDict: [:])
navigationItem.titleView = titleView
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in

View File

@ -31,7 +31,11 @@ extension HomeTimelineViewController {
guard let self = self else { return }
self.showWelcomeAction(action)
},
UIAction(title: "Show Or Remove EmptyView", image: UIImage(systemName: "clear"), attributes: []) { [weak self] action in
UIAction(title: "Show Confirm Email", image: UIImage(systemName: "envelope"), attributes: []) { [weak self] action in
guard let self = self else { return }
self.showConfirmEmail(action)
},
UIAction(title: "Toggle EmptyView", image: UIImage(systemName: "clear"), attributes: []) { [weak self] action in
guard let self = self else { return }
if self.emptyView.superview != nil {
self.emptyView.removeFromSuperview()
@ -310,6 +314,11 @@ extension HomeTimelineViewController {
@objc private func showWelcomeAction(_ sender: UIAction) {
coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
}
@objc private func showConfirmEmail(_ sender: UIAction) {
let mastodonConfirmEmailViewModel = MastodonConfirmEmailViewModel()
coordinator.present(scene: .mastodonConfirmEmail(viewModel: mastodonConfirmEmailViewModel), from: nil, transition: .modal(animated: true, completion: nil))
}
@objc private func showPublicTimelineAction(_ sender: UIAction) {
coordinator.present(scene: .publicTimeline, from: self, transition: .show)

View File

@ -80,8 +80,8 @@ extension HomeTimelineViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = L10n.Scene.HomeTimeline.title
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in

View File

@ -12,6 +12,7 @@ import GameplayKit
import MastodonSDK
import OSLog
import UIKit
import ActiveLabel
import Meta
import MetaTextView
@ -47,7 +48,8 @@ final class NotificationViewController: UIViewController, NeedsDependency {
extension NotificationViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
@ -277,6 +279,35 @@ extension NotificationViewController: ContentOffsetAdjustableTimelineViewControl
// MARK: - NotificationTableViewCellDelegate
extension NotificationViewController: NotificationTableViewCellDelegate {
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, avatarImageViewDidPressed imageView: UIImageView) {
guard let diffableDataSource = viewModel.diffableDataSource else { return }
guard let indexPath = tableView.indexPath(for: cell) else { return }
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
switch item {
case .notification(let objectID, _):
guard let notification = try? viewModel.fetchedResultsController.managedObjectContext.existingObject(with: objectID) as? MastodonNotification else { return }
let viewModel = ProfileViewModel(context: context, optionalMastodonUser: notification.account)
coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show)
default:
break
}
}
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, authorNameLabelDidPressed label: ActiveLabel) {
guard let diffableDataSource = viewModel.diffableDataSource else { return }
guard let indexPath = tableView.indexPath(for: cell) else { return }
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
switch item {
case .notification(let objectID, _):
guard let notification = try? viewModel.fetchedResultsController.managedObjectContext.existingObject(with: objectID) as? MastodonNotification else { return }
let viewModel = ProfileViewModel(context: context, optionalMastodonUser: notification.account)
coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show)
default:
break
}
}
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) {
viewModel.acceptFollowRequest(notification: notification)
}
@ -284,13 +315,6 @@ extension NotificationViewController: NotificationTableViewCellDelegate {
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) {
viewModel.rejectFollowRequest(notification: notification)
}
func userAvatarDidPressed(notification: MastodonNotification) {
let viewModel = ProfileViewModel(context: context, optionalMastodonUser: notification.account)
DispatchQueue.main.async {
self.coordinator.present(scene: .profile(viewModel: viewModel), from: self, transition: .show)
}
}
func userNameLabelDidPressed(notification: MastodonNotification) {
let viewModel = CachedProfileViewModel(context: context, mastodonUser: notification.account)

View File

@ -5,8 +5,10 @@
// Created by sxiaojian on 2021/4/14.
//
import os.log
import Combine
import Foundation
import CoreDataStack
import UIKit
import ActiveLabel
import MetaTextView
@ -14,6 +16,23 @@ import Meta
import FLAnimatedImage
import Nuke
protocol NotificationTableViewCellDelegate: AnyObject {
var context: AppContext! { get }
func parent() -> UIViewController
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, avatarImageViewDidPressed imageView: UIImageView)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, authorNameLabelDidPressed label: ActiveLabel)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, revealContentWarningButtonDidPressed button: UIButton)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta)
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton)
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton)
}
final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
static let actionImageBorderWidth: CGFloat = 2
@ -219,13 +238,12 @@ extension NotificationStatusTableViewCell {
statusView.bottomAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.bottomAnchor),
])
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in
return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor
})
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
// remove item don't display
@ -246,7 +264,14 @@ extension NotificationStatusTableViewCell {
])
statusView.delegate = self
let avatarImageViewTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
avatarImageViewTapGestureRecognizer.addTarget(self, action: #selector(NotificationStatusTableViewCell.avatarImageViewTapGestureRecognizerHandler(_:)))
avatarImageView.addGestureRecognizer(avatarImageViewTapGestureRecognizer)
let authorNameLabelTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
authorNameLabelTapGestureRecognizer.addTarget(self, action: #selector(NotificationStatusTableViewCell.authorNameLabelTapGestureRecognizerHandler(_:)))
nameLabel.addGestureRecognizer(authorNameLabelTapGestureRecognizer)
resetSeparatorLineLayout()
}
@ -260,6 +285,28 @@ extension NotificationStatusTableViewCell {
}
extension NotificationStatusTableViewCell {
private func setupBackgroundColor(theme: Theme) {
statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in
return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor
})
}
}
extension NotificationStatusTableViewCell {
@objc private func avatarImageViewTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
delegate?.notificationStatusTableViewCell(self, avatarImageViewDidPressed: avatarImageView)
}
@objc private func authorNameLabelTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
delegate?.notificationStatusTableViewCell(self, authorNameLabelDidPressed: nameLabel)
}
}
// MARK: - StatusViewDelegate
extension NotificationStatusTableViewCell: StatusViewDelegate {

View File

@ -1,265 +0,0 @@
//
// NotificationTableViewCell.swift
// Mastodon
//
// Created by sxiaojian on 2021/4/13.
//
import Combine
import CoreDataStack
import Foundation
import UIKit
import Meta
import MetaTextView
import ActiveLabel
import FLAnimatedImage
import Nuke
protocol NotificationTableViewCellDelegate: AnyObject {
var context: AppContext! { get }
func parent() -> UIViewController
func userAvatarDidPressed(notification: MastodonNotification)
func userNameLabelDidPressed(notification: MastodonNotification)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, revealContentWarningButtonDidPressed button: UIButton)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
func notificationStatusTableViewCell(_ cell: NotificationStatusTableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta)
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton)
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton)
}
//final class NotificationTableViewCell: UITableViewCell {
// static let actionImageBorderWidth: CGFloat = 2
//
// var disposeBag = Set<AnyCancellable>()
//
// var delegate: NotificationTableViewCellDelegate?
//
// var avatarImageViewTask: ImageTask?
// let avatarImageView: UIImageView = {
// let imageView = FLAnimatedImageView()
// imageView.layer.cornerRadius = 4
// imageView.layer.cornerCurve = .continuous
// imageView.clipsToBounds = true
// return imageView
// }()
//
// let actionImageView: UIImageView = {
// let imageView = UIImageView()
// imageView.tintColor = Asset.Colors.Background.systemBackground.color
// return imageView
// }()
//
// let actionImageBackground: UIView = {
// let view = UIView()
// view.layer.cornerRadius = (24 + NotificationTableViewCell.actionImageBorderWidth) / 2
// view.layer.cornerCurve = .continuous
// view.clipsToBounds = true
// view.layer.borderWidth = NotificationTableViewCell.actionImageBorderWidth
// view.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor
// view.tintColor = Asset.Colors.Background.systemBackground.color
// return view
// }()
//
// let avatarContainer: UIView = {
// let view = UIView()
// return view
// }()
//
// let actionLabel: UILabel = {
// let label = UILabel()
// label.textColor = Asset.Colors.Label.secondary.color
// label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 15, weight: .regular), maximumPointSize: 20)
// label.lineBreakMode = .byTruncatingTail
// return label
// }()
//
// let nameLabel: ActiveLabel = {
// let label = ActiveLabel()
// label.textColor = Asset.Colors.brandBlue.color
// label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20)
// label.lineBreakMode = .byTruncatingTail
// return label
// }()
//
// let acceptButton: UIButton = {
// let button = UIButton(type: .custom)
// let actionImage = UIImage(systemName: "checkmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate)
// button.setImage(actionImage, for: .normal)
// button.tintColor = Asset.Colors.Label.secondary.color
// return button
// }()
//
// let rejectButton: UIButton = {
// let button = UIButton(type: .custom)
// let actionImage = UIImage(systemName: "xmark.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold))?.withRenderingMode(.alwaysTemplate)
// button.setImage(actionImage, for: .normal)
// button.tintColor = Asset.Colors.Label.secondary.color
// return button
// }()
//
// let buttonStackView = UIStackView()
//
// let separatorLine = UIView.separatorLine
//
// var separatorLineToEdgeLeadingLayoutConstraint: NSLayoutConstraint!
// var separatorLineToEdgeTrailingLayoutConstraint: NSLayoutConstraint!
//
// var separatorLineToMarginLeadingLayoutConstraint: NSLayoutConstraint!
// var separatorLineToMarginTrailingLayoutConstraint: NSLayoutConstraint!
//
// override func prepareForReuse() {
// super.prepareForReuse()
// avatarImageViewTask?.cancel()
// avatarImageViewTask = nil
// disposeBag.removeAll()
// }
//
// override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
// super.init(style: style, reuseIdentifier: reuseIdentifier)
// configure()
// }
//
// required init?(coder: NSCoder) {
// super.init(coder: coder)
// configure()
// }
//}
//
//extension NotificationTableViewCell {
// func configure() {
// backgroundColor = Asset.Colors.Background.systemBackground.color
// selectedBackgroundView = {
// let view = UIView()
// view.backgroundColor = Asset.Colors.Background.Cell.highlight.color
// return view
// }()
//
// let containerStackView = UIStackView()
// containerStackView.axis = .vertical
// containerStackView.alignment = .fill
// containerStackView.layoutMargins = UIEdgeInsets(top: 14, left: 0, bottom: 12, right: 0)
// containerStackView.isLayoutMarginsRelativeArrangement = true
// containerStackView.translatesAutoresizingMaskIntoConstraints = false
// contentView.addSubview(containerStackView)
// NSLayoutConstraint.activate([
// containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
// containerStackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
// contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor),
// contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor),
// ])
//
// let horizontalStackView = UIStackView()
// horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
// horizontalStackView.axis = .horizontal
// horizontalStackView.spacing = 6
//
// horizontalStackView.addArrangedSubview(avatarContainer)
// avatarContainer.translatesAutoresizingMaskIntoConstraints = false
// NSLayoutConstraint.activate([
// avatarContainer.heightAnchor.constraint(equalToConstant: 47).priority(.required - 1),
// avatarContainer.widthAnchor.constraint(equalToConstant: 47).priority(.required - 1)
// ])
//
// avatarContainer.addSubview(avatarImageView)
// avatarImageView.translatesAutoresizingMaskIntoConstraints = false
// NSLayoutConstraint.activate([
// avatarImageView.heightAnchor.constraint(equalToConstant: 35).priority(.required - 1),
// avatarImageView.widthAnchor.constraint(equalToConstant: 35).priority(.required - 1),
// avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor),
// avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor)
// ])
//
// avatarContainer.addSubview(actionImageBackground)
// actionImageBackground.translatesAutoresizingMaskIntoConstraints = false
// NSLayoutConstraint.activate([
// actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1),
// actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationTableViewCell.actionImageBorderWidth).priority(.required - 1),
// actionImageBackground.bottomAnchor.constraint(equalTo: avatarContainer.bottomAnchor),
// actionImageBackground.trailingAnchor.constraint(equalTo: avatarContainer.trailingAnchor)
// ])
//
// avatarContainer.addSubview(actionImageView)
// actionImageView.translatesAutoresizingMaskIntoConstraints = false
// NSLayoutConstraint.activate([
// actionImageView.centerXAnchor.constraint(equalTo: actionImageBackground.centerXAnchor),
// actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor)
// ])
//
// nameLabel.translatesAutoresizingMaskIntoConstraints = false
// horizontalStackView.addArrangedSubview(nameLabel)
// actionLabel.translatesAutoresizingMaskIntoConstraints = false
// horizontalStackView.addArrangedSubview(actionLabel)
// nameLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
// nameLabel.setContentHuggingPriority(.required - 1, for: .horizontal)
// actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
//
// containerStackView.addArrangedSubview(horizontalStackView)
//
// buttonStackView.translatesAutoresizingMaskIntoConstraints = false
// buttonStackView.axis = .horizontal
// buttonStackView.distribution = .fillEqually
// acceptButton.translatesAutoresizingMaskIntoConstraints = false
// rejectButton.translatesAutoresizingMaskIntoConstraints = false
// buttonStackView.addArrangedSubview(acceptButton)
// buttonStackView.addArrangedSubview(rejectButton)
// containerStackView.addArrangedSubview(buttonStackView)
//
// separatorLine.translatesAutoresizingMaskIntoConstraints = false
// contentView.addSubview(separatorLine)
// separatorLineToEdgeLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
// separatorLineToEdgeTrailingLayoutConstraint = separatorLine.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
// separatorLineToMarginLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor)
// separatorLineToMarginTrailingLayoutConstraint = separatorLine.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor)
// NSLayoutConstraint.activate([
// separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
// separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)),
// ])
// resetSeparatorLineLayout()
// }
//
// override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
// super.traitCollectionDidChange(previousTraitCollection)
//
// actionImageBackground.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor
// resetSeparatorLineLayout()
// }
//}
//
//extension NotificationTableViewCell {
//
// private func resetSeparatorLineLayout() {
// separatorLineToEdgeLeadingLayoutConstraint.isActive = false
// separatorLineToEdgeTrailingLayoutConstraint.isActive = false
// separatorLineToMarginLeadingLayoutConstraint.isActive = false
// separatorLineToMarginTrailingLayoutConstraint.isActive = false
//
// if traitCollection.userInterfaceIdiom == .phone {
// // to edge
// NSLayoutConstraint.activate([
// separatorLineToEdgeLeadingLayoutConstraint,
// separatorLineToEdgeTrailingLayoutConstraint,
// ])
// } else {
// if traitCollection.horizontalSizeClass == .compact {
// // to edge
// NSLayoutConstraint.activate([
// separatorLineToEdgeLeadingLayoutConstraint,
// separatorLineToEdgeTrailingLayoutConstraint,
// ])
// } else {
// // to margin
// NSLayoutConstraint.activate([
// separatorLineToMarginLeadingLayoutConstraint,
// separatorLineToMarginTrailingLayoutConstraint,
// ])
// }
// }
// }
//
//}

View File

@ -37,4 +37,17 @@ final class MastodonConfirmEmailViewModel {
self.userToken = userToken
self.updateCredentialQuery = updateCredentialQuery
}
#if DEBUG
init() {
self.context = AppContext.shared
self.email = "example.com"
self.authenticateInfo = AuthenticationViewModel.AuthenticateInfo(
domain: "",
application: Mastodon.Entity.Application(name: "", website: nil, vapidKey: nil, redirectURI: nil, clientID: "clientID", clientSecret: "clientSecret")
)!
self.userToken = Mastodon.Entity.Token(accessToken: "", tokenType: "", scope: "", createdAt: Date())
self.updateCredentialQuery = Mastodon.API.Account.UpdateCredentialQuery(discoverable: nil, bot: nil, displayName: nil, note: nil, avatar: nil, header: nil, locked: nil, source: nil, fieldsAttributes: nil)
}
#endif
}

View File

@ -88,6 +88,7 @@ extension ProfileHeaderViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
@ -222,13 +223,18 @@ extension ProfileHeaderViewController {
}
.store(in: &disposeBag)
Publishers.CombineLatest(
Publishers.CombineLatest3(
viewModel.isEditing,
viewModel.displayProfileInfo.fields
viewModel.displayProfileInfo.fields,
viewModel.needsFiledCollectionViewHidden
)
.receive(on: RunLoop.main)
.sink { [weak self] isEditing, fields in
.sink { [weak self] isEditing, fields, needsHidden in
guard let self = self else { return }
guard !needsHidden else {
self.profileHeaderView.fieldCollectionView.isHidden = true
return
}
self.profileHeaderView.fieldCollectionView.isHidden = isEditing ? false : fields.isEmpty
}
.store(in: &disposeBag)

View File

@ -22,6 +22,7 @@ final class ProfileHeaderViewModel {
let isEditing = CurrentValueSubject<Bool, Never>(false)
let viewDidAppear = CurrentValueSubject<Bool, Never>(false)
let needsSetupBottomShadow = CurrentValueSubject<Bool, Never>(true)
let needsFiledCollectionViewHidden = CurrentValueSubject<Bool, Never>(false)
let isTitleViewContentOffsetSet = CurrentValueSubject<Bool, Never>(false)
let emojiDict = CurrentValueSubject<MastodonStatusContent.EmojiDict, Never>([:])
let accountForEdit = CurrentValueSubject<Mastodon.Entity.Account?, Never>(nil)

View File

@ -218,7 +218,7 @@ final class ProfileHeaderView: UIView {
collectionView.isScrollEnabled = false
return collectionView
}()
var fieldCollectionViewHeightLaoutConstraint: NSLayoutConstraint!
var fieldCollectionViewHeightLayoutConstraint: NSLayoutConstraint!
var fieldCollectionViewHeightObservation: NSKeyValueObservation?
override init(frame: CGRect) {
@ -239,6 +239,8 @@ final class ProfileHeaderView: UIView {
extension ProfileHeaderView {
private func _init() {
backgroundColor = ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
fieldCollectionView.backgroundColor = ThemeService.shared.currentTheme.value.profileFieldCollectionViewBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
@ -427,17 +429,17 @@ extension ProfileHeaderView {
fieldCollectionView.translatesAutoresizingMaskIntoConstraints = false
metaContainerStackView.addArrangedSubview(fieldCollectionView)
fieldCollectionViewHeightLaoutConstraint = fieldCollectionView.heightAnchor.constraint(equalToConstant: 44).priority(.defaultHigh)
fieldCollectionViewHeightLayoutConstraint = fieldCollectionView.heightAnchor.constraint(equalToConstant: 44).priority(.defaultHigh)
NSLayoutConstraint.activate([
fieldCollectionViewHeightLaoutConstraint,
fieldCollectionViewHeightLayoutConstraint,
])
fieldCollectionViewHeightObservation = fieldCollectionView.observe(\.contentSize, options: .new, changeHandler: { [weak self] tableView, _ in
guard let self = self else { return }
guard self.fieldCollectionView.contentSize.height != .zero else {
self.fieldCollectionViewHeightLaoutConstraint.constant = 44
self.fieldCollectionViewHeightLayoutConstraint.constant = 44
return
}
self.fieldCollectionViewHeightLaoutConstraint.constant = self.fieldCollectionView.contentSize.height
self.fieldCollectionViewHeightLayoutConstraint.constant = self.fieldCollectionView.contentSize.height
})
bringSubviewToFront(bannerContainerView)

View File

@ -141,7 +141,8 @@ extension ProfileViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
@ -322,7 +323,7 @@ extension ProfileViewController {
self.titleView.isHidden = true
return
}
let subtitle = L10n.Scene.Profile.subtitle(formattedStatusCount)
let subtitle = L10n.Plural.Count.MetricFormatted.post(formattedStatusCount, statusesCount)
self.titleView.update(title: title, subtitle: subtitle, emojiDict: emojiDict)
self.titleView.isHidden = false
}
@ -495,7 +496,8 @@ extension ProfileViewController {
let isNeedSetHidden = isBlocking || isBlockedBy || suspended
self.profileHeaderViewController.viewModel.needsSetupBottomShadow.value = !isNeedSetHidden
self.profileHeaderViewController.profileHeaderView.bioContainerView.isHidden = isNeedSetHidden
self.profileHeaderViewController.pageSegmentedControl.isHidden = isNeedSetHidden
self.profileHeaderViewController.viewModel.needsFiledCollectionViewHidden.value = isNeedSetHidden
self.profileHeaderViewController.pageSegmentedControl.isEnabled = !isNeedSetHidden
self.viewModel.needsPagePinToTop.value = isNeedSetHidden
}
.store(in: &disposeBag)
@ -530,7 +532,7 @@ extension ProfileViewController {
self.profileHeaderViewController.profileHeaderView.statusDashboardView.followersDashboardMeterView.accessibilityLabel = L10n.Scene.Profile.Dashboard.Accessibility.countFollowers(count ?? 0)
}
.store(in: &disposeBag)
viewModel.needsPaingEnabled
viewModel.needsPagingEnabled
.receive(on: RunLoop.main)
.sink { [weak self] needsPaingEnabled in
guard let self = self else { return }

View File

@ -63,7 +63,7 @@ class ProfileViewModel: NSObject {
let isMeBarButtonItemsHidden = CurrentValueSubject<Bool, Never>(true)
let needsPagePinToTop = CurrentValueSubject<Bool, Never>(false)
let needsPaingEnabled = CurrentValueSubject<Bool, Never>(true)
let needsPagingEnabled = CurrentValueSubject<Bool, Never>(true)
let needsImageOverlayBlurred = CurrentValueSubject<Bool, Never>(false)
init(context: AppContext, optionalMastodonUser mastodonUser: MastodonUser?) {
@ -161,7 +161,7 @@ class ProfileViewModel: NSObject {
isBlockingOrBlocked
.map { !$0 }
.assign(to: \.value, on: needsPaingEnabled)
.assign(to: \.value, on: needsPagingEnabled)
.store(in: &disposeBag)
isBlockingOrBlocked

View File

@ -135,14 +135,12 @@ extension SearchViewController {
navigationItem.compactAppearance = barAppearance
navigationItem.scrollEdgeAppearance = barAppearance
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.view.backgroundColor = theme.systemGroupedBackgroundColor
self.searchHeader.backgroundColor = theme.systemGroupedBackgroundColor
self.searchingTableView.backgroundColor = theme.systemBackgroundColor
self.statusBar.backgroundColor = theme.navigationBarBackgroundColor
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
@ -171,6 +169,13 @@ extension SearchViewController {
viewModel.viewDidAppeared.send()
}
private func setupBackgroundColor(theme: Theme) {
view.backgroundColor = theme.systemGroupedBackgroundColor
searchHeader.backgroundColor = theme.systemGroupedBackgroundColor
searchingTableView.backgroundColor = theme.systemBackgroundColor
statusBar.backgroundColor = theme.navigationBarBackgroundColor
}
func setupSearchBar() {
searchBar.delegate = self
view.addSubview(searchBar)

View File

@ -102,7 +102,13 @@ class SettingsViewController: UIViewController, NeedsDependency {
tableView.register(SettingsLinkTableViewCell.self, forCellReuseIdentifier: String(describing: SettingsLinkTableViewCell.self))
return tableView
}()
let tableFooterActiveLabel: ActiveLabel = {
let label = ActiveLabel(style: .default)
label.adjustsFontForContentSizeCategory = true
label.textAlignment = .center
return label
}()
lazy var tableFooterView: UIView = {
// init with a frame to fix a conflict ('UIView-Encapsulated-Layout-Height' UIStackView:0x7ffe41e47da0.height == 0)
let view = UIStackView(frame: CGRect(x: 0, y: 0, width: 320, height: 320))
@ -110,14 +116,9 @@ class SettingsViewController: UIViewController, NeedsDependency {
view.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
view.axis = .vertical
view.alignment = .center
let label = ActiveLabel(style: .default)
label.adjustsFontForContentSizeCategory = true
label.textAlignment = .center
label.configure(content: "Mastodon is open source software. You can contribute or report issues on GitHub at <a href=\"https://github.com/tootsuite/mastodon\">tootsuite/mastodon</a> (v3.3.0).", emojiDict: [:])
label.delegate = self
view.addArrangedSubview(label)
tableFooterActiveLabel.delegate = self
view.addArrangedSubview(tableFooterActiveLabel)
return view
}()
@ -190,30 +191,26 @@ class SettingsViewController: UIViewController, NeedsDependency {
}
}
.store(in: &disposeBag)
viewModel.currentInstance
.receive(on: RunLoop.main)
.sink { [weak self] instance in
guard let self = self else { return }
let version = instance?.version ?? "-"
let link = #"<a href="https://github.com/tootsuite/mastodon">tootsuite/mastodon</a>"#
let content = L10n.Scene.Settings.Footer.mastodonDescription(link, version)
self.tableFooterActiveLabel.configure(content: content, emojiDict: [:])
}
.store(in: &disposeBag)
}
private func setupView() {
self.view.backgroundColor = UIColor(dynamicProvider: { traitCollection in
switch traitCollection.userInterfaceLevel {
case .elevated where traitCollection.userInterfaceStyle == .dark:
return ThemeService.shared.currentTheme.value.systemElevatedBackgroundColor
default:
return ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
}
})
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.view.backgroundColor = UIColor(dynamicProvider: { traitCollection in
switch traitCollection.userInterfaceLevel {
case .elevated where traitCollection.userInterfaceStyle == .dark:
return theme.systemElevatedBackgroundColor
default:
return theme.secondarySystemBackgroundColor
}
})
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
@ -229,6 +226,17 @@ class SettingsViewController: UIViewController, NeedsDependency {
updateSectionHeaderStackViewLayout()
}
private func setupBackgroundColor(theme: Theme) {
view.backgroundColor = UIColor(dynamicProvider: { traitCollection in
switch traitCollection.userInterfaceLevel {
case .elevated where traitCollection.userInterfaceStyle == .dark:
return theme.systemElevatedBackgroundColor
default:
return theme.secondarySystemBackgroundColor
}
})
}
private func setupNavigation() {
navigationController?.navigationBar.prefersLargeTitles = true

View File

@ -32,6 +32,7 @@ class SettingsViewModel {
/// - does not has one
/// - does not find subscription for selected trigger when change trigger
let createSubscriptionSubject = PassthroughSubject<(triggerBy: String, values: [Bool?]), Never>()
let currentInstance = CurrentValueSubject<Mastodon.Entity.Instance?, Never>(nil)
/// update a subscription when:
/// - change switch for specified alerts
@ -55,6 +56,26 @@ class SettingsViewModel {
self.processDataSource(setting)
})
.store(in: &disposeBag)
context.authenticationService.activeMastodonAuthenticationBox
.compactMap { $0?.domain }
.map { context.apiService.instance(domain: $0) }
.switchToLatest()
.sink { [weak self] completion in
guard let self = self else { return }
switch completion {
case .failure(let error):
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch instance fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
self.currentInstance.value = nil
case .finished:
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch instance success", ((#file as NSString).lastPathComponent), #line, #function)
}
} receiveValue: { [weak self] response in
guard let self = self else { return }
self.currentInstance.value = response.value
}
.store(in: &disposeBag)
}
deinit {

View File

@ -0,0 +1,102 @@
//
// AppearanceView.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-7-6.
//
import UIKit
class AppearanceView: UIView {
lazy var imageView: UIImageView = {
let view = UIImageView()
// accessibility
view.accessibilityIgnoresInvertColors = true
return view
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .regular)
label.textColor = Asset.Colors.Label.primary.color
label.textAlignment = .center
return label
}()
lazy var checkBox: UIButton = {
let button = UIButton()
button.isUserInteractionEnabled = false
button.setImage(UIImage(systemName: "circle"), for: .normal)
button.setImage(UIImage(systemName: "checkmark.circle.fill"), for: .selected)
button.imageView?.preferredSymbolConfiguration = UIImage.SymbolConfiguration(textStyle: .body)
button.imageView?.tintColor = Asset.Colors.Label.secondary.color
button.imageView?.contentMode = .scaleAspectFill
return button
}()
lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 10
view.distribution = .equalSpacing
return view
}()
var selected: Bool = false {
didSet {
checkBox.isSelected = selected
if selected {
checkBox.imageView?.tintColor = Asset.Colors.brandBlue.color
} else {
checkBox.imageView?.tintColor = Asset.Colors.Label.secondary.color
}
}
}
// MARK: - Methods
init(image: UIImage?, title: String) {
super.init(frame: .zero)
setupUI()
imageView.image = image
titleLabel.text = title
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Private methods
private func setupUI() {
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(checkBox)
addSubview(stackView)
translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: self.topAnchor),
stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 218.0 / 100.0),
])
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
self.alpha = 0.5
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
UIView.animate(withDuration: 0.33) {
self.alpha = 1
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
UIView.animate(withDuration: 0.33) {
self.alpha = 1
}
}
}

View File

@ -12,81 +12,6 @@ protocol SettingsAppearanceTableViewCellDelegate: AnyObject {
func settingsAppearanceCell(_ cell: SettingsAppearanceTableViewCell, didSelectAppearanceMode appearanceMode: SettingsItem.AppearanceMode)
}
class AppearanceView: UIView {
lazy var imageView: UIImageView = {
let view = UIImageView()
// accessibility
view.accessibilityIgnoresInvertColors = true
return view
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .regular)
label.textColor = Asset.Colors.Label.primary.color
label.textAlignment = .center
return label
}()
lazy var checkBox: UIButton = {
let button = UIButton()
button.isUserInteractionEnabled = false
button.setImage(UIImage(systemName: "circle"), for: .normal)
button.setImage(UIImage(systemName: "checkmark.circle.fill"), for: .selected)
button.imageView?.preferredSymbolConfiguration = UIImage.SymbolConfiguration(textStyle: .body)
button.imageView?.tintColor = Asset.Colors.Label.secondary.color
button.imageView?.contentMode = .scaleAspectFill
return button
}()
lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 10
view.distribution = .equalSpacing
return view
}()
var selected: Bool = false {
didSet {
checkBox.isSelected = selected
if selected {
checkBox.imageView?.tintColor = Asset.Colors.brandBlue.color
} else {
checkBox.imageView?.tintColor = Asset.Colors.Label.secondary.color
}
}
}
// MARK: - Methods
init(image: UIImage?, title: String) {
super.init(frame: .zero)
setupUI()
imageView.image = image
titleLabel.text = title
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Private methods
private func setupUI() {
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(checkBox)
addSubview(stackView)
translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: self.topAnchor),
stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 218.0 / 100.0),
])
}
}
class SettingsAppearanceTableViewCell: UITableViewCell {
var disposeBag = Set<AnyCancellable>()

View File

@ -165,7 +165,7 @@ final class StatusView: UIView {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12, weight: .regular))
label.textColor = Asset.Colors.Label.secondary.color
label.text = L10n.Common.Controls.Status.Poll.VoteCount.single(0)
label.text = L10n.Plural.Count.vote(0)
return label
}()
let pollStatusDotLabel: UILabel = {

View File

@ -51,7 +51,8 @@ extension ThreadViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
ThemeService.shared.currentTheme
.receive(on: RunLoop.main)
.sink { [weak self] theme in

View File

@ -17,7 +17,7 @@ extension Mastodon.Entity {
/// # Reference
/// [Document](https://docs.joinmastodon.org/entities/application/)
public struct Application: Codable {
public let name: String
public let website: String?
@ -27,6 +27,15 @@ extension Mastodon.Entity {
public let redirectURI: String? // undocumented
public let clientID: String?
public let clientSecret: String?
public init(name: String, website: String?, vapidKey: String?, redirectURI: String?, clientID: String?, clientSecret: String?) {
self.name = name
self.website = website
self.vapidKey = vapidKey
self.redirectURI = redirectURI
self.clientID = clientID
self.clientSecret = clientSecret
}
enum CodingKeys: String, CodingKey {
case name

View File

@ -17,10 +17,18 @@ extension Mastodon.Entity {
/// # Reference
/// [Document](https://docs.joinmastodon.org/entities/token/)
public struct Token: Codable {
public let accessToken: String
public let tokenType: String
public let scope: String
public let createdAt: Date
public init(accessToken: String, tokenType: String, scope: String, createdAt: Date) {
self.accessToken = accessToken
self.tokenType = tokenType
self.scope = scope
self.createdAt = createdAt
}
enum CodingKeys: String, CodingKey {
case accessToken = "access_token"

View File

@ -1,5 +1,7 @@
strings:
inputs: Mastodon/Resources/en.lproj/Localizable.strings
inputs:
- Mastodon/Resources/en.lproj/Localizable.strings
- Mastodon/Resources/en.lproj/Localizable.stringsdict
outputs:
- templateName: structured-swift5
output: Mastodon/Generated/Strings.swift
@ -7,4 +9,4 @@ xcassets:
inputs: Mastodon/Resources/Assets.xcassets
outputs:
templateName: swift5
output: Mastodon/Generated/Assets.swift
output: Mastodon/Generated/Assets.swift