Merge branch 'release/0.8.3'
This commit is contained in:
commit
8fdcf8a6b1
|
@ -412,6 +412,7 @@
|
|||
DB9E0D6F25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */; };
|
||||
DBA0A10925FB3C2B0079C110 /* RoundedEdgesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */; };
|
||||
DBA0A11325FB3FC10079C110 /* ComposeToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */; };
|
||||
DBA1DB80268F84F80052DB59 /* NotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA1DB7F268F84F80052DB59 /* NotificationType.swift */; };
|
||||
DBA5E7A3263AD0A3004598BB /* PhotoLibraryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A2263AD0A3004598BB /* PhotoLibraryService.swift */; };
|
||||
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A4263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift */; };
|
||||
DBA5E7A9263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBA5E7A8263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift */; };
|
||||
|
@ -1031,6 +1032,7 @@
|
|||
DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIInterpolatingMotionEffect.swift; sourceTree = "<group>"; };
|
||||
DBA0A10825FB3C2B0079C110 /* RoundedEdgesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedEdgesButton.swift; sourceTree = "<group>"; };
|
||||
DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeToolbarView.swift; sourceTree = "<group>"; };
|
||||
DBA1DB7F268F84F80052DB59 /* NotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationType.swift; sourceTree = "<group>"; };
|
||||
DBA5E7A2263AD0A3004598BB /* PhotoLibraryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryService.swift; sourceTree = "<group>"; };
|
||||
DBA5E7A4263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuImagePreviewViewModel.swift; sourceTree = "<group>"; };
|
||||
DBA5E7A8263BD3A4004598BB /* ContextMenuImagePreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuImagePreviewViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1776,6 +1778,7 @@
|
|||
DB6D9F4F2635761F008423CD /* SubscriptionAlerts.swift */,
|
||||
DBAFB7342645463500371D5F /* Emojis.swift */,
|
||||
DBA94439265CC0FC00C537E1 /* Fields.swift */,
|
||||
DBA1DB7F268F84F80052DB59 /* NotificationType.swift */,
|
||||
);
|
||||
path = CoreDataStack;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3181,6 +3184,7 @@
|
|||
DBAC648F267DC84D007FE9FD /* TableNodeDiffableDataSource.swift in Sources */,
|
||||
2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */,
|
||||
0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */,
|
||||
DBA1DB80268F84F80052DB59 /* NotificationType.swift in Sources */,
|
||||
DB9A488A26034D40008B817C /* ComposeViewModel+PublishState.swift in Sources */,
|
||||
DBAC649B267DF8C8007FE9FD /* ActivityIndicatorNode.swift in Sources */,
|
||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
||||
|
@ -3843,7 +3847,7 @@
|
|||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
|
@ -3851,7 +3855,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -3870,7 +3874,7 @@
|
|||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
|
@ -3878,7 +3882,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -4198,7 +4202,7 @@
|
|||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
|
@ -4206,7 +4210,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -4312,7 +4316,7 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -4320,7 +4324,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -4431,7 +4435,7 @@
|
|||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
|
@ -4439,7 +4443,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -4545,7 +4549,7 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -4553,7 +4557,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -4599,7 +4603,7 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -4607,7 +4611,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -4622,7 +4626,7 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -4630,7 +4634,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 0.8.2;
|
||||
MARKETING_VERSION = 0.8.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.NotificationService;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>26</integer>
|
||||
<integer>20</integer>
|
||||
</dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>20</integer>
|
||||
<integer>19</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -37,7 +37,7 @@
|
|||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>21</integer>
|
||||
<integer>18</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -20,7 +20,6 @@ enum NotificationSection: Equatable, Hashable {
|
|||
extension NotificationSection {
|
||||
static func tableViewDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
timestampUpdatePublisher: AnyPublisher<Date, Never>,
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
delegate: NotificationTableViewCellDelegate,
|
||||
dependency: NeedsDependency
|
||||
|
@ -34,73 +33,46 @@ extension NotificationSection {
|
|||
guard let notification = try? managedObjectContext.existingObject(with: objectID) as? MastodonNotification else {
|
||||
return UITableViewCell()
|
||||
}
|
||||
guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: notification.typeRaw) else {
|
||||
// filter out invalid type using predicate
|
||||
assertionFailure()
|
||||
return UITableViewCell()
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationStatusTableViewCell.self), for: indexPath) as! NotificationStatusTableViewCell
|
||||
cell.delegate = delegate
|
||||
|
||||
// configure author
|
||||
cell.avatarImageViewTask = Nuke.loadImage(
|
||||
with: notification.account.avatarImageURL(),
|
||||
options: ImageLoadingOptions(
|
||||
placeholder: UIImage.placeholder(color: .systemFill),
|
||||
transition: .fadeIn(duration: 0.2)
|
||||
),
|
||||
into: cell.avatarImageView
|
||||
)
|
||||
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
||||
cell?.delegate?.userAvatarDidPressed(notification: notification)
|
||||
}
|
||||
|
||||
let createAt = notification.createAt
|
||||
let timeText = createAt.timeAgoSinceNow
|
||||
|
||||
let actionText = type.actionText
|
||||
let actionImageName = type.actionImageName
|
||||
let color = type.color
|
||||
|
||||
if let status = notification.status {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationStatusTableViewCell.self), for: indexPath) as! NotificationStatusTableViewCell
|
||||
cell.delegate = delegate
|
||||
let activeMastodonAuthenticationBox = dependency.context.authenticationService.activeMastodonAuthenticationBox.value
|
||||
let requestUserID = activeMastodonAuthenticationBox?.userID ?? ""
|
||||
let frame = CGRect(x: 0, y: 0, width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right, height: tableView.readableContentGuide.layoutFrame.height)
|
||||
StatusSection.configure(
|
||||
cell: cell,
|
||||
tableView: tableView,
|
||||
dependency: dependency,
|
||||
readableLayoutFrame: frame,
|
||||
status: status,
|
||||
requestUserID: requestUserID,
|
||||
statusItemAttribute: attribute
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.actionImageView.image = UIImage(
|
||||
systemName: notification.notificationType.actionImageName,
|
||||
withConfiguration: UIImage.SymbolConfiguration(
|
||||
pointSize: 12, weight: .semibold
|
||||
)
|
||||
cell.actionImageBackground.backgroundColor = color
|
||||
cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict)
|
||||
cell.actionLabel.text = actionText + " · " + timeText
|
||||
timestampUpdatePublisher
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
let timeText = createAt.timeAgoSinceNow
|
||||
cell.actionLabel.text = actionText + " · " + timeText
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
if let url = notification.account.avatarImageURL() {
|
||||
cell.avatarImageViewTask = Nuke.loadImage(
|
||||
with: url,
|
||||
options: ImageLoadingOptions(
|
||||
placeholder: UIImage.placeholder(color: .systemFill),
|
||||
transition: .fadeIn(duration: 0.2)
|
||||
),
|
||||
into: cell.avatarImageView
|
||||
)
|
||||
}
|
||||
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
||||
cell?.delegate?.userAvatarDidPressed(notification: notification)
|
||||
)?.withRenderingMode(.alwaysTemplate)
|
||||
cell.actionImageBackground.backgroundColor = notification.notificationType.color
|
||||
|
||||
// configure author name, notification description, timestamp
|
||||
cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict)
|
||||
let createAt = notification.createAt
|
||||
let actionText = notification.notificationType.actionText
|
||||
cell.actionLabel.text = actionText + " · " + createAt.timeAgoSinceNow
|
||||
AppContext.shared.timestampUpdatePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.actionLabel.text = actionText + " · " + createAt.timeAgoSinceNow
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
if let actionImage = UIImage(systemName: actionImageName, withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .semibold))?.withRenderingMode(.alwaysTemplate) {
|
||||
cell.actionImageView.image = actionImage
|
||||
}
|
||||
return cell
|
||||
|
||||
} else {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationTableViewCell.self), for: indexPath) as! NotificationTableViewCell
|
||||
cell.delegate = delegate
|
||||
timestampUpdatePublisher
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
let timeText = createAt.timeAgoSinceNow
|
||||
cell.actionLabel.text = actionText + " · " + timeText
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// configure follow request (if exist)
|
||||
if case .followRequest = notification.notificationType {
|
||||
cell.acceptButton.publisher(for: .touchUpInside)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
|
@ -113,29 +85,43 @@ extension NotificationSection {
|
|||
cell.delegate?.notificationTableViewCell(cell, notification: notification, rejectButtonDidPressed: cell.rejectButton)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.actionImageBackground.backgroundColor = color
|
||||
cell.actionLabel.text = actionText + " · " + timeText
|
||||
cell.nameLabel.configure(content: notification.account.displayNameWithFallback, emojiDict: notification.account.emojiDict)
|
||||
if let url = notification.account.avatarImageURL() {
|
||||
cell.avatarImageViewTask = Nuke.loadImage(
|
||||
with: url,
|
||||
options: ImageLoadingOptions(
|
||||
placeholder: UIImage.placeholder(color: .systemFill),
|
||||
transition: .fadeIn(duration: 0.2)
|
||||
),
|
||||
into: cell.avatarImageView
|
||||
)
|
||||
}
|
||||
cell.avatarImageView.gesture().sink { [weak cell] _ in
|
||||
cell?.delegate?.userAvatarDidPressed(notification: notification)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
if let actionImage = UIImage(systemName: actionImageName, withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .semibold))?.withRenderingMode(.alwaysTemplate) {
|
||||
cell.actionImageView.image = actionImage
|
||||
}
|
||||
cell.buttonStackView.isHidden = (type != .followRequest)
|
||||
return cell
|
||||
cell.buttonStackView.isHidden = false
|
||||
} else {
|
||||
cell.buttonStackView.isHidden = true
|
||||
}
|
||||
|
||||
// configure status (if exist)
|
||||
if let status = notification.status {
|
||||
let frame = CGRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right,
|
||||
height: tableView.readableContentGuide.layoutFrame.height
|
||||
)
|
||||
StatusSection.configure(
|
||||
cell: cell,
|
||||
tableView: tableView,
|
||||
dependency: dependency,
|
||||
readableLayoutFrame: frame,
|
||||
status: status,
|
||||
requestUserID: notification.userID,
|
||||
statusItemAttribute: attribute
|
||||
)
|
||||
cell.statusContainerView.isHidden = false
|
||||
cell.containerStackView.alignment = .top
|
||||
cell.containerStackViewBottomLayoutConstraint.constant = 0
|
||||
} else {
|
||||
if case .followRequest = notification.notificationType {
|
||||
cell.containerStackView.alignment = .top
|
||||
} else {
|
||||
cell.containerStackView.alignment = .center
|
||||
}
|
||||
cell.statusContainerView.isHidden = true
|
||||
cell.containerStackViewBottomLayoutConstraint.constant = 5 // 5pt margin when no status view
|
||||
}
|
||||
|
||||
return cell
|
||||
|
||||
case .bottomLoader:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self)) as! TimelineBottomLoaderTableViewCell
|
||||
cell.startAnimating()
|
||||
|
|
|
@ -512,13 +512,8 @@ extension StatusSection {
|
|||
let name = author.displayName.isEmpty ? author.username : author.displayName
|
||||
return L10n.Common.Controls.Status.userReblogged(name)
|
||||
}()
|
||||
MastodonStatusContent.parseResult(content: headerText, emojiDict: status.author.emojiDict)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak cell] parseResult in
|
||||
guard let cell = cell else { return }
|
||||
cell.statusView.headerInfoLabel.configure(contentParseResult: parseResult)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
// sync set display name to avoid layout issue
|
||||
cell.statusView.headerInfoLabel.configure(content: headerText, emojiDict: status.author.emojiDict)
|
||||
cell.statusView.headerInfoLabel.accessibilityLabel = headerText
|
||||
cell.statusView.headerInfoLabel.isAccessibilityElement = true
|
||||
} else if status.inReplyToID != nil {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// NotificationType.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-7-3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
extension MastodonNotification {
|
||||
var notificationType: Mastodon.Entity.Notification.NotificationType {
|
||||
return Mastodon.Entity.Notification.NotificationType(rawValue: typeRaw) ?? ._other(typeRaw)
|
||||
}
|
||||
}
|
|
@ -40,10 +40,10 @@ internal enum Asset {
|
|||
internal static let disabled = ColorAsset(name: "Colors/Background/Poll/disabled")
|
||||
}
|
||||
internal static let alertYellow = ColorAsset(name: "Colors/Background/alert.yellow")
|
||||
internal static let bar = ColorAsset(name: "Colors/Background/bar")
|
||||
internal static let dangerBorder = ColorAsset(name: "Colors/Background/danger.border")
|
||||
internal static let danger = ColorAsset(name: "Colors/Background/danger")
|
||||
internal static let mediaTypeIndicotor = ColorAsset(name: "Colors/Background/media.type.indicotor")
|
||||
internal static let navigationBar = ColorAsset(name: "Colors/Background/navigationBar")
|
||||
internal static let onboardingBackground = ColorAsset(name: "Colors/Background/onboarding.background")
|
||||
internal static let secondaryGroupedSystemBackground = ColorAsset(name: "Colors/Background/secondary.grouped.system.background")
|
||||
internal static let secondarySystemBackground = ColorAsset(name: "Colors/Background/secondary.system.background")
|
||||
|
@ -54,8 +54,10 @@ internal enum Asset {
|
|||
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Colors/Background/tertiary.system.grouped.background")
|
||||
}
|
||||
internal enum Border {
|
||||
internal static let notification = ColorAsset(name: "Colors/Border/notification")
|
||||
internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll")
|
||||
internal static let notificationStatus = ColorAsset(name: "Colors/Border/notification.status")
|
||||
internal static let searchCard = ColorAsset(name: "Colors/Border/searchCard")
|
||||
internal static let status = ColorAsset(name: "Colors/Border/status")
|
||||
}
|
||||
internal enum Button {
|
||||
internal static let actionToolbar = ColorAsset(name: "Colors/Button/action.toolbar")
|
||||
|
@ -84,6 +86,9 @@ internal enum Asset {
|
|||
internal enum Slider {
|
||||
internal static let track = ColorAsset(name: "Colors/Slider/track")
|
||||
}
|
||||
internal enum TabBar {
|
||||
internal static let itemInactive = ColorAsset(name: "Colors/TabBar/item.inactive")
|
||||
}
|
||||
internal enum TextField {
|
||||
internal static let background = ColorAsset(name: "Colors/TextField/background")
|
||||
internal static let invalid = ColorAsset(name: "Colors/TextField/invalid")
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.940",
|
||||
"blue" : "29",
|
||||
"green" : "29",
|
||||
"red" : "29"
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.263",
|
||||
"green" : "0.208",
|
||||
"red" : "0.192"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x43",
|
||||
"green" : "0x35",
|
||||
"red" : "0x31"
|
||||
"blue" : "67",
|
||||
"green" : "53",
|
||||
"red" : "49"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.216",
|
||||
"green" : "0.173",
|
||||
"red" : "0.157"
|
||||
"blue" : "55",
|
||||
"green" : "44",
|
||||
"red" : "40"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.263",
|
||||
"green" : "0.208",
|
||||
"red" : "0.192"
|
||||
"blue" : "67",
|
||||
"green" : "53",
|
||||
"red" : "49"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.431",
|
||||
"green" : "0.341",
|
||||
"red" : "0.310"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE8",
|
||||
"green" : "0xE1",
|
||||
"red" : "0xD9"
|
||||
"blue" : "232",
|
||||
"green" : "225",
|
||||
"red" : "217"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "60",
|
||||
"green" : "58",
|
||||
"red" : "58"
|
||||
"blue" : "110",
|
||||
"green" : "87",
|
||||
"red" : "79"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.003",
|
||||
"blue" : "213",
|
||||
"green" : "213",
|
||||
"red" : "213"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.549",
|
||||
"green" : "0.510",
|
||||
"red" : "0.431"
|
||||
"blue" : "140",
|
||||
"green" : "130",
|
||||
"red" : "110"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
@ -23,9 +23,9 @@
|
|||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.392",
|
||||
"green" : "0.365",
|
||||
"red" : "0.310"
|
||||
"blue" : "100",
|
||||
"green" : "93",
|
||||
"red" : "79"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "140",
|
||||
"green" : "130",
|
||||
"red" : "110"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "200",
|
||||
"green" : "174",
|
||||
"red" : "155"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ extension MainTabBarController {
|
|||
|
||||
delegate = self
|
||||
|
||||
view.backgroundColor = .systemBackground
|
||||
view.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||
|
||||
let tabs = Tab.allCases
|
||||
let viewControllers: [UIViewController] = tabs.map { tab in
|
||||
|
@ -113,13 +113,6 @@ extension MainTabBarController {
|
|||
setViewControllers(viewControllers, animated: false)
|
||||
selectedIndex = 0
|
||||
|
||||
// TODO: custom accent color
|
||||
let tabBarAppearance = UITabBarAppearance()
|
||||
tabBarAppearance.configureWithDefaultBackground()
|
||||
tabBarAppearance.selectionIndicatorTintColor = Asset.Colors.brandBlue.color
|
||||
tabBar.standardAppearance = tabBarAppearance
|
||||
|
||||
|
||||
context.apiService.error
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] error in
|
||||
|
|
|
@ -32,11 +32,8 @@ final class NotificationViewController: UIViewController, NeedsDependency {
|
|||
|
||||
let tableView: UITableView = {
|
||||
let tableView = ControlContainableTableView()
|
||||
tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
||||
tableView.register(NotificationTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationTableViewCell.self))
|
||||
tableView.register(NotificationStatusTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationStatusTableViewCell.self))
|
||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = UITableView.automaticDimension
|
||||
tableView.separatorStyle = .none
|
||||
tableView.tableFooterView = UIView()
|
||||
|
@ -192,33 +189,33 @@ extension NotificationViewController {
|
|||
extension NotificationViewController: StatusTableViewControllerAspect { }
|
||||
|
||||
// MARK: - TableViewCellHeightCacheableContainer
|
||||
//extension NotificationViewController: TableViewCellHeightCacheableContainer {
|
||||
// var cellFrameCache: NSCache<NSNumber, NSValue> {
|
||||
// viewModel.cellFrameCache
|
||||
// }
|
||||
//
|
||||
// func cacheTableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
// guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
// guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
// let key = item.hashValue
|
||||
// let frame = cell.frame
|
||||
// viewModel.cellFrameCache.setObject(NSValue(cgRect: frame), forKey: NSNumber(value: key))
|
||||
// }
|
||||
//
|
||||
// func handleTableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
// guard let diffableDataSource = viewModel.diffableDataSource else { return UITableView.automaticDimension }
|
||||
// guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return UITableView.automaticDimension }
|
||||
// guard let frame = viewModel.cellFrameCache.object(forKey: NSNumber(value: item.hashValue))?.cgRectValue else {
|
||||
// if case .bottomLoader = item {
|
||||
// return TimelineLoaderTableViewCell.cellHeight
|
||||
// } else {
|
||||
// return UITableView.automaticDimension
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ceil(frame.height)
|
||||
// }
|
||||
//}
|
||||
extension NotificationViewController: TableViewCellHeightCacheableContainer {
|
||||
var cellFrameCache: NSCache<NSNumber, NSValue> {
|
||||
viewModel.cellFrameCache
|
||||
}
|
||||
|
||||
func cacheTableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
let key = item.hashValue
|
||||
let frame = cell.frame
|
||||
viewModel.cellFrameCache.setObject(NSValue(cgRect: frame), forKey: NSNumber(value: key))
|
||||
}
|
||||
|
||||
func handleTableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return UITableView.automaticDimension }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return UITableView.automaticDimension }
|
||||
guard let frame = viewModel.cellFrameCache.object(forKey: NSNumber(value: item.hashValue))?.cgRectValue else {
|
||||
if case .bottomLoader = item {
|
||||
return TimelineLoaderTableViewCell.cellHeight
|
||||
} else {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
}
|
||||
|
||||
return ceil(frame.height)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
|
@ -274,11 +271,11 @@ extension NotificationViewController: ContentOffsetAdjustableTimelineViewControl
|
|||
// MARK: - NotificationTableViewCellDelegate
|
||||
extension NotificationViewController: NotificationTableViewCellDelegate {
|
||||
|
||||
func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) {
|
||||
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton) {
|
||||
viewModel.acceptFollowRequest(notification: notification)
|
||||
}
|
||||
|
||||
func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) {
|
||||
func notificationTableViewCell(_ cell: NotificationStatusTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton) {
|
||||
viewModel.rejectFollowRequest(notification: notification)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,14 +17,8 @@ extension NotificationViewModel {
|
|||
delegate: NotificationTableViewCellDelegate,
|
||||
dependency: NeedsDependency
|
||||
) {
|
||||
let timestampUpdatePublisher = Timer.publish(every: 1.0, on: .main, in: .common)
|
||||
.autoconnect()
|
||||
.share()
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
diffableDataSource = NotificationSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
timestampUpdatePublisher: timestampUpdatePublisher,
|
||||
managedObjectContext: context.managedObjectContext,
|
||||
delegate: delegate,
|
||||
dependency: dependency
|
||||
|
|
|
@ -15,12 +15,17 @@ import FLAnimatedImage
|
|||
import Nuke
|
||||
|
||||
final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
||||
|
||||
static let actionImageBorderWidth: CGFloat = 2
|
||||
static let statusPadding = UIEdgeInsets(top: 50, left: 73, bottom: 24, right: 24)
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
var pollCountdownSubscription: AnyCancellable?
|
||||
var delegate: NotificationTableViewCellDelegate?
|
||||
|
||||
var containerStackViewBottomLayoutConstraint: NSLayoutConstraint!
|
||||
let containerStackView = UIStackView()
|
||||
|
||||
var avatarImageViewTask: ImageTask?
|
||||
let avatarImageView: UIImageView = {
|
||||
let imageView = FLAnimatedImageView()
|
||||
|
@ -51,7 +56,9 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
|||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
let contentStackView = UIStackView()
|
||||
|
||||
let actionLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
|
@ -67,18 +74,34 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
|
|||
label.lineBreakMode = .byTruncatingTail
|
||||
return label
|
||||
}()
|
||||
|
||||
let statusBorder: UIView = {
|
||||
|
||||
let buttonStackView = UIStackView()
|
||||
|
||||
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 statusContainerView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
view.layer.masksToBounds = true
|
||||
view.layer.cornerRadius = 6
|
||||
view.layer.borderWidth = 2
|
||||
view.layer.cornerCurve = .continuous
|
||||
view.layer.borderColor = Asset.Colors.Border.notification.color.cgColor
|
||||
view.clipsToBounds = true
|
||||
view.layer.borderWidth = 2
|
||||
view.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor
|
||||
return view
|
||||
}()
|
||||
|
||||
let statusView = StatusView()
|
||||
|
||||
let separatorLine = UIView.separatorLine
|
||||
|
@ -120,91 +143,102 @@ extension NotificationStatusTableViewCell {
|
|||
view.backgroundColor = Asset.Colors.Background.Cell.highlight.color
|
||||
return view
|
||||
}()
|
||||
let containerStackView = UIStackView()
|
||||
|
||||
containerStackView.axis = .horizontal
|
||||
containerStackView.alignment = .top
|
||||
containerStackView.spacing = 4
|
||||
containerStackView.distribution = .fill
|
||||
containerStackView.spacing = 14 + 2 // 2pt for status container outline border
|
||||
containerStackView.layoutMargins = UIEdgeInsets(top: 14, left: 0, bottom: 12, right: 0)
|
||||
containerStackView.isLayoutMarginsRelativeArrangement = true
|
||||
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(containerStackView)
|
||||
containerStackViewBottomLayoutConstraint = contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor)
|
||||
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),
|
||||
containerStackViewBottomLayoutConstraint.priority(.required - 1),
|
||||
])
|
||||
|
||||
containerStackView.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
|
||||
avatarContainer.addSubview(avatarImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
avatarImageView.topAnchor.constraint(equalTo: avatarContainer.topAnchor),
|
||||
avatarImageView.leadingAnchor.constraint(equalTo: avatarContainer.leadingAnchor),
|
||||
avatarImageView.trailingAnchor.constraint(equalTo: avatarContainer.trailingAnchor),
|
||||
avatarImageView.bottomAnchor.constraint(equalTo: avatarContainer.bottomAnchor),
|
||||
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
|
||||
avatarContainer.addSubview(actionImageBackground)
|
||||
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)
|
||||
actionImageBackground.heightAnchor.constraint(equalToConstant: 24 + NotificationStatusTableViewCell.actionImageBorderWidth).priority(.required - 1),
|
||||
actionImageBackground.widthAnchor.constraint(equalToConstant: 24 + NotificationStatusTableViewCell.actionImageBorderWidth).priority(.required - 1),
|
||||
actionImageBackground.centerYAnchor.constraint(equalTo: avatarImageView.bottomAnchor),
|
||||
actionImageBackground.centerXAnchor.constraint(equalTo: avatarContainer.trailingAnchor),
|
||||
])
|
||||
|
||||
avatarContainer.addSubview(actionImageView)
|
||||
actionImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
actionImageBackground.addSubview(actionImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
actionImageView.centerXAnchor.constraint(equalTo: actionImageBackground.centerXAnchor),
|
||||
actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor)
|
||||
actionImageView.centerYAnchor.constraint(equalTo: actionImageBackground.centerYAnchor),
|
||||
])
|
||||
|
||||
containerStackView.addArrangedSubview(contentStackView)
|
||||
contentStackView.axis = .vertical
|
||||
contentStackView.spacing = 6
|
||||
|
||||
// header
|
||||
let actionStackView = UIStackView()
|
||||
contentStackView.addArrangedSubview(actionStackView)
|
||||
actionStackView.axis = .horizontal
|
||||
actionStackView.distribution = .fill
|
||||
actionStackView.spacing = 4
|
||||
actionStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
nameLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
actionStackView.addArrangedSubview(nameLabel)
|
||||
actionLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
actionStackView.addArrangedSubview(actionLabel)
|
||||
nameLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical)
|
||||
nameLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
let statusStackView = UIStackView()
|
||||
statusStackView.axis = .vertical
|
||||
|
||||
statusStackView.distribution = .fill
|
||||
statusStackView.spacing = 4
|
||||
statusStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusStackView.addArrangedSubview(actionStackView)
|
||||
|
||||
statusBorder.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusBorder.addSubview(statusView)
|
||||
NSLayoutConstraint.activate([
|
||||
statusView.topAnchor.constraint(equalTo: statusBorder.topAnchor, constant: 12),
|
||||
statusView.leadingAnchor.constraint(equalTo: statusBorder.leadingAnchor, constant: 12),
|
||||
statusBorder.bottomAnchor.constraint(equalTo: statusView.bottomAnchor, constant: 12),
|
||||
statusBorder.trailingAnchor.constraint(equalTo: statusView.trailingAnchor, constant: 12),
|
||||
])
|
||||
|
||||
statusView.delegate = self
|
||||
|
||||
statusStackView.addArrangedSubview(statusBorder)
|
||||
|
||||
containerStackView.addArrangedSubview(statusStackView)
|
||||
|
||||
actionStackView.addArrangedSubview(nameLabel)
|
||||
actionStackView.addArrangedSubview(actionLabel)
|
||||
nameLabel.setContentHuggingPriority(.required - 1, for: .horizontal)
|
||||
nameLabel.setContentHuggingPriority(.required - 1, for: .vertical)
|
||||
nameLabel.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
|
||||
nameLabel.setContentCompressionResistancePriority(.required - 1, for: .vertical)
|
||||
actionLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
|
||||
// follow request
|
||||
contentStackView.addArrangedSubview(buttonStackView)
|
||||
buttonStackView.addArrangedSubview(acceptButton)
|
||||
buttonStackView.addArrangedSubview(rejectButton)
|
||||
buttonStackView.axis = .horizontal
|
||||
buttonStackView.distribution = .fillEqually
|
||||
|
||||
// status
|
||||
contentStackView.addArrangedSubview(statusContainerView)
|
||||
statusContainerView.layoutMargins = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
|
||||
statusView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusContainerView.addSubview(statusView)
|
||||
NSLayoutConstraint.activate([
|
||||
statusView.topAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.topAnchor),
|
||||
statusView.leadingAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.leadingAnchor),
|
||||
statusView.trailingAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.trailingAnchor),
|
||||
statusView.bottomAnchor.constraint(equalTo: statusContainerView.layoutMarginsGuide.bottomAnchor),
|
||||
])
|
||||
statusContainerView.backgroundColor = UIColor(dynamicProvider: { collection in
|
||||
switch collection.userInterfaceStyle {
|
||||
case .dark:
|
||||
return Asset.Colors.Background.tertiarySystemGroupedBackground.color
|
||||
default:
|
||||
return .clear
|
||||
}
|
||||
})
|
||||
// remove item don't display
|
||||
statusView.actionToolbarContainer.removeFromStackView()
|
||||
// it affect stackView's height, need remove
|
||||
statusView.headerContainerView.removeFromStackView()
|
||||
|
||||
// adaptive separator
|
||||
separatorLine.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(separatorLine)
|
||||
separatorLineToEdgeLeadingLayoutConstraint = separatorLine.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
|
||||
|
@ -215,21 +249,18 @@ extension NotificationStatusTableViewCell {
|
|||
separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||
separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)),
|
||||
])
|
||||
resetSeparatorLineLayout()
|
||||
|
||||
statusView.delegate = self
|
||||
|
||||
// remove item don't display
|
||||
statusView.actionToolbarContainer.removeFromStackView()
|
||||
// it affect stackView's height,need remove
|
||||
statusView.avatarView.removeFromStackView()
|
||||
statusView.usernameLabel.removeFromStackView()
|
||||
resetSeparatorLineLayout()
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
resetSeparatorLineLayout()
|
||||
statusBorder.layer.borderColor = Asset.Colors.Border.notification.color.cgColor
|
||||
actionImageBackground.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor
|
||||
statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,238 +28,238 @@ protocol NotificationTableViewCellDelegate: AnyObject {
|
|||
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: NotificationTableViewCell, notification: MastodonNotification, acceptButtonDidPressed button: UIButton)
|
||||
func notificationTableViewCell(_ cell: NotificationTableViewCell, notification: MastodonNotification, rejectButtonDidPressed button: UIButton)
|
||||
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,
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//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,
|
||||
// ])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
|
|
@ -39,7 +39,7 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||
|
||||
let statusBar: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = Asset.Colors.Background.navigationBar.color
|
||||
view.backgroundColor = Asset.Colors.Background.bar.color
|
||||
return view
|
||||
}()
|
||||
|
||||
|
@ -52,7 +52,7 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||
// searchBar.setImage(micImage, for: .bookmark, state: .normal)
|
||||
// searchBar.showsBookmarkButton = true
|
||||
searchBar.scopeButtonTitles = [L10n.Scene.Search.Searching.Segment.all, L10n.Scene.Search.Searching.Segment.people, L10n.Scene.Search.Searching.Segment.hashtags]
|
||||
searchBar.barTintColor = Asset.Colors.Background.navigationBar.color
|
||||
searchBar.barTintColor = Asset.Colors.Background.bar.color
|
||||
return searchBar
|
||||
}()
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ class SettingsViewController: UIViewController, NeedsDependency {
|
|||
tableView.delegate = self
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.separatorColor = Asset.Colors.Background.Cell.separator.color
|
||||
|
||||
tableView.register(SettingsAppearanceTableViewCell.self, forCellReuseIdentifier: String(describing: SettingsAppearanceTableViewCell.self))
|
||||
tableView.register(SettingsToggleTableViewCell.self, forCellReuseIdentifier: String(describing: SettingsToggleTableViewCell.self))
|
||||
|
|
|
@ -15,7 +15,7 @@ protocol PlayerContainerViewDelegate: AnyObject {
|
|||
}
|
||||
|
||||
final class PlayerContainerView: UIView {
|
||||
static let cornerRadius: CGFloat = 8
|
||||
static let cornerRadius: CGFloat = ContentWarningOverlayView.cornerRadius
|
||||
|
||||
private let container = UIView()
|
||||
private let touchBlockingView = TouchBlockingView()
|
||||
|
|
|
@ -77,8 +77,7 @@ extension ContentWarningOverlayView {
|
|||
private func _init() {
|
||||
backgroundColor = .clear
|
||||
isUserInteractionEnabled = true
|
||||
layer.masksToBounds = false
|
||||
|
||||
|
||||
// visual effect style
|
||||
// add blur visual effect view in the setup method
|
||||
blurVisualEffectView.layer.masksToBounds = true
|
||||
|
@ -115,10 +114,10 @@ extension ContentWarningOverlayView {
|
|||
contentOverlayView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(contentOverlayView)
|
||||
NSLayoutConstraint.activate([
|
||||
topAnchor.constraint(equalTo: contentOverlayView.topAnchor, constant: 2),
|
||||
leadingAnchor.constraint(equalTo: contentOverlayView.leadingAnchor, constant: 2),
|
||||
contentOverlayView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 2),
|
||||
contentOverlayView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 2),
|
||||
topAnchor.constraint(equalTo: contentOverlayView.topAnchor),
|
||||
leadingAnchor.constraint(equalTo: contentOverlayView.leadingAnchor),
|
||||
contentOverlayView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
contentOverlayView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
])
|
||||
|
||||
let blurContentWarningLabelContainer = UIStackView()
|
||||
|
|
|
@ -97,8 +97,8 @@ final class StatusView: UIView {
|
|||
}()
|
||||
let avatarImageView: UIImageView = {
|
||||
let imageView = FLAnimatedImageView()
|
||||
imageView.layer.shouldRasterize = true
|
||||
imageView.layer.rasterizationScale = UIScreen.main.scale
|
||||
// imageView.layer.shouldRasterize = true
|
||||
// imageView.layer.rasterizationScale = UIScreen.main.scale
|
||||
return imageView
|
||||
}()
|
||||
let avatarStackedContainerButton: AvatarStackContainerButton = AvatarStackContainerButton()
|
||||
|
@ -343,6 +343,7 @@ extension StatusView {
|
|||
let titleContainerStackView = UIStackView()
|
||||
authorMetaContainerStackView.addArrangedSubview(titleContainerStackView)
|
||||
titleContainerStackView.axis = .horizontal
|
||||
titleContainerStackView.alignment = .center
|
||||
titleContainerStackView.spacing = 4
|
||||
nameLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
titleContainerStackView.addArrangedSubview(nameLabel)
|
||||
|
@ -379,7 +380,7 @@ extension StatusView {
|
|||
authorContainerStackView.topAnchor.constraint(equalTo: authorContainerView.topAnchor),
|
||||
authorContainerStackView.leadingAnchor.constraint(equalTo: authorContainerView.leadingAnchor),
|
||||
authorContainerStackView.trailingAnchor.constraint(equalTo: authorContainerView.trailingAnchor),
|
||||
authorContainerView.bottomAnchor.constraint(equalTo: authorContainerStackView.bottomAnchor, constant: StatusView.containerStackViewSpacing).priority(.defaultHigh),
|
||||
authorContainerView.bottomAnchor.constraint(equalTo: authorContainerStackView.bottomAnchor, constant: StatusView.containerStackViewSpacing).priority(.required - 1),
|
||||
])
|
||||
containerStackView.addArrangedSubview(authorContainerView)
|
||||
|
||||
|
|
|
@ -46,11 +46,12 @@ final class AvatarStackContainerButton: UIControl {
|
|||
extension AvatarStackContainerButton {
|
||||
|
||||
private func _init() {
|
||||
topLeadingAvatarStackedImageView.layer.shouldRasterize = true
|
||||
topLeadingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale
|
||||
|
||||
bottomTrailingAvatarStackedImageView.layer.shouldRasterize = true
|
||||
bottomTrailingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale
|
||||
// GIF get worse when enable rasterize
|
||||
// topLeadingAvatarStackedImageView.layer.shouldRasterize = true
|
||||
// topLeadingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale
|
||||
//
|
||||
// bottomTrailingAvatarStackedImageView.layer.shouldRasterize = true
|
||||
// bottomTrailingAvatarStackedImageView.layer.rasterizationScale = UIScreen.main.scale
|
||||
|
||||
topLeadingAvatarStackedImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(topLeadingAvatarStackedImageView)
|
||||
|
|
|
@ -33,6 +33,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
|
||||
// set tint color
|
||||
window.tintColor = Asset.Colors.brandBlue.color
|
||||
|
||||
// set navigation bar appearance
|
||||
let appearance = UINavigationBarAppearance()
|
||||
appearance.configureWithDefaultBackground()
|
||||
appearance.backgroundColor = Asset.Colors.Background.bar.color
|
||||
UINavigationBar.appearance().standardAppearance = appearance
|
||||
UINavigationBar.appearance().compactAppearance = appearance
|
||||
UINavigationBar.appearance().scrollEdgeAppearance = appearance
|
||||
|
||||
// set tab bar appearance
|
||||
let tabBarAppearance = UITabBarAppearance()
|
||||
tabBarAppearance.configureWithDefaultBackground()
|
||||
|
||||
let tabBarItemAppearance = UITabBarItemAppearance()
|
||||
tabBarItemAppearance.selected.iconColor = Asset.Colors.brandBlue.color
|
||||
tabBarItemAppearance.focused.iconColor = Asset.Colors.TabBar.itemInactive.color
|
||||
tabBarItemAppearance.normal.iconColor = Asset.Colors.TabBar.itemInactive.color
|
||||
tabBarItemAppearance.disabled.iconColor = Asset.Colors.TabBar.itemInactive.color
|
||||
tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance
|
||||
tabBarAppearance.inlineLayoutAppearance = tabBarItemAppearance
|
||||
tabBarAppearance.compactInlineLayoutAppearance = tabBarItemAppearance
|
||||
|
||||
tabBarAppearance.backgroundColor = Asset.Colors.Background.bar.color
|
||||
tabBarAppearance.selectionIndicatorTintColor = Asset.Colors.brandBlue.color
|
||||
UITabBar.appearance().standardAppearance = tabBarAppearance
|
||||
|
||||
let appContext = AppContext.shared
|
||||
let sceneCoordinator = SceneCoordinator(scene: scene, sceneDelegate: self, appContext: appContext)
|
||||
|
|
Loading…
Reference in New Issue