Merge pull request #217 from mastodon/feature/share-action-extension

Add share action extension
This commit is contained in:
CMK 2021-07-20 20:29:32 +08:00 committed by GitHub
commit 6046cb0e94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
159 changed files with 3604 additions and 963 deletions

View File

@ -7,12 +7,14 @@
import os import os
import Foundation import Foundation
import Combine
import CoreData import CoreData
import AppShared import AppShared
public final class CoreDataStack { public final class CoreDataStack {
private(set) var storeDescriptions: [NSPersistentStoreDescription] private(set) var storeDescriptions: [NSPersistentStoreDescription]
public let didFinishLoad = CurrentValueSubject<Bool, Never>(false)
init(persistentStoreDescriptions storeDescriptions: [NSPersistentStoreDescription]) { init(persistentStoreDescriptions storeDescriptions: [NSPersistentStoreDescription]) {
self.storeDescriptions = storeDescriptions self.storeDescriptions = storeDescriptions
@ -33,7 +35,10 @@ public final class CoreDataStack {
*/ */
let container = CoreDataStack.persistentContainer() let container = CoreDataStack.persistentContainer()
CoreDataStack.configure(persistentContainer: container, storeDescriptions: storeDescriptions) CoreDataStack.configure(persistentContainer: container, storeDescriptions: storeDescriptions)
CoreDataStack.load(persistentContainer: container) CoreDataStack.load(persistentContainer: container) { [weak self] in
guard let self = self else { return }
self.didFinishLoad.value = true
}
return container return container
}() }()
@ -52,7 +57,7 @@ public final class CoreDataStack {
container.persistentStoreDescriptions = storeDescriptions container.persistentStoreDescriptions = storeDescriptions
} }
static func load(persistentContainer container: NSPersistentContainer) { static func load(persistentContainer container: NSPersistentContainer, callback: @escaping () -> Void) {
container.loadPersistentStores(completionHandler: { (storeDescription, error) in container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? { if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
@ -85,6 +90,8 @@ public final class CoreDataStack {
container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.automaticallyMergesChangesFromParent = true
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, storeDescription.debugDescription) os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, storeDescription.debugDescription)
callback()
}) })
} }
@ -96,7 +103,10 @@ extension CoreDataStack {
let oldStoreURL = persistentContainer.persistentStoreCoordinator.url(for: persistentContainer.persistentStoreCoordinator.persistentStores.first!) let oldStoreURL = persistentContainer.persistentStoreCoordinator.url(for: persistentContainer.persistentStoreCoordinator.persistentStores.first!)
try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: oldStoreURL, ofType: NSSQLiteStoreType, options: nil) try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: oldStoreURL, ofType: NSSQLiteStoreType, options: nil)
CoreDataStack.load(persistentContainer: persistentContainer) CoreDataStack.load(persistentContainer: persistentContainer) { [weak self] in
guard let self = self else { return }
self.didFinishLoad.value = true
}
} }
} }

View File

@ -22,7 +22,7 @@
"publish_post_failure": { "publish_post_failure": {
"title": "Publish Failure", "title": "Publish Failure",
"message": "Failed to publish the post.\nPlease check your internet connection.", "message": "Failed to publish the post.\nPlease check your internet connection.",
"attchments_message": { "attachments_message": {
"video_attach_with_photo": "Cannot attach a video to a post that already contains images.", "video_attach_with_photo": "Cannot attach a video to a post that already contains images.",
"more_than_one_video": "Cannot attach more than one video." "more_than_one_video": "Cannot attach more than one video."
} }
@ -524,7 +524,7 @@
}, },
"boring_zone": { "boring_zone": {
"title": "The Boring Zone", "title": "The Boring Zone",
"account_settings": "Account settings", "account_settings": "Account Settings",
"terms": "Terms of Service", "terms": "Terms of Service",
"privacy": "Privacy Policy" "privacy": "Privacy Policy"
}, },

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -105,8 +105,8 @@
"repositoryURL": "https://github.com/onevcat/Kingfisher.git", "repositoryURL": "https://github.com/onevcat/Kingfisher.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "15d199e84677303a7004ed2c5ecaa1a90f3863f8", "revision": "44450a8f564d7c0165f736ba2250649ff8d3e556",
"version": "6.2.1" "version": "6.3.0"
} }
}, },
{ {
@ -123,8 +123,8 @@
"repositoryURL": "https://github.com/kean/Nuke.git", "repositoryURL": "https://github.com/kean/Nuke.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "69ae6d5b8c4b898450432f94bd35f863d3830cfc", "revision": "83e1edaa5a30c567eb129c21c6d00f2f552d2c6f",
"version": "10.3.0" "version": "10.3.1"
} }
}, },
{ {
@ -163,6 +163,15 @@
"version": "1.0.0" "version": "1.0.0"
} }
}, },
{
"package": "Introspect",
"repositoryURL": "https://github.com/siteline/SwiftUI-Introspect.git",
"state": {
"branch": null,
"revision": "2e09be8af614401bc9f87d40093ec19ce56ccaf2",
"version": "0.1.3"
}
},
{ {
"package": "SwiftyJSON", "package": "SwiftyJSON",
"repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON.git", "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON.git",

View File

@ -34,7 +34,7 @@ extension CategoryPickerSection {
cell.categoryView.titleLabel.textColor = .white cell.categoryView.titleLabel.textColor = .white
} }
} else { } else {
cell.categoryView.bgView.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color cell.categoryView.bgView.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
cell.categoryView.bgView.applyShadow(color: Asset.Colors.brandBlue.color, alpha: 0, x: 0, y: 0, blur: 0.0) cell.categoryView.bgView.applyShadow(color: Asset.Colors.brandBlue.color, alpha: 0, x: 0, y: 0, blur: 0.0)
if case .all = item { if case .all = item {
cell.categoryView.titleLabel.textColor = Asset.Colors.brandBlue.color cell.categoryView.titleLabel.textColor = Asset.Colors.brandBlue.color

View File

@ -33,16 +33,6 @@ extension SearchResultSection {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SearchResultTableViewCell.self), for: indexPath) as! SearchResultTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SearchResultTableViewCell.self), for: indexPath) as! SearchResultTableViewCell
cell.config(with: tag) cell.config(with: tag)
return cell return cell
// case .hashtagObjectID(let hashtagObjectID):
// let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SearchingTableViewCell.self), for: indexPath) as! SearchingTableViewCell
// let tag = dependency.context.managedObjectContext.object(with: hashtagObjectID) as! Tag
// cell.config(with: tag)
// return cell
// case .accountObjectID(let accountObjectID):
// let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SearchingTableViewCell.self), for: indexPath) as! SearchingTableViewCell
// let user = dependency.context.managedObjectContext.object(with: accountObjectID) as! MastodonUser
// cell.config(with: user)
// return cell
case .status(let statusObjectID, let attribute): case .status(let statusObjectID, let attribute):
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusTableViewCell.self), for: indexPath) as! StatusTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusTableViewCell.self), for: indexPath) as! StatusTableViewCell
if let status = try? dependency.context.managedObjectContext.existingObject(with: statusObjectID) as? Status { if let status = try? dependency.context.managedObjectContext.existingObject(with: statusObjectID) as? Status {
@ -73,8 +63,6 @@ extension SearchResultSection {
cell.loadMoreLabel.isHidden = true cell.loadMoreLabel.isHidden = true
} }
return cell return cell
default:
fatalError()
} // end switch } // end switch
} // end UITableViewDiffableDataSource } // end UITableViewDiffableDataSource
} // end func } // end func

View File

@ -44,14 +44,26 @@ extension NotificationSection {
avatarImageURL: notification.account.avatarImageURL() avatarImageURL: notification.account.avatarImageURL()
) )
) )
cell.actionImageView.image = UIImage(
systemName: notification.notificationType.actionImageName, func createActionImage() -> UIImage? {
withConfiguration: UIImage.SymbolConfiguration( return UIImage(
pointSize: 12, weight: .semibold systemName: notification.notificationType.actionImageName,
) withConfiguration: UIImage.SymbolConfiguration(
)? pointSize: 12, weight: .semibold
.withRenderingMode(.alwaysTemplate) )
.af.imageAspectScaled(toFit: CGSize(width: 14, height: 14)) )?
.withTintColor(.systemBackground)
.af.imageAspectScaled(toFit: CGSize(width: 14, height: 14))
}
cell.actionImageView.image = createActionImage()
cell.traitCollectionDidChange
.receive(on: DispatchQueue.main)
.sink { [weak cell] in
guard let cell = cell else { return }
cell.actionImageView.image = createActionImage()
}
.store(in: &cell.disposeBag)
cell.actionImageView.backgroundColor = notification.notificationType.color cell.actionImageView.backgroundColor = notification.notificationType.color

View File

@ -103,7 +103,7 @@ extension PollSection {
cell.pollOptionView.optionPercentageLabel.isHidden = false cell.pollOptionView.optionPercentageLabel.isHidden = false
cell.pollOptionView.optionPercentageLabel.text = String(Int(100 * percentage)) + "%" cell.pollOptionView.optionPercentageLabel.text = String(Int(100 * percentage)) + "%"
cell.pollOptionView.voteProgressStripView.isHidden = false cell.pollOptionView.voteProgressStripView.isHidden = false
cell.pollOptionView.voteProgressStripView.tintColor = voted ? Asset.Colors.brandBlue.color : Asset.Colors.Background.Poll.disabled.color cell.pollOptionView.voteProgressStripView.tintColor = voted ? Asset.Colors.brandBlue.color : Asset.Colors.Poll.disabled.color
cell.pollOptionView.voteProgressStripView.setProgress(CGFloat(percentage), animated: animated) cell.pollOptionView.voteProgressStripView.setProgress(CGFloat(percentage), animated: animated)
} }
} }

View File

@ -9,6 +9,7 @@ import UIKit
import Foundation import Foundation
import ActiveLabel import ActiveLabel
import os.log import os.log
import MastodonUI
extension ActiveLabel { extension ActiveLabel {
@ -58,7 +59,7 @@ extension ActiveLabel {
} }
extension ActiveLabel { extension ActiveLabel {
func configure(text: String) { public func configure(text: String) {
attributedText = nil attributedText = nil
activeEntities.removeAll() activeEntities.removeAll()
self.text = text self.text = text
@ -69,7 +70,7 @@ extension ActiveLabel {
extension ActiveLabel { extension ActiveLabel {
/// status content /// status content
func configure(content: String, emojiDict: MastodonStatusContent.EmojiDict) { public func configure(content: String, emojiDict: MastodonStatusContent.EmojiDict) {
attributedText = nil attributedText = nil
activeEntities.removeAll() activeEntities.removeAll()
@ -83,7 +84,7 @@ extension ActiveLabel {
} }
} }
func configure(contentParseResult parseResult: MastodonStatusContent.ParseResult?) { public func configure(contentParseResult parseResult: MastodonStatusContent.ParseResult?) {
attributedText = nil attributedText = nil
activeEntities.removeAll() activeEntities.removeAll()
text = parseResult?.trimmed ?? "" text = parseResult?.trimmed ?? ""
@ -92,14 +93,14 @@ extension ActiveLabel {
} }
/// account note /// account note
func configure(note: String, emojiDict: MastodonStatusContent.EmojiDict) { public func configure(note: String, emojiDict: MastodonStatusContent.EmojiDict) {
configure(content: note, emojiDict: emojiDict) configure(content: note, emojiDict: emojiDict)
} }
} }
extension ActiveLabel { extension ActiveLabel {
/// account field /// account field
func configure(field: String, emojiDict: MastodonStatusContent.EmojiDict) { public func configure(field: String, emojiDict: MastodonStatusContent.EmojiDict) {
configure(content: field, emojiDict: emojiDict) configure(content: field, emojiDict: emojiDict)
} }
} }

View File

@ -1,154 +0,0 @@
//
// CGImage.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-3-31.
//
import CoreImage
extension CGImage {
// Reference
// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf
// Luma Y = 0.2126R + 0.7152G + 0.0722B
var brightness: CGFloat? {
let context = CIContext() // default with metal accelerate
let ciImage = CIImage(cgImage: self)
let rec709Image = context.createCGImage(
ciImage,
from: ciImage.extent,
format: .RGBA8,
colorSpace: CGColorSpace(name: CGColorSpace.itur_709) // BT.709 a.k.a Rec.709
)
guard let image = rec709Image,
image.bitsPerPixel == 32,
let data = rec709Image?.dataProvider?.data,
let pointer = CFDataGetBytePtr(data) else { return nil }
let length = CFDataGetLength(data)
guard length > 0 else { return nil }
var luma: CGFloat = 0.0
for i in stride(from: 0, to: length, by: 4) {
let r = pointer[i]
let g = pointer[i + 1]
let b = pointer[i + 2]
let Y = 0.2126 * CGFloat(r) + 0.7152 * CGFloat(g) + 0.0722 * CGFloat(b)
luma += Y
}
luma /= CGFloat(width * height)
return luma
}
}
#if canImport(SwiftUI) && DEBUG
import SwiftUI
import UIKit
class BrightnessView: UIView {
let label = UILabel()
let imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
stackView.distribution = .fillEqually
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(label)
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true
label.textAlignment = .center
label.numberOfLines = 0
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setImage(_ image: UIImage) {
imageView.image = image
guard let brightness = image.cgImage?.brightness,
let style = image.domainLumaCoefficientsStyle else {
label.text = "<nil>"
return
}
let styleDescription: String = {
switch style {
case .light: return "Light"
case .dark: return "Dark"
case .unspecified: fallthrough
@unknown default:
return "Unknown"
}
}()
label.text = styleDescription + "\n" + "\(brightness)"
}
}
struct CGImage_Brightness_Previews: PreviewProvider {
static var previews: some View {
Group {
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .black))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .gray))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .separator))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .red))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .green))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .blue))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
UIViewPreview(width: 375) {
let view = BrightnessView()
view.setImage(.placeholder(color: .secondarySystemGroupedBackground))
return view
}
.previewLayout(.fixed(width: 375, height: 44))
}
}
}
#endif

View File

@ -32,23 +32,6 @@ internal enum Asset {
internal static let plusCircle = ImageAsset(name: "Circles/plus.circle") internal static let plusCircle = ImageAsset(name: "Circles/plus.circle")
} }
internal enum Colors { internal enum Colors {
internal enum Background {
internal enum Poll {
internal static let disabled = ColorAsset(name: "Colors/Background/Poll/disabled")
}
internal static let alertYellow = ColorAsset(name: "Colors/Background/alert.yellow")
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 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")
internal static let systemBackground = ColorAsset(name: "Colors/Background/system.background")
internal static let systemElevatedBackground = ColorAsset(name: "Colors/Background/system.elevated.background")
internal static let systemGroupedBackground = ColorAsset(name: "Colors/Background/system.grouped.background")
internal static let tertiarySystemBackground = ColorAsset(name: "Colors/Background/tertiary.system.background")
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "Colors/Background/tertiary.system.grouped.background")
}
internal enum Border { internal enum Border {
internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll") internal static let composePoll = ColorAsset(name: "Colors/Border/compose.poll")
internal static let notificationStatus = ColorAsset(name: "Colors/Border/notification.status") internal static let notificationStatus = ColorAsset(name: "Colors/Border/notification.status")
@ -73,6 +56,9 @@ internal enum Asset {
internal static let mention = ColorAsset(name: "Colors/Notification/mention") internal static let mention = ColorAsset(name: "Colors/Notification/mention")
internal static let reblog = ColorAsset(name: "Colors/Notification/reblog") internal static let reblog = ColorAsset(name: "Colors/Notification/reblog")
} }
internal enum Poll {
internal static let disabled = ColorAsset(name: "Colors/Poll/disabled")
}
internal enum Shadow { internal enum Shadow {
internal static let searchCard = ColorAsset(name: "Colors/Shadow/SearchCard") internal static let searchCard = ColorAsset(name: "Colors/Shadow/SearchCard")
} }
@ -87,12 +73,15 @@ internal enum Asset {
internal static let invalid = ColorAsset(name: "Colors/TextField/invalid") internal static let invalid = ColorAsset(name: "Colors/TextField/invalid")
internal static let valid = ColorAsset(name: "Colors/TextField/valid") internal static let valid = ColorAsset(name: "Colors/TextField/valid")
} }
internal static let alertYellow = ColorAsset(name: "Colors/alert.yellow")
internal static let battleshipGrey = ColorAsset(name: "Colors/battleshipGrey") internal static let battleshipGrey = ColorAsset(name: "Colors/battleshipGrey")
internal static let brandBlue = ColorAsset(name: "Colors/brand.blue") internal static let brandBlue = ColorAsset(name: "Colors/brand.blue")
internal static let brandBlueDarken20 = ColorAsset(name: "Colors/brand.blue.darken.20") internal static let brandBlueDarken20 = ColorAsset(name: "Colors/brand.blue.darken.20")
internal static let dangerBorder = ColorAsset(name: "Colors/danger.border")
internal static let danger = ColorAsset(name: "Colors/danger") internal static let danger = ColorAsset(name: "Colors/danger")
internal static let disabled = ColorAsset(name: "Colors/disabled") internal static let disabled = ColorAsset(name: "Colors/disabled")
internal static let inactive = ColorAsset(name: "Colors/inactive") internal static let inactive = ColorAsset(name: "Colors/inactive")
internal static let mediaTypeIndicotor = ColorAsset(name: "Colors/media.type.indicotor")
internal static let successGreen = ColorAsset(name: "Colors/success.green") internal static let successGreen = ColorAsset(name: "Colors/success.green")
internal static let systemOrange = ColorAsset(name: "Colors/system.orange") internal static let systemOrange = ColorAsset(name: "Colors/system.orange")
} }
@ -103,10 +92,6 @@ internal enum Asset {
internal static let faceSmilingAdaptive = ImageAsset(name: "Human/face.smiling.adaptive") internal static let faceSmilingAdaptive = ImageAsset(name: "Human/face.smiling.adaptive")
} }
internal enum Scene { internal enum Scene {
internal enum Compose {
internal static let background = ColorAsset(name: "Scene/Compose/background")
internal static let toolbarBackground = ColorAsset(name: "Scene/Compose/toolbar.background")
}
internal enum Profile { internal enum Profile {
internal enum Banner { internal enum Banner {
internal static let bioEditBackgroundGray = ColorAsset(name: "Scene/Profile/Banner/bio.edit.background.gray") internal static let bioEditBackgroundGray = ColorAsset(name: "Scene/Profile/Banner/bio.edit.background.gray")
@ -136,6 +121,7 @@ internal enum Asset {
} }
internal enum Theme { internal enum Theme {
internal enum Mastodon { internal enum Mastodon {
internal static let composeToolbarBackground = ColorAsset(name: "Theme/Mastodon/compose.toolbar.background")
internal static let contentWarningOverlayBackground = ColorAsset(name: "Theme/Mastodon/content.warning.overlay.background") internal static let contentWarningOverlayBackground = ColorAsset(name: "Theme/Mastodon/content.warning.overlay.background")
internal static let navigationBarBackground = ColorAsset(name: "Theme/Mastodon/navigation.bar.background") internal static let navigationBarBackground = ColorAsset(name: "Theme/Mastodon/navigation.bar.background")
internal static let profileFieldCollectionViewBackground = ColorAsset(name: "Theme/Mastodon/profile.field.collection.view.background") internal static let profileFieldCollectionViewBackground = ColorAsset(name: "Theme/Mastodon/profile.field.collection.view.background")
@ -153,6 +139,7 @@ internal enum Asset {
internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/Mastodon/tab.bar.item.inactive.icon.color") internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/Mastodon/tab.bar.item.inactive.icon.color")
} }
internal enum System { internal enum System {
internal static let composeToolbarBackground = ColorAsset(name: "Theme/system/compose.toolbar.background")
internal static let contentWarningOverlayBackground = ColorAsset(name: "Theme/system/content.warning.overlay.background") internal static let contentWarningOverlayBackground = ColorAsset(name: "Theme/system/content.warning.overlay.background")
internal static let navigationBarBackground = ColorAsset(name: "Theme/system/navigation.bar.background") internal static let navigationBarBackground = ColorAsset(name: "Theme/system/navigation.bar.background")
internal static let profileFieldCollectionViewBackground = ColorAsset(name: "Theme/system/profile.field.collection.view.background") internal static let profileFieldCollectionViewBackground = ColorAsset(name: "Theme/system/profile.field.collection.view.background")
@ -170,6 +157,23 @@ internal enum Asset {
internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/system/tab.bar.item.inactive.icon.color") internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/system/tab.bar.item.inactive.icon.color")
} }
} }
internal enum Deprecated {
internal enum Background {
internal static let danger = ColorAsset(name: "_Deprecated/Background/danger")
internal static let onboardingBackground = ColorAsset(name: "_Deprecated/Background/onboarding.background")
internal static let secondaryGroupedSystemBackground = ColorAsset(name: "_Deprecated/Background/secondary.grouped.system.background")
internal static let secondarySystemBackground = ColorAsset(name: "_Deprecated/Background/secondary.system.background")
internal static let systemBackground = ColorAsset(name: "_Deprecated/Background/system.background")
internal static let systemElevatedBackground = ColorAsset(name: "_Deprecated/Background/system.elevated.background")
internal static let systemGroupedBackground = ColorAsset(name: "_Deprecated/Background/system.grouped.background")
internal static let tertiarySystemBackground = ColorAsset(name: "_Deprecated/Background/tertiary.system.background")
internal static let tertiarySystemGroupedBackground = ColorAsset(name: "_Deprecated/Background/tertiary.system.grouped.background")
}
internal enum Compose {
internal static let background = ColorAsset(name: "_Deprecated/Compose/background")
internal static let toolbarBackground = ColorAsset(name: "_Deprecated/Compose/toolbar.background")
}
}
} }
// swiftlint:enable identifier_name line_length nesting type_body_length type_name // swiftlint:enable identifier_name line_length nesting type_body_length type_name

View File

@ -58,11 +58,11 @@ internal enum L10n {
internal static let message = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.Message") internal static let message = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.Message")
/// Publish Failure /// Publish Failure
internal static let title = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.Title") internal static let title = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.Title")
internal enum AttchmentsMessage { internal enum AttachmentsMessage {
/// Cannot attach more than one video. /// Cannot attach more than one video.
internal static let moreThanOneVideo = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.AttchmentsMessage.MoreThanOneVideo") internal static let moreThanOneVideo = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.AttachmentsMessage.MoreThanOneVideo")
/// Cannot attach a video to a post that already contains images. /// Cannot attach a video to a post that already contains images.
internal static let videoAttachWithPhoto = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.AttchmentsMessage.VideoAttachWithPhoto") internal static let videoAttachWithPhoto = L10n.tr("Localizable", "Common.Alerts.PublishPostFailure.AttachmentsMessage.VideoAttachWithPhoto")
} }
} }
internal enum SavePhotoFailure { internal enum SavePhotoFailure {
@ -950,7 +950,7 @@ internal enum L10n {
internal static let trueBlackDarkMode = L10n.tr("Localizable", "Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode") internal static let trueBlackDarkMode = L10n.tr("Localizable", "Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode")
} }
internal enum BoringZone { internal enum BoringZone {
/// Account settings /// Account Settings
internal static let accountSettings = L10n.tr("Localizable", "Scene.Settings.Section.BoringZone.AccountSettings") internal static let accountSettings = L10n.tr("Localizable", "Scene.Settings.Section.BoringZone.AccountSettings")
/// Privacy Policy /// Privacy Policy
internal static let privacy = L10n.tr("Localizable", "Scene.Settings.Section.BoringZone.Privacy") internal static let privacy = L10n.tr("Localizable", "Scene.Settings.Section.BoringZone.Privacy")

View File

@ -0,0 +1,17 @@
//
// MastodonAuthenticationBox.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-7-20.
//
import Foundation
import MastodonSDK
import CoreDataStack
struct MastodonAuthenticationBox {
let domain: String
let userID: MastodonUser.ID
let appAuthorization: Mastodon.API.OAuth.Authorization
let userAuthorization: Mastodon.API.OAuth.Authorization
}

View File

@ -5,59 +5,59 @@
// Created by MainasuK Cirno on 2021-3-30. // Created by MainasuK Cirno on 2021-3-30.
// //
import Foundation //import Foundation
import ActiveLabel //import ActiveLabel
//
enum MastodonField { //enum MastodonField {
//
@available(*, deprecated, message: "rely on server meta rendering") // @available(*, deprecated, message: "rely on server meta rendering")
static func parse(field string: String, emojiDict: MastodonStatusContent.EmojiDict) -> ParseResult { // public static func parse(field string: String, emojiDict: MastodonStatusContent.EmojiDict) -> ParseResult {
// use content parser get emoji entities // // use content parser get emoji entities
let value = string // let value = string
//
var string = string // var string = string
var entities: [ActiveEntity] = [] // var entities: [ActiveEntity] = []
//
do { // do {
let contentParseresult = try MastodonStatusContent.parse(content: string, emojiDict: emojiDict) // let contentParseresult = try MastodonStatusContent.parse(content: string, emojiDict: emojiDict)
string = contentParseresult.trimmed // string = contentParseresult.trimmed
entities.append(contentsOf: contentParseresult.activeEntities) // entities.append(contentsOf: contentParseresult.activeEntities)
} catch { // } catch {
// assertionFailure(error.localizedDescription) // // assertionFailure(error.localizedDescription)
} // }
//
let mentionMatches = string.matches(pattern: "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?)") // let mentionMatches = string.matches(pattern: "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?)")
let hashtagMatches = string.matches(pattern: "(?:#([^\\s.]+))") // let hashtagMatches = string.matches(pattern: "(?:#([^\\s.]+))")
let urlMatches = string.matches(pattern: "(?i)https?://\\S+(?:/|\\b)") // let urlMatches = string.matches(pattern: "(?i)https?://\\S+(?:/|\\b)")
//
//
for match in mentionMatches { // for match in mentionMatches {
guard let text = string.substring(with: match, at: 0) else { continue } // guard let text = string.substring(with: match, at: 0) else { continue }
let entity = ActiveEntity(range: match.range, type: .mention(text, userInfo: nil)) // let entity = ActiveEntity(range: match.range, type: .mention(text, userInfo: nil))
entities.append(entity) // entities.append(entity)
} // }
//
for match in hashtagMatches { // for match in hashtagMatches {
guard let text = string.substring(with: match, at: 0) else { continue } // guard let text = string.substring(with: match, at: 0) else { continue }
let entity = ActiveEntity(range: match.range, type: .hashtag(text, userInfo: nil)) // let entity = ActiveEntity(range: match.range, type: .hashtag(text, userInfo: nil))
entities.append(entity) // entities.append(entity)
} // }
//
for match in urlMatches { // for match in urlMatches {
guard let text = string.substring(with: match, at: 0) else { continue } // guard let text = string.substring(with: match, at: 0) else { continue }
let entity = ActiveEntity(range: match.range, type: .url(text, trimmed: text, url: text, userInfo: nil)) // let entity = ActiveEntity(range: match.range, type: .url(text, trimmed: text, url: text, userInfo: nil))
entities.append(entity) // entities.append(entity)
} // }
//
return ParseResult(value: value, trimmed: string, activeEntities: entities) // return ParseResult(value: value, trimmed: string, activeEntities: entities)
} // }
//
} //}
//
extension MastodonField { //extension MastodonField {
struct ParseResult { // public struct ParseResult {
let value: String // let value: String
let trimmed: String // let trimmed: String
let activeEntities: [ActiveEntity] // let activeEntities: [ActiveEntity]
} // }
} //}

View File

@ -7,9 +7,9 @@
import Foundation import Foundation
final class MastodonMetricFormatter: Formatter { final public class MastodonMetricFormatter: Formatter {
func string(from number: Int) -> String? { public func string(from number: Int) -> String? {
let isPositive = number >= 0 let isPositive = number >= 0
let symbol = isPositive ? "" : "-" let symbol = isPositive ? "" : "-"

View File

@ -7,19 +7,19 @@
import Foundation import Foundation
enum MastodonRegex { public enum MastodonRegex {
/// mention, hashtag. /// mention, hashtag.
/// @... /// @...
/// #... /// #...
static let highlightPattern = "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))" public static let highlightPattern = "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))"
/// emoji /// emoji
/// :shortcode: /// :shortcode:
/// accept ^\B: or \s: but not accept \B: to force user input a space to make emoji take effect /// accept ^\B: or \s: but not accept \B: to force user input a space to make emoji take effect
/// precondition :\B with following space /// precondition :\B with following space
static let emojiPattern = "(?:(^\\B:|\\s:)([a-zA-Z0-9_]+)(:\\B(?=\\s)))" public static let emojiPattern = "(?:(^\\B:|\\s:)([a-zA-Z0-9_]+)(:\\B(?=\\s)))"
/// mention, hashtag, emoji /// mention, hashtag, emoji
/// @ /// @
/// # /// #
/// : /// :
static let autoCompletePattern = "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))|(^\\B:|\\s:)([a-zA-Z0-9_]+)" public static let autoCompletePattern = "(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))|(^\\B:|\\s:)([a-zA-Z0-9_]+)"
} }

View File

@ -8,7 +8,7 @@
import UIKit import UIKit
extension MastodonStatusContent { extension MastodonStatusContent {
struct Appearance { public struct Appearance {
let attributes: [NSAttributedString.Key: Any] let attributes: [NSAttributedString.Key: Any]
let urlAttributes: [NSAttributedString.Key: Any] let urlAttributes: [NSAttributedString.Key: Any]
let hashtagAttributes: [NSAttributedString.Key: Any] let hashtagAttributes: [NSAttributedString.Key: Any]

View File

@ -9,20 +9,20 @@ import Foundation
import ActiveLabel import ActiveLabel
extension MastodonStatusContent { extension MastodonStatusContent {
struct ParseResult: Hashable { public struct ParseResult: Hashable {
let document: String public let document: String
let original: String public let original: String
let trimmed: String public let trimmed: String
let activeEntities: [ActiveEntity] public let activeEntities: [ActiveEntity]
static func == (lhs: MastodonStatusContent.ParseResult, rhs: MastodonStatusContent.ParseResult) -> Bool { public static func == (lhs: MastodonStatusContent.ParseResult, rhs: MastodonStatusContent.ParseResult) -> Bool {
return lhs.document == rhs.document return lhs.document == rhs.document
&& lhs.original == rhs.original && lhs.original == rhs.original
&& lhs.trimmed == rhs.trimmed && lhs.trimmed == rhs.trimmed
&& lhs.activeEntities.count == rhs.activeEntities.count // FIXME: && lhs.activeEntities.count == rhs.activeEntities.count // FIXME:
} }
func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
hasher.combine(document) hasher.combine(document)
hasher.combine(original) hasher.combine(original)
hasher.combine(trimmed) hasher.combine(trimmed)
@ -57,7 +57,7 @@ extension ActiveEntityType {
static let appScheme = "mastodon" static let appScheme = "mastodon"
init?(url: URL) { public init?(url: URL) {
guard let scheme = url.scheme?.lowercased() else { return nil } guard let scheme = url.scheme?.lowercased() else { return nil }
guard scheme == ActiveEntityType.appScheme else { guard scheme == ActiveEntityType.appScheme else {
self = .url("", trimmed: "", url: url.absoluteString, userInfo: nil) self = .url("", trimmed: "", url: url.absoluteString, userInfo: nil)
@ -78,7 +78,7 @@ extension ActiveEntityType {
return nil return nil
} }
var uri: URL? { public var uri: URL? {
switch self { switch self {
case .url(_, _, let url, _): case .url(_, _, let url, _):
return URL(string: url) return URL(string: url)

View File

@ -10,14 +10,14 @@ import Combine
import ActiveLabel import ActiveLabel
import Fuzi import Fuzi
enum MastodonStatusContent { public enum MastodonStatusContent {
typealias EmojiShortcode = String public typealias EmojiShortcode = String
typealias EmojiDict = [EmojiShortcode: URL] public typealias EmojiDict = [EmojiShortcode: URL]
static let workingQueue = DispatchQueue(label: "org.joinmastodon.app.ActiveLabel.working-queue", qos: .userInteractive, attributes: .concurrent) static let workingQueue = DispatchQueue(label: "org.joinmastodon.app.ActiveLabel.working-queue", qos: .userInteractive, attributes: .concurrent)
static func parseResult(content: String, emojiDict: MastodonStatusContent.EmojiDict) -> AnyPublisher<MastodonStatusContent.ParseResult?, Never> { public static func parseResult(content: String, emojiDict: MastodonStatusContent.EmojiDict) -> AnyPublisher<MastodonStatusContent.ParseResult?, Never> {
return Future { promise in return Future { promise in
self.workingQueue.async { self.workingQueue.async {
let parseResult = try? MastodonStatusContent.parse(content: content, emojiDict: emojiDict) let parseResult = try? MastodonStatusContent.parse(content: content, emojiDict: emojiDict)
@ -27,7 +27,7 @@ enum MastodonStatusContent {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult { public static func parse(content: String, emojiDict: EmojiDict) throws -> MastodonStatusContent.ParseResult {
let document: String = { let document: String = {
var content = content var content = content
for (shortcode, url) in emojiDict { for (shortcode, url) in emojiDict {

View File

@ -19,7 +19,8 @@ extension UserDefaults {
@objc dynamic var preferredStaticAvatar: Bool { @objc dynamic var preferredStaticAvatar: Bool {
get { get {
register(defaults: [#function: false]) // default false
// without set register to profile timeline performance
return bool(forKey: #function) return bool(forKey: #function)
} }
set { self[#function] = newValue } set { self[#function] = newValue }

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import MastodonExtension
extension UserDefaults { extension UserDefaults {

View File

@ -1,12 +0,0 @@
//
// SplashPreference.swift
// Mastodon
//
// Created by Cirno MainasuK on 2020-2-4.
//
import UIKit
extension UserDefaults {
// TODO: splash scene
}

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import MastodonExtension
extension UserDefaults { extension UserDefaults {

View File

@ -24,11 +24,22 @@ extension AvatarConfigurableView {
public func configure(with configuration: AvatarConfigurableViewConfiguration) { public func configure(with configuration: AvatarConfigurableViewConfiguration) {
let placeholderImage: UIImage = { let placeholderImage: UIImage = {
guard let placeholderImage = configuration.placeholderImage else { guard let placeholderImage = configuration.placeholderImage else {
#if APP_EXTENSION
let placeholderImage = configuration.placeholderImage ?? UIImage.placeholder(size: Self.configurableAvatarImageSize, color: .systemFill)
if Self.configurableAvatarImageCornerRadius < Self.configurableAvatarImageSize.width * 0.5 {
return placeholderImage
.af.imageAspectScaled(toFill: Self.configurableAvatarImageSize)
.af.imageRounded(withCornerRadius: Self.configurableAvatarImageCornerRadius, divideRadiusByImageScale: false)
} else {
return placeholderImage.af.imageRoundedIntoCircle()
}
#else
return AppContext.shared.placeholderImageCacheService.image( return AppContext.shared.placeholderImageCacheService.image(
color: .systemFill, color: .systemFill,
size: Self.configurableAvatarImageSize, size: Self.configurableAvatarImageSize,
cornerRadius: Self.configurableAvatarImageCornerRadius cornerRadius: Self.configurableAvatarImageCornerRadius
) )
#endif
} }
return placeholderImage return placeholderImage
}() }()
@ -115,7 +126,7 @@ extension AvatarConfigurableView {
} }
struct AvatarConfigurableViewConfiguration { struct AvatarConfigurableViewConfiguration {
let avatarImageURL: URL? let avatarImageURL: URL?
let placeholderImage: UIImage? let placeholderImage: UIImage?
let borderColor: UIColor? let borderColor: UIColor?

View File

@ -525,7 +525,10 @@ extension StatusProviderFacade {
.sink { [weak provider] status in .sink { [weak provider] status in
guard let provider = provider else { return } guard let provider = provider else { return }
guard let status = status?.reblog ?? status else { return } guard let status = status?.reblog ?? status else { return }
let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()
let composeViewModel = ComposeViewModel(context: provider.context, composeKind: .reply(repliedToStatusObjectID: status.objectID)) let composeViewModel = ComposeViewModel(context: provider.context, composeKind: .reply(repliedToStatusObjectID: status.objectID))
provider.coordinator.present(scene: .compose(viewModel: composeViewModel), from: provider, transition: .modal(animated: true, completion: nil)) provider.coordinator.present(scene: .compose(viewModel: composeViewModel), from: provider, transition: .modal(animated: true, completion: nil))
} }

View File

@ -49,7 +49,7 @@ extension UserProviderFacade {
private static func _toggleUserFollowRelationship( private static func _toggleUserFollowRelationship(
context: AppContext, context: AppContext,
activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox, activeMastodonAuthenticationBox: MastodonAuthenticationBox,
mastodonUser: AnyPublisher<MastodonUser?, Never> mastodonUser: AnyPublisher<MastodonUser?, Never>
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
mastodonUser mastodonUser
@ -111,7 +111,7 @@ extension UserProviderFacade {
private static func _toggleUserBlockRelationship( private static func _toggleUserBlockRelationship(
context: AppContext, context: AppContext,
activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox, activeMastodonAuthenticationBox: MastodonAuthenticationBox,
mastodonUser: AnyPublisher<MastodonUser?, Never> mastodonUser: AnyPublisher<MastodonUser?, Never>
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
mastodonUser mastodonUser
@ -174,7 +174,7 @@ extension UserProviderFacade {
private static func _toggleUserMuteRelationship( private static func _toggleUserMuteRelationship(
context: AppContext, context: AppContext,
activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox, activeMastodonAuthenticationBox: MastodonAuthenticationBox,
mastodonUser: AnyPublisher<MastodonUser?, Never> mastodonUser: AnyPublisher<MastodonUser?, Never>
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
mastodonUser mastodonUser

View File

@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE8",
"green" : "0xE1",
"red" : "0xD9"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "34",
"green" : "27",
"red" : "25"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFE",
"green" : "0xFF",
"red" : "0xFE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "55",
"green" : "44",
"red" : "40"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE8",
"green" : "0xE1",
"red" : "0xD9"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "67",
"green" : "53",
"red" : "49"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "90", "blue" : "0.259",
"green" : "64", "green" : "0.180",
"red" : "223" "red" : "0.639"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "0.600", "alpha" : "0.600",
"blue" : "0", "blue" : "0.000",
"green" : "0", "green" : "0.000",
"red" : "0" "red" : "0.000"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "222",
"green" : "216",
"red" : "214"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "32",
"green" : "32",
"red" : "32"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "254", "blue" : "0.871",
"green" : "255", "green" : "0.847",
"red" : "254" "red" : "0.839"
} }
}, },
"idiom" : "universal" "idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "67", "blue" : "0.263",
"green" : "53", "green" : "0.208",
"red" : "49" "red" : "0.192"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.871",
"green" : "0.847",
"red" : "0.839"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.920",
"blue" : "0.125",
"green" : "0.125",
"red" : "0.125"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "232", "blue" : "0.353",
"green" : "225", "green" : "0.251",
"red" : "217" "red" : "0.875"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "66", "blue" : "0.910",
"green" : "46", "green" : "0.882",
"red" : "163" "red" : "0.851"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "232", "blue" : "0.996",
"green" : "225", "green" : "1.000",
"red" : "217" "red" : "0.996"
} }
}, },
"idiom" : "universal" "idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "34", "blue" : "0.263",
"green" : "27", "green" : "0.208",
"red" : "25" "red" : "0.192"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "254", "blue" : "0.910",
"green" : "255", "green" : "0.882",
"red" : "254" "red" : "0.851"
} }
}, },
"idiom" : "universal" "idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0x37", "blue" : "0.133",
"green" : "0x2C", "green" : "0.106",
"red" : "0x28" "red" : "0.098"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.996",
"green" : "1.000",
"red" : "0.996"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.216",
"green" : "0.173",
"red" : "0.157"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -23,9 +23,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "55", "blue" : "0.216",
"green" : "44", "green" : "0.173",
"red" : "40" "red" : "0.157"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.910",
"green" : "0.882",
"red" : "0.851"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.133",
"green" : "0.106",
"red" : "0.098"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.996",
"green" : "1.000",
"red" : "0.996"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.216",
"green" : "0.173",
"red" : "0.157"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.910",
"green" : "0.882",
"red" : "0.851"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.263",
"green" : "0.208",
"red" : "0.192"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -23,9 +23,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0x37", "blue" : "0.216",
"green" : "0x2C", "green" : "0.173",
"red" : "0x28" "red" : "0.157"
} }
}, },
"idiom" : "universal" "idiom" : "universal"

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.871",
"green" : "0.847",
"red" : "0.839"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.920",
"blue" : "0.125",
"green" : "0.125",
"red" : "0.125"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -10,8 +10,8 @@
"Common.Alerts.DiscardPostContent.Title" = "Discard Draft"; "Common.Alerts.DiscardPostContent.Title" = "Discard Draft";
"Common.Alerts.EditProfileFailure.Message" = "Cannot edit profile. Please try again."; "Common.Alerts.EditProfileFailure.Message" = "Cannot edit profile. Please try again.";
"Common.Alerts.EditProfileFailure.Title" = "Edit Profile Error"; "Common.Alerts.EditProfileFailure.Title" = "Edit Profile Error";
"Common.Alerts.PublishPostFailure.AttchmentsMessage.MoreThanOneVideo" = "Cannot attach more than one video."; "Common.Alerts.PublishPostFailure.AttachmentsMessage.MoreThanOneVideo" = "Cannot attach more than one video.";
"Common.Alerts.PublishPostFailure.AttchmentsMessage.VideoAttachWithPhoto" = "Cannot attach a video to a post that already contains images."; "Common.Alerts.PublishPostFailure.AttachmentsMessage.VideoAttachWithPhoto" = "Cannot attach a video to a post that already contains images.";
"Common.Alerts.PublishPostFailure.Message" = "Failed to publish the post. "Common.Alerts.PublishPostFailure.Message" = "Failed to publish the post.
Please check your internet connection."; Please check your internet connection.";
"Common.Alerts.PublishPostFailure.Title" = "Publish Failure"; "Common.Alerts.PublishPostFailure.Title" = "Publish Failure";
@ -323,7 +323,7 @@ any server.";
"Scene.Settings.Section.Appearance.Title" = "Appearance"; "Scene.Settings.Section.Appearance.Title" = "Appearance";
"Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable animated avatars"; "Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable animated avatars";
"Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black dark mode"; "Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black dark mode";
"Scene.Settings.Section.BoringZone.AccountSettings" = "Account settings"; "Scene.Settings.Section.BoringZone.AccountSettings" = "Account Settings";
"Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy"; "Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy";
"Scene.Settings.Section.BoringZone.Terms" = "Terms of Service"; "Scene.Settings.Section.BoringZone.Terms" = "Terms of Service";
"Scene.Settings.Section.BoringZone.Title" = "The Boring Zone"; "Scene.Settings.Section.BoringZone.Title" = "The Boring Zone";

View File

@ -10,8 +10,8 @@
"Common.Alerts.DiscardPostContent.Title" = "Discard Draft"; "Common.Alerts.DiscardPostContent.Title" = "Discard Draft";
"Common.Alerts.EditProfileFailure.Message" = "Cannot edit profile. Please try again."; "Common.Alerts.EditProfileFailure.Message" = "Cannot edit profile. Please try again.";
"Common.Alerts.EditProfileFailure.Title" = "Edit Profile Error"; "Common.Alerts.EditProfileFailure.Title" = "Edit Profile Error";
"Common.Alerts.PublishPostFailure.AttchmentsMessage.MoreThanOneVideo" = "Cannot attach more than one video."; "Common.Alerts.PublishPostFailure.AttachmentsMessage.MoreThanOneVideo" = "Cannot attach more than one video.";
"Common.Alerts.PublishPostFailure.AttchmentsMessage.VideoAttachWithPhoto" = "Cannot attach a video to a post that already contains images."; "Common.Alerts.PublishPostFailure.AttachmentsMessage.VideoAttachWithPhoto" = "Cannot attach a video to a post that already contains images.";
"Common.Alerts.PublishPostFailure.Message" = "Failed to publish the post. "Common.Alerts.PublishPostFailure.Message" = "Failed to publish the post.
Please check your internet connection."; Please check your internet connection.";
"Common.Alerts.PublishPostFailure.Title" = "Publish Failure"; "Common.Alerts.PublishPostFailure.Title" = "Publish Failure";
@ -323,7 +323,7 @@ any server.";
"Scene.Settings.Section.Appearance.Title" = "Appearance"; "Scene.Settings.Section.Appearance.Title" = "Appearance";
"Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable animated avatars"; "Scene.Settings.Section.AppearanceSettings.DisableAvatarAnimation" = "Disable animated avatars";
"Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black dark mode"; "Scene.Settings.Section.AppearanceSettings.TrueBlackDarkMode" = "True black dark mode";
"Scene.Settings.Section.BoringZone.AccountSettings" = "Account settings"; "Scene.Settings.Section.BoringZone.AccountSettings" = "Account Settings";
"Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy"; "Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy";
"Scene.Settings.Section.BoringZone.Terms" = "Terms of Service"; "Scene.Settings.Section.BoringZone.Terms" = "Terms of Service";
"Scene.Settings.Section.BoringZone.Title" = "The Boring Zone"; "Scene.Settings.Section.BoringZone.Title" = "The Boring Zone";

View File

@ -6,8 +6,11 @@
// //
import UIKit import UIKit
import Combine
final class AutoCompleteTopChevronView: UIView { final class AutoCompleteTopChevronView: UIView {
var disposeBag = Set<AnyCancellable>()
static let chevronSize = CGSize(width: 20, height: 12) static let chevronSize = CGSize(width: 20, height: 12)
@ -16,10 +19,10 @@ final class AutoCompleteTopChevronView: UIView {
private let maskLayer = CAShapeLayer() private let maskLayer = CAShapeLayer()
var chevronMinX: CGFloat = 0 var chevronMinX: CGFloat = 0
var topViewBackgroundColor = Asset.Scene.Compose.background.color { var topViewBackgroundColor = ThemeService.shared.currentTheme.value.systemElevatedBackgroundColor {
didSet { setNeedsLayout() } didSet { setNeedsLayout() }
} }
var bottomViewBackgroundColor = Asset.Colors.Background.systemBackground.color { var bottomViewBackgroundColor = ThemeService.shared.currentTheme.value.systemBackgroundColor {
didSet { setNeedsLayout() } didSet { setNeedsLayout() }
} }
@ -70,6 +73,15 @@ extension AutoCompleteTopChevronView {
shadowLayer.fillColor = topViewBackgroundColor.cgColor shadowLayer.fillColor = topViewBackgroundColor.cgColor
shadowView.layer.addSublayer(shadowLayer) shadowView.layer.addSublayer(shadowLayer)
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: DispatchQueue.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
} }
override func layoutSubviews() { override func layoutSubviews() {
@ -114,6 +126,13 @@ extension AutoCompleteTopChevronView {
} }
extension AutoCompleteTopChevronView {
private func setupBackgroundColor(theme: Theme) {
topViewBackgroundColor = theme.systemElevatedBackgroundColor
bottomViewBackgroundColor = theme.systemBackgroundColor
}
}
extension AutoCompleteTopChevronView { extension AutoCompleteTopChevronView {
func invertMask(in rect: CGRect) -> CAShapeLayer { func invertMask(in rect: CGRect) -> CAShapeLayer {
let path = UIBezierPath() let path = UIBezierPath()
@ -153,7 +172,7 @@ struct AutoCompleteTopChevronView_Previews: PreviewProvider {
view.chevronMinX = 10 view.chevronMinX = 10
return view return view
} }
.background(Color(Asset.Scene.Compose.background.color)) .background(Color(ThemeService.shared.currentTheme.value.systemElevatedBackgroundColor))
.padding(20) .padding(20)
.previewLayout(.fixed(width: 375 + 40, height: 100 + 40)) .previewLayout(.fixed(width: 375 + 40, height: 100 + 40))
UIViewPreview(width: 375) { UIViewPreview(width: 375) {
@ -166,7 +185,7 @@ struct AutoCompleteTopChevronView_Previews: PreviewProvider {
view.chevronMinX = 10 view.chevronMinX = 10
return view return view
} }
.background(Color(Asset.Scene.Compose.background.color)) .background(Color(ThemeService.shared.currentTheme.value.systemElevatedBackgroundColor))
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
.padding(20) .padding(20)
.previewLayout(.fixed(width: 375 + 40, height: 100 + 40)) .previewLayout(.fixed(width: 375 + 40, height: 100 + 40))

View File

@ -8,13 +8,16 @@
import os.log import os.log
import UIKit import UIKit
import Combine import Combine
import MastodonUI
protocol ComposeStatusAttachmentCollectionViewCellDelegate: AnyObject { protocol ComposeStatusAttachmentCollectionViewCellDelegate: AnyObject {
func composeStatusAttachmentCollectionViewCell(_ cell: ComposeStatusAttachmentCollectionViewCell, removeButtonDidPressed button: UIButton) func composeStatusAttachmentCollectionViewCell(_ cell: ComposeStatusAttachmentCollectionViewCell, removeButtonDidPressed button: UIButton)
} }
final class ComposeStatusAttachmentCollectionViewCell: UICollectionViewCell { final class ComposeStatusAttachmentCollectionViewCell: UICollectionViewCell {
let logger = Logger(subsystem: "ComposeStatusAttachmentCollectionViewCell", category: "UI")
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
static let verticalMarginHeight: CGFloat = ComposeStatusAttachmentCollectionViewCell.removeButtonSize.height * 0.5 static let verticalMarginHeight: CGFloat = ComposeStatusAttachmentCollectionViewCell.removeButtonSize.height * 0.5
@ -29,10 +32,10 @@ final class ComposeStatusAttachmentCollectionViewCell: UICollectionViewCell {
let image = UIImage(systemName: "minus")!.withConfiguration(UIImage.SymbolConfiguration(pointSize: 14, weight: .bold)) let image = UIImage(systemName: "minus")!.withConfiguration(UIImage.SymbolConfiguration(pointSize: 14, weight: .bold))
button.tintColor = .white button.tintColor = .white
button.setImage(image, for: .normal) button.setImage(image, for: .normal)
button.setBackgroundImage(.placeholder(color: Asset.Colors.Background.danger.color), for: .normal) button.setBackgroundImage(.placeholder(color: Asset.Colors.danger.color), for: .normal)
button.layer.masksToBounds = true button.layer.masksToBounds = true
button.layer.cornerRadius = ComposeStatusAttachmentCollectionViewCell.removeButtonSize.width * 0.5 button.layer.cornerRadius = ComposeStatusAttachmentCollectionViewCell.removeButtonSize.width * 0.5
button.layer.borderColor = Asset.Colors.Background.dangerBorder.color.cgColor button.layer.borderColor = Asset.Colors.dangerBorder.color.cgColor
button.layer.borderWidth = 1 button.layer.borderWidth = 1
return button return button
}() }()
@ -58,7 +61,7 @@ final class ComposeStatusAttachmentCollectionViewCell: UICollectionViewCell {
} }
deinit { deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
} }
} }
@ -96,7 +99,7 @@ extension ComposeStatusAttachmentCollectionViewCell {
extension ComposeStatusAttachmentCollectionViewCell { extension ComposeStatusAttachmentCollectionViewCell {
@objc private func removeButtonDidPressed(_ sender: UIButton) { @objc private func removeButtonDidPressed(_ sender: UIButton) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
delegate?.composeStatusAttachmentCollectionViewCell(self, removeButtonDidPressed: sender) delegate?.composeStatusAttachmentCollectionViewCell(self, removeButtonDidPressed: sender)
} }

View File

@ -5,14 +5,16 @@
// Created by MainasuK Cirno on 2021-6-28. // Created by MainasuK Cirno on 2021-6-28.
// //
import os.log import os.log
import UIKit import UIKit
import Combine import Combine
import MetaTextView import MetaTextView
import UITextView_Placeholder
final class ComposeStatusContentTableViewCell: UITableViewCell { final class ComposeStatusContentTableViewCell: UITableViewCell {
let logger = Logger(subsystem: "ComposeStatusContentTableViewCell", category: "UI")
var disposeBag = Set<AnyCancellable>() var disposeBag = Set<AnyCancellable>()
let statusView = ReplicaStatusView() let statusView = ReplicaStatusView()
@ -149,7 +151,7 @@ extension ComposeStatusContentTableViewCell: UITextViewDelegate {
} }
func textViewDidChange(_ textView: UITextView) { func textViewDidChange(_ textView: UITextView) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: text: %s", ((#file as NSString).lastPathComponent), #line, #function, textView.text) logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): text: \(textView.text ?? "<nil>")")
guard textView === statusContentWarningEditorView.textView else { return } guard textView === statusContentWarningEditorView.textView else { return }
// replace line break with space // replace line break with space
textView.text = textView.text.replacingOccurrences(of: "\n", with: " ") textView.text = textView.text.replacingOccurrences(of: "\n", with: " ")

View File

@ -14,6 +14,7 @@ import MetaTextView
import MastodonMeta import MastodonMeta
import Meta import Meta
import Nuke import Nuke
import MastodonUI
final class ComposeViewController: UIViewController, NeedsDependency { final class ComposeViewController: UIViewController, NeedsDependency {
@ -188,7 +189,7 @@ extension ComposeViewController {
]) ])
tableView.delegate = self tableView.delegate = self
viewModel.setupDiffableDataSource( viewModel.setupDataSource(
tableView: tableView, tableView: tableView,
metaTextDelegate: self, metaTextDelegate: self,
metaTextViewDelegate: self, metaTextViewDelegate: self,
@ -263,7 +264,6 @@ extension ComposeViewController {
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
} }
} }
self.updateKeyboardBackground(isKeyboardDisplay: isShow)
return return
} }
// isShow AND dock state // isShow AND dock state
@ -279,14 +279,12 @@ extension ComposeViewController {
self.autoCompleteViewController.tableView.contentInset.bottom = autoCompleteTableViewBottomInset self.autoCompleteViewController.tableView.contentInset.bottom = autoCompleteTableViewBottomInset
self.autoCompleteViewController.tableView.verticalScrollIndicatorInsets.bottom = autoCompleteTableViewBottomInset self.autoCompleteViewController.tableView.verticalScrollIndicatorInsets.bottom = autoCompleteTableViewBottomInset
// adjust inset for collectionView // adjust inset for tableView
let contentFrame = self.view.convert(self.tableView.frame, to: nil) let contentFrame = self.view.convert(self.tableView.frame, to: nil)
let padding = contentFrame.maxY + extraMargin - endFrame.minY let padding = contentFrame.maxY + extraMargin - endFrame.minY
guard padding > 0 else { guard padding > 0 else {
self.tableView.contentInset.bottom = self.view.safeAreaInsets.bottom + extraMargin self.tableView.contentInset.bottom = self.view.safeAreaInsets.bottom + extraMargin
self.tableView.verticalScrollIndicatorInsets.bottom = self.view.safeAreaInsets.bottom + extraMargin self.tableView.verticalScrollIndicatorInsets.bottom = self.view.safeAreaInsets.bottom + extraMargin
self.updateKeyboardBackground(isKeyboardDisplay: false)
return return
} }
@ -296,7 +294,6 @@ extension ComposeViewController {
self.composeToolbarViewBottomLayoutConstraint.constant = endFrame.height self.composeToolbarViewBottomLayoutConstraint.constant = endFrame.height
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
} }
self.updateKeyboardBackground(isKeyboardDisplay: isShow)
}) })
.store(in: &disposeBag) .store(in: &disposeBag)
@ -586,14 +583,11 @@ extension ComposeViewController {
imagePicker.delegate = self imagePicker.delegate = self
return imagePicker return imagePicker
} }
private func updateKeyboardBackground(isKeyboardDisplay: Bool) {
composeToolbarBackgroundView.backgroundColor = Asset.Scene.Compose.toolbarBackground.color
}
private func setupBackgroundColor(theme: Theme) { private func setupBackgroundColor(theme: Theme) {
view.backgroundColor = theme.systemElevatedBackgroundColor view.backgroundColor = theme.systemElevatedBackgroundColor
tableView.backgroundColor = theme.systemElevatedBackgroundColor tableView.backgroundColor = theme.systemElevatedBackgroundColor
composeToolbarBackgroundView.backgroundColor = theme.composeToolbarBackgroundColor
} }
} }

View File

@ -16,7 +16,7 @@ import MetaTextView
extension ComposeViewModel { extension ComposeViewModel {
func setupDiffableDataSource( func setupDataSource(
tableView: UITableView, tableView: UITableView,
metaTextDelegate: MetaTextDelegate, metaTextDelegate: MetaTextDelegate,
metaTextViewDelegate: UITextViewDelegate, metaTextViewDelegate: UITextViewDelegate,

View File

@ -28,7 +28,7 @@ final class ComposeViewModel: NSObject {
let isContentWarningComposing = CurrentValueSubject<Bool, Never>(false) let isContentWarningComposing = CurrentValueSubject<Bool, Never>(false)
let selectedStatusVisibility: CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never> let selectedStatusVisibility: CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never> let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never> let activeAuthenticationBox: CurrentValueSubject<MastodonAuthenticationBox?, Never>
let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make initial event emit let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make initial event emit
let repliedToCellFrame = CurrentValueSubject<CGRect, Never>(.zero) let repliedToCellFrame = CurrentValueSubject<CGRect, Never>(.zero)
let autoCompleteRetryLayoutTimes = CurrentValueSubject<Int, Never>(0) let autoCompleteRetryLayoutTimes = CurrentValueSubject<Int, Never>(0)
@ -202,13 +202,13 @@ final class ComposeViewModel: NSObject {
} }
.assign(to: \.value, on: characterCount) .assign(to: \.value, on: characterCount)
.store(in: &disposeBag) .store(in: &disposeBag)
// bind compose bar button item UI state // bind compose bar button item UI state
let isComposeContentEmpty = composeStatusAttribute.composeContent let isComposeContentEmpty = composeStatusAttribute.composeContent
.map { ($0 ?? "").isEmpty } .map { ($0 ?? "").isEmpty }
let isComposeContentValid = composeStatusAttribute.composeContent let isComposeContentValid = characterCount
.map { composeContent -> Bool in .map { characterCount -> Bool in
let composeContent = composeContent ?? "" return characterCount <= ComposeViewModel.composeContentLimit
return composeContent.count <= ComposeViewModel.composeContentLimit
} }
let isMediaEmpty = attachmentServices let isMediaEmpty = attachmentServices
.map { $0.isEmpty } .map { $0.isEmpty }
@ -224,10 +224,10 @@ final class ComposeViewModel: NSObject {
} }
let isPublishBarButtonItemEnabledPrecondition1 = Publishers.CombineLatest4( let isPublishBarButtonItemEnabledPrecondition1 = Publishers.CombineLatest4(
isComposeContentEmpty.eraseToAnyPublisher(), isComposeContentEmpty,
isComposeContentValid.eraseToAnyPublisher(), isComposeContentValid,
isMediaEmpty.eraseToAnyPublisher(), isMediaEmpty,
isMediaUploadAllSuccess.eraseToAnyPublisher() isMediaUploadAllSuccess
) )
.map { isComposeContentEmpty, isComposeContentValid, isMediaEmpty, isMediaUploadAllSuccess -> Bool in .map { isComposeContentEmpty, isComposeContentValid, isMediaEmpty, isMediaUploadAllSuccess -> Bool in
if isMediaEmpty { if isMediaEmpty {
@ -239,10 +239,10 @@ final class ComposeViewModel: NSObject {
.eraseToAnyPublisher() .eraseToAnyPublisher()
let isPublishBarButtonItemEnabledPrecondition2 = Publishers.CombineLatest4( let isPublishBarButtonItemEnabledPrecondition2 = Publishers.CombineLatest4(
isComposeContentEmpty.eraseToAnyPublisher(), isComposeContentEmpty,
isComposeContentValid.eraseToAnyPublisher(), isComposeContentValid,
isPollComposing.eraseToAnyPublisher(), isPollComposing,
isPollAttributeAllValid.eraseToAnyPublisher() isPollAttributeAllValid
) )
.map { isComposeContentEmpty, isComposeContentValid, isPollComposing, isPollAttributeAllValid -> Bool in .map { isComposeContentEmpty, isComposeContentValid, isPollComposing, isPollAttributeAllValid -> Bool in
if isPollComposing { if isPollComposing {
@ -390,9 +390,9 @@ extension ComposeViewModel {
var failureReason: String? { var failureReason: String? {
switch self { switch self {
case .videoAttachWithPhoto: case .videoAttachWithPhoto:
return L10n.Common.Alerts.PublishPostFailure.AttchmentsMessage.videoAttachWithPhoto return L10n.Common.Alerts.PublishPostFailure.AttachmentsMessage.videoAttachWithPhoto
case .moreThanOneVideo: case .moreThanOneVideo:
return L10n.Common.Alerts.PublishPostFailure.AttchmentsMessage.moreThanOneVideo return L10n.Common.Alerts.PublishPostFailure.AttachmentsMessage.moreThanOneVideo
} }
} }
} }

View File

@ -88,7 +88,7 @@ extension ComposeStatusAttachmentTableViewCell {
guard let image = thumbnailImage else { guard let image = thumbnailImage else {
let placeholder = UIImage.placeholder( let placeholder = UIImage.placeholder(
size: size, size: size,
color: Asset.Colors.Background.systemGroupedBackground.color color: ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
) )
.af.imageRounded( .af.imageRounded(
withCornerRadius: AttachmentContainerView.containerViewCornerRadius withCornerRadius: AttachmentContainerView.containerViewCornerRadius
@ -116,9 +116,11 @@ extension ComposeStatusAttachmentTableViewCell {
} else { } else {
guard let uploadState = uploadState else { return } guard let uploadState = uploadState else { return }
switch uploadState { switch uploadState {
case is MastodonAttachmentService.UploadState.Finish, case is MastodonAttachmentService.UploadState.Finish:
is MastodonAttachmentService.UploadState.Fail:
cell.attachmentContainerView.activityIndicatorView.stopAnimating() cell.attachmentContainerView.activityIndicatorView.stopAnimating()
case is MastodonAttachmentService.UploadState.Fail:
cell.attachmentContainerView.activityIndicatorView.stopAnimating()
// FIXME: not display
cell.attachmentContainerView.emptyStateView.label.text = { cell.attachmentContainerView.emptyStateView.label.text = {
if let file = attachmentService.file.value { if let file = attachmentService.file.value {
switch file { switch file {

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import MastodonUI
extension AttachmentContainerView { extension AttachmentContainerView {
final class EmptyStateView: UIView { final class EmptyStateView: UIView {

View File

@ -96,11 +96,15 @@ final class ComposeToolbarView: UIView {
extension ComposeToolbarView { extension ComposeToolbarView {
private func _init() { private func _init() {
// magic keyboard color (iOS 14): setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
// light with white background: RGB 214 216 222 ThemeService.shared.currentTheme
// dark with black background: RGB 43 43 43 .receive(on: DispatchQueue.main)
backgroundColor = Asset.Scene.Compose.toolbarBackground.color .sink { [weak self] theme in
guard let self = self else { return }
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
let stackView = UIStackView() let stackView = UIStackView()
stackView.axis = .horizontal stackView.axis = .horizontal
stackView.spacing = 0 stackView.spacing = 0
@ -215,6 +219,10 @@ extension ComposeToolbarView {
extension ComposeToolbarView { extension ComposeToolbarView {
private func setupBackgroundColor(theme: Theme) {
backgroundColor = theme.composeToolbarBackgroundColor
}
private static func configureToolbarButtonAppearance(button: UIButton) { private static func configureToolbarButtonAppearance(button: UIButton) {
button.tintColor = Asset.Colors.brandBlue.color button.tintColor = Asset.Colors.brandBlue.color
button.setBackgroundImage(.placeholder(size: ComposeToolbarView.toolbarButtonSize, color: .systemFill), for: .highlighted) button.setBackgroundImage(.placeholder(size: ComposeToolbarView.toolbarButtonSize, color: .systemFill), for: .highlighted)

View File

@ -48,7 +48,7 @@ final class ReplicaStatusView: UIView {
let headerIconLabel: UILabel = { let headerIconLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.attributedText = StatusView.iconAttributedString(image: StatusView.reblogIconImage) label.attributedText = ReplicaStatusView.iconAttributedString(image: ReplicaStatusView.reblogIconImage)
return label return label
}() }()
@ -67,7 +67,6 @@ final class ReplicaStatusView: UIView {
return view return view
}() }()
let avatarImageView: UIImageView = FLAnimatedImageView() let avatarImageView: UIImageView = FLAnimatedImageView()
let avatarStackedContainerButton: AvatarStackContainerButton = AvatarStackContainerButton()
let nameLabel: ActiveLabel = { let nameLabel: ActiveLabel = {
let label = ActiveLabel(style: .statusName) let label = ActiveLabel(style: .statusName)
@ -157,7 +156,7 @@ extension ReplicaStatusView {
headerContainerStackView.topAnchor.constraint(equalTo: headerContainerView.topAnchor), headerContainerStackView.topAnchor.constraint(equalTo: headerContainerView.topAnchor),
headerContainerStackView.leadingAnchor.constraint(equalTo: headerContainerView.leadingAnchor), headerContainerStackView.leadingAnchor.constraint(equalTo: headerContainerView.leadingAnchor),
headerContainerStackView.trailingAnchor.constraint(equalTo: headerContainerView.trailingAnchor), headerContainerStackView.trailingAnchor.constraint(equalTo: headerContainerView.trailingAnchor),
headerContainerView.bottomAnchor.constraint(equalTo: headerContainerStackView.bottomAnchor, constant: StatusView.containerStackViewSpacing).priority(.defaultHigh), headerContainerView.bottomAnchor.constraint(equalTo: headerContainerStackView.bottomAnchor, constant: ReplicaStatusView.containerStackViewSpacing).priority(.defaultHigh),
]) ])
containerStackView.addArrangedSubview(headerContainerView) containerStackView.addArrangedSubview(headerContainerView)
defer { defer {
@ -167,15 +166,15 @@ extension ReplicaStatusView {
// author container: [avatar | author meta container | reveal button] // author container: [avatar | author meta container | reveal button]
let authorContainerStackView = UIStackView() let authorContainerStackView = UIStackView()
authorContainerStackView.axis = .horizontal authorContainerStackView.axis = .horizontal
authorContainerStackView.spacing = StatusView.avatarToLabelSpacing authorContainerStackView.spacing = ReplicaStatusView.avatarToLabelSpacing
authorContainerStackView.distribution = .fill authorContainerStackView.distribution = .fill
// avatar // avatar
avatarView.translatesAutoresizingMaskIntoConstraints = false avatarView.translatesAutoresizingMaskIntoConstraints = false
authorContainerStackView.addArrangedSubview(avatarView) authorContainerStackView.addArrangedSubview(avatarView)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
avatarView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.required - 1), avatarView.widthAnchor.constraint(equalToConstant: ReplicaStatusView.avatarImageSize.width).priority(.required - 1),
avatarView.heightAnchor.constraint(equalToConstant: StatusView.avatarImageSize.height).priority(.required - 1), avatarView.heightAnchor.constraint(equalToConstant: ReplicaStatusView.avatarImageSize.height).priority(.required - 1),
]) ])
avatarImageView.translatesAutoresizingMaskIntoConstraints = false avatarImageView.translatesAutoresizingMaskIntoConstraints = false
avatarView.addSubview(avatarImageView) avatarView.addSubview(avatarImageView)
@ -185,14 +184,6 @@ extension ReplicaStatusView {
avatarImageView.trailingAnchor.constraint(equalTo: avatarView.trailingAnchor), avatarImageView.trailingAnchor.constraint(equalTo: avatarView.trailingAnchor),
avatarImageView.bottomAnchor.constraint(equalTo: avatarView.bottomAnchor), avatarImageView.bottomAnchor.constraint(equalTo: avatarView.bottomAnchor),
]) ])
avatarStackedContainerButton.translatesAutoresizingMaskIntoConstraints = false
avatarView.addSubview(avatarStackedContainerButton)
NSLayoutConstraint.activate([
avatarStackedContainerButton.topAnchor.constraint(equalTo: avatarView.topAnchor),
avatarStackedContainerButton.leadingAnchor.constraint(equalTo: avatarView.leadingAnchor),
avatarStackedContainerButton.trailingAnchor.constraint(equalTo: avatarView.trailingAnchor),
avatarStackedContainerButton.bottomAnchor.constraint(equalTo: avatarView.bottomAnchor),
])
// author meta container: [title container | subtitle container] // author meta container: [title container | subtitle container]
let authorMetaContainerStackView = UIStackView() let authorMetaContainerStackView = UIStackView()
@ -235,7 +226,7 @@ extension ReplicaStatusView {
authorContainerStackView.topAnchor.constraint(equalTo: authorContainerView.topAnchor), authorContainerStackView.topAnchor.constraint(equalTo: authorContainerView.topAnchor),
authorContainerStackView.leadingAnchor.constraint(equalTo: authorContainerView.leadingAnchor), authorContainerStackView.leadingAnchor.constraint(equalTo: authorContainerView.leadingAnchor),
authorContainerStackView.trailingAnchor.constraint(equalTo: authorContainerView.trailingAnchor), authorContainerStackView.trailingAnchor.constraint(equalTo: authorContainerView.trailingAnchor),
authorContainerView.bottomAnchor.constraint(equalTo: authorContainerStackView.bottomAnchor, constant: StatusView.containerStackViewSpacing).priority(.defaultHigh), authorContainerView.bottomAnchor.constraint(equalTo: authorContainerStackView.bottomAnchor, constant: ReplicaStatusView.containerStackViewSpacing).priority(.defaultHigh),
]) ])
containerStackView.addArrangedSubview(authorContainerView) containerStackView.addArrangedSubview(authorContainerView)
@ -252,8 +243,6 @@ extension ReplicaStatusView {
// status // status
statusContainerStackView.addArrangedSubview(contentMetaText.textView) statusContainerStackView.addArrangedSubview(contentMetaText.textView)
contentMetaText.textView.setContentCompressionResistancePriority(.required - 1, for: .vertical) contentMetaText.textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
avatarStackedContainerButton.isHidden = true
} }
} }

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import MastodonUI
final class StatusContentWarningEditorView: UIView { final class StatusContentWarningEditorView: UIView {
@ -72,28 +73,6 @@ extension StatusContentWarningEditorView {
containerStackView.addArrangedSubview(iconImageView) containerStackView.addArrangedSubview(iconImageView)
iconImageView.setContentHuggingPriority(.required - 1, for: .horizontal) iconImageView.setContentHuggingPriority(.required - 1, for: .horizontal)
containerStackView.addArrangedSubview(textView) 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

@ -5,13 +5,15 @@
// Created by MainasuK Cirno on 2021-2-5. // Created by MainasuK Cirno on 2021-2-5.
// //
#if DEBUG
import os.log import os.log
import UIKit import UIKit
import CoreData import CoreData
import CoreDataStack import CoreDataStack
#if DEBUG
import FLEX import FLEX
import SwiftUI
import MastodonUI
extension HomeTimelineViewController { extension HomeTimelineViewController {
var debugMenu: UIMenu { var debugMenu: UIMenu {
@ -363,5 +365,6 @@ extension HomeTimelineViewController {
transition: .modal(animated: true, completion: nil) transition: .modal(animated: true, completion: nil)
) )
} }
} }
#endif #endif

View File

@ -7,6 +7,7 @@
import os.log import os.log
import UIKit import UIKit
import MastodonUI
protocol HomeTimelineNavigationBarTitleViewDelegate: AnyObject { protocol HomeTimelineNavigationBarTitleViewDelegate: AnyObject {
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, logoButtonDidPressed sender: UIButton) func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, logoButtonDidPressed sender: UIButton)
@ -113,7 +114,7 @@ extension HomeTimelineNavigationBarTitleView {
configureButton( configureButton(
title: L10n.Scene.HomeTimeline.NavigationBarState.offline, title: L10n.Scene.HomeTimeline.NavigationBarState.offline,
textColor: .white, textColor: .white,
backgroundColor: Asset.Colors.Background.danger.color backgroundColor: Asset.Colors.danger.color
) )
button.isHidden = false button.isHidden = false
case .publishingPostLabel: case .publishingPostLabel:

View File

@ -42,6 +42,15 @@ class MainTabBarController: UITabBarController {
case .me: return UIImage(systemName: "person.fill")! case .me: return UIImage(systemName: "person.fill")!
} }
} }
var largeImage: UIImage {
switch self {
case .home: return UIImage(systemName: "house.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 80))!
case .search: return UIImage(systemName: "magnifyingglass", withConfiguration: UIImage.SymbolConfiguration(pointSize: 80))!
case .notification: return UIImage(systemName: "bell.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 80))!
case .me: return UIImage(systemName: "person.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 80))!
}
}
func viewController(context: AppContext, coordinator: SceneCoordinator) -> UIViewController { func viewController(context: AppContext, coordinator: SceneCoordinator) -> UIViewController {
let viewController: UIViewController let viewController: UIViewController
@ -112,13 +121,19 @@ extension MainTabBarController {
let tabs = Tab.allCases let tabs = Tab.allCases
let viewControllers: [UIViewController] = tabs.map { tab in let viewControllers: [UIViewController] = tabs.map { tab in
let viewController = tab.viewController(context: context, coordinator: coordinator) let viewController = tab.viewController(context: context, coordinator: coordinator)
viewController.tabBarItem.title = "" // set text to empty string for image only style (SDK failed to layout when set to nil) viewController.tabBarItem.title = tab.title
viewController.tabBarItem.image = tab.image viewController.tabBarItem.image = tab.image
viewController.tabBarItem.accessibilityLabel = tab.title viewController.tabBarItem.accessibilityLabel = tab.title
viewController.tabBarItem.largeContentSizeImage = tab.largeImage
viewController.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
return viewController return viewController
} }
setViewControllers(viewControllers, animated: false) setViewControllers(viewControllers, animated: false)
selectedIndex = 0 selectedIndex = 0
UITabBarItem.appearance().setTitleTextAttributes([.foregroundColor : UIColor.clear], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([.foregroundColor : UIColor.clear], for: .highlighted)
UITabBarItem.appearance().setTitleTextAttributes([.foregroundColor : UIColor.clear], for: .selected)
context.apiService.error context.apiService.error
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)

View File

@ -26,7 +26,7 @@ final class NotificationViewModel: NSObject {
let selectedIndex = CurrentValueSubject<NotificationSegment, Never>(.EveryThing) let selectedIndex = CurrentValueSubject<NotificationSegment, Never>(.EveryThing)
let noMoreNotification = CurrentValueSubject<Bool, Never>(false) let noMoreNotification = CurrentValueSubject<Bool, Never>(false)
let activeMastodonAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never> let activeMastodonAuthenticationBox: CurrentValueSubject<MastodonAuthenticationBox?, Never>
let fetchedResultsController: NSFetchedResultsController<MastodonNotification>! let fetchedResultsController: NSFetchedResultsController<MastodonNotification>!
let notificationPredicate = CurrentValueSubject<NSPredicate?, Never>(nil) let notificationPredicate = CurrentValueSubject<NSPredicate?, Never>(nil)
let cellFrameCache = NSCache<NSString, NSValue>() let cellFrameCache = NSCache<NSString, NSValue>()

View File

@ -50,17 +50,18 @@ final class NotificationStatusTableViewCell: UITableViewCell, StatusCell {
let imageView = FLAnimatedImageView() let imageView = FLAnimatedImageView()
return imageView return imageView
}() }()
let traitCollectionDidChange = PassthroughSubject<Void, Never>()
let actionImageView: UIImageView = { let actionImageView: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
imageView.contentMode = .center imageView.contentMode = .center
imageView.tintColor = Asset.Colors.Background.systemBackground.color
imageView.isOpaque = true imageView.isOpaque = true
imageView.layer.masksToBounds = true imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = NotificationStatusTableViewCell.actionImageViewSize.width * 0.5 imageView.layer.cornerRadius = NotificationStatusTableViewCell.actionImageViewSize.width * 0.5
imageView.layer.cornerCurve = .circular imageView.layer.cornerCurve = .circular
imageView.layer.borderWidth = NotificationStatusTableViewCell.actionImageBorderWidth imageView.layer.borderWidth = NotificationStatusTableViewCell.actionImageBorderWidth
imageView.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor
imageView.layer.shouldRasterize = true imageView.layer.shouldRasterize = true
imageView.layer.rasterizationScale = UIScreen.main.scale imageView.layer.rasterizationScale = UIScreen.main.scale
return imageView return imageView
@ -197,8 +198,8 @@ extension NotificationStatusTableViewCell {
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
actionImageView.centerYAnchor.constraint(equalTo: avatarContainer.bottomAnchor), actionImageView.centerYAnchor.constraint(equalTo: avatarContainer.bottomAnchor),
actionImageView.centerXAnchor.constraint(equalTo: avatarContainer.trailingAnchor), actionImageView.centerXAnchor.constraint(equalTo: avatarContainer.trailingAnchor),
actionImageView.widthAnchor.constraint(equalToConstant: NotificationStatusTableViewCell.actionImageViewSize.width), actionImageView.widthAnchor.constraint(equalToConstant: NotificationStatusTableViewCell.actionImageViewSize.width).priority(.required - 1),
actionImageView.heightAnchor.constraint(equalToConstant: NotificationStatusTableViewCell.actionImageViewSize.height), actionImageView.heightAnchor.constraint(equalTo: actionImageView.widthAnchor, multiplier: 1.0),
]) ])
containerStackView.addArrangedSubview(contentStackView) containerStackView.addArrangedSubview(contentStackView)
@ -282,14 +283,23 @@ extension NotificationStatusTableViewCell {
nameLabel.addGestureRecognizer(authorNameLabelTapGestureRecognizer) nameLabel.addGestureRecognizer(authorNameLabelTapGestureRecognizer)
resetSeparatorLineLayout() resetSeparatorLineLayout()
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme
.receive(on: DispatchQueue.main)
.sink { [weak self] theme in
guard let self = self else { return }
self.setupBackgroundColor(theme: theme)
}
.store(in: &disposeBag)
} }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection) super.traitCollectionDidChange(previousTraitCollection)
resetSeparatorLineLayout() resetSeparatorLineLayout()
avatarImageView.layer.borderColor = Asset.Colors.Background.systemBackground.color.cgColor setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor traitCollectionDidChange.send()
} }
private func configure(isFiltered: Bool) { private func configure(isFiltered: Bool) {
@ -297,12 +307,14 @@ extension NotificationStatusTableViewCell {
filteredLabel.isHidden = !isFiltered filteredLabel.isHidden = !isFiltered
isUserInteractionEnabled = !isFiltered isUserInteractionEnabled = !isFiltered
} }
} }
extension NotificationStatusTableViewCell { extension NotificationStatusTableViewCell {
private func setupBackgroundColor(theme: Theme) { private func setupBackgroundColor(theme: Theme) {
actionImageView.layer.borderColor = theme.systemBackgroundColor.cgColor
avatarImageView.layer.borderColor = Asset.Theme.Mastodon.systemBackground.color.cgColor
statusContainerView.layer.borderColor = Asset.Colors.Border.notificationStatus.color.cgColor
statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in statusContainerView.backgroundColor = UIColor(dynamicProvider: { traitCollection in
return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor return traitCollection.userInterfaceStyle == .light ? theme.systemBackgroundColor : theme.tertiarySystemGroupedBackgroundColor
}) })

View File

@ -115,7 +115,7 @@ extension MastodonPickServerViewController {
tableViewTopPaddingView.trailingAnchor.constraint(equalTo: view.trailingAnchor), tableViewTopPaddingView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableViewTopPaddingViewHeightLayoutConstraint, tableViewTopPaddingViewHeightLayoutConstraint,
]) ])
tableViewTopPaddingView.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color tableViewTopPaddingView.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
view.addSubview(tableView) view.addSubview(tableView)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
@ -422,7 +422,7 @@ extension MastodonPickServerViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView() let headerView = UIView()
headerView.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color headerView.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
return headerView return headerView
} }

View File

@ -55,7 +55,7 @@ extension PickServerCategoriesCell {
private func _init() { private func _init() {
selectionStyle = .none selectionStyle = .none
backgroundColor = Asset.Colors.Background.systemGroupedBackground.color backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
metricView.translatesAutoresizingMaskIntoConstraints = false metricView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(metricView) contentView.addSubview(metricView)

View File

@ -27,7 +27,7 @@ class PickServerCell: UITableViewCell {
let containerView: UIView = { let containerView: UIView = {
let view = UIView() let view = UIView()
view.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 10, right: 16) view.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 10, right: 16)
view.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color view.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
return view return view
}() }()
@ -101,7 +101,7 @@ class PickServerCell: UITableViewCell {
let separator: UIView = { let separator: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
return view return view
}() }()

View File

@ -13,14 +13,14 @@ final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell {
let containerView: UIView = { let containerView: UIView = {
let view = UIView() let view = UIView()
view.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 10, right: 16) view.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 10, right: 16)
view.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color view.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
return view return view
}() }()
let seperator: UIView = { let seperator: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
return view return view
}() }()

View File

@ -17,7 +17,7 @@ class PickServerSearchCell: UITableViewCell {
private var bgView: UIView = { private var bgView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color view.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
view.layer.maskedCorners = [ view.layer.maskedCorners = [
.layerMinXMinYCorner, .layerMinXMinYCorner,
@ -108,7 +108,7 @@ class PickServerSearchCell: UITableViewCell {
extension PickServerSearchCell { extension PickServerSearchCell {
private func _init() { private func _init() {
selectionStyle = .none selectionStyle = .none
backgroundColor = Asset.Colors.Background.systemGroupedBackground.color backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
searchTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) searchTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
searchTextField.delegate = self searchTextField.delegate = self

View File

@ -35,7 +35,7 @@ extension PickServerTitleCell {
private func _init() { private func _init() {
selectionStyle = .none selectionStyle = .none
backgroundColor = Asset.Colors.Background.systemGroupedBackground.color backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
contentView.addSubview(titleLabel) contentView.addSubview(titleLabel)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([

View File

@ -48,7 +48,7 @@ extension PickServerCategoryView {
addSubview(bgView) addSubview(bgView)
addSubview(titleLabel) addSubview(titleLabel)
bgView.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color bgView.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
bgView.leadingAnchor.constraint(equalTo: self.leadingAnchor), bgView.leadingAnchor.constraint(equalTo: self.leadingAnchor),

View File

@ -44,7 +44,7 @@ final class PickServerEmptyStateView: UIView {
extension PickServerEmptyStateView { extension PickServerEmptyStateView {
private func _init() { private func _init() {
backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
layer.maskedCorners = [ layer.maskedCorners = [
.layerMinXMaxYCorner, .layerMinXMaxYCorner,
.layerMaxXMaxYCorner .layerMaxXMaxYCorner

View File

@ -84,7 +84,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
button.setImage(image?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate), for: UIControl.State.normal) button.setImage(image?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate), for: UIControl.State.normal)
button.imageView?.tintColor = Asset.Colors.Label.secondary.color button.imageView?.tintColor = Asset.Colors.Label.secondary.color
button.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color button.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
button.layer.cornerRadius = 10 button.layer.cornerRadius = 10
button.clipsToBounds = true button.clipsToBounds = true
@ -99,7 +99,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
icon.backgroundColor = UIColor(dynamicProvider: { collection in icon.backgroundColor = UIColor(dynamicProvider: { collection in
switch collection.userInterfaceStyle { switch collection.userInterfaceStyle {
case .dark: case .dark:
return Asset.Colors.Background.secondaryGroupedSystemBackground.color return Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
default: default:
return .white return .white
} }
@ -119,7 +119,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
textField.returnKeyType = .next textField.returnKeyType = .next
textField.autocapitalizationType = .none textField.autocapitalizationType = .none
textField.autocorrectionType = .no textField.autocorrectionType = .no
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color textField.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
textField.textColor = Asset.Colors.Label.primary.color textField.textColor = Asset.Colors.Label.primary.color
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Username.placeholder, textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Username.placeholder,
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
@ -170,7 +170,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
textField.returnKeyType = .next textField.returnKeyType = .next
textField.autocapitalizationType = .none textField.autocapitalizationType = .none
textField.autocorrectionType = .no textField.autocorrectionType = .no
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color textField.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
textField.textColor = Asset.Colors.Label.primary.color textField.textColor = Asset.Colors.Label.primary.color
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.DisplayName.placeholder, textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.DisplayName.placeholder,
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
@ -189,7 +189,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
textField.autocapitalizationType = .none textField.autocapitalizationType = .none
textField.autocorrectionType = .no textField.autocorrectionType = .no
textField.keyboardType = .emailAddress textField.keyboardType = .emailAddress
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color textField.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
textField.textColor = Asset.Colors.Label.primary.color textField.textColor = Asset.Colors.Label.primary.color
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Email.placeholder, textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Email.placeholder,
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
@ -216,7 +216,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
textField.autocorrectionType = .no textField.autocorrectionType = .no
textField.keyboardType = .asciiCapable textField.keyboardType = .asciiCapable
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color textField.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
textField.textColor = Asset.Colors.Label.primary.color textField.textColor = Asset.Colors.Label.primary.color
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Password.placeholder, textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Password.placeholder,
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,
@ -248,7 +248,7 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
textField.returnKeyType = .next // set to "Return" depends on if the last input field or not textField.returnKeyType = .next // set to "Return" depends on if the last input field or not
textField.autocapitalizationType = .none textField.autocapitalizationType = .none
textField.autocorrectionType = .no textField.autocorrectionType = .no
textField.backgroundColor = Asset.Colors.Background.secondaryGroupedSystemBackground.color textField.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
textField.textColor = Asset.Colors.Label.primary.color textField.textColor = Asset.Colors.Label.primary.color
textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Invite.registrationUserInviteRequest, textField.attributedPlaceholder = NSAttributedString(string: L10n.Scene.Register.Input.Invite.registrationUserInviteRequest,
attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color, attributes: [NSAttributedString.Key.foregroundColor: Asset.Colors.Label.secondary.color,

View File

@ -49,7 +49,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
let bottomContainerView: UIView = { let bottomContainerView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
return view return view
}() }()
@ -60,7 +60,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
textView.isSelectable = true textView.isSelectable = true
textView.isEditable = false textView.isEditable = false
textView.isScrollEnabled = false textView.isScrollEnabled = false
textView.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color textView.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
return textView return textView
}() }()

View File

@ -20,7 +20,7 @@ extension OnboardingViewControllerAppearance {
static var viewBottomPaddingHeight: CGFloat { return 11 } static var viewBottomPaddingHeight: CGFloat { return 11 }
func setupOnboardingAppearance() { func setupOnboardingAppearance() {
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
setupNavigationBarAppearance() setupNavigationBarAppearance()
@ -42,7 +42,7 @@ extension OnboardingViewControllerAppearance {
func setupNavigationBarBackgroundView() { func setupNavigationBarBackgroundView() {
let navigationBarBackgroundView: UIView = { let navigationBarBackgroundView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
return view return view
}() }()

View File

@ -17,7 +17,7 @@ final class FavoriteViewModel {
// input // input
let context: AppContext let context: AppContext
let activeMastodonAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never> let activeMastodonAuthenticationBox: CurrentValueSubject<MastodonAuthenticationBox?, Never>
let statusFetchedResultsController: StatusFetchedResultsController let statusFetchedResultsController: StatusFetchedResultsController
let cellFrameCache = NSCache<NSNumber, NSValue>() let cellFrameCache = NSCache<NSNumber, NSValue>()

View File

@ -465,7 +465,7 @@ extension ProfileHeaderViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil) picker.dismiss(animated: true, completion: nil)
guard let result = results.first else { return } guard let result = results.first else { return }
PHPickerResultLoader.loadImageData(from: result) ItemProviderLoader.loadImageData(from: result)
.sink { [weak self] completion in .sink { [weak self] completion in
guard let _ = self else { return } guard let _ = self else { return }
switch completion { switch completion {

View File

@ -76,7 +76,7 @@ final class ProfileHeaderView: UIView {
let avatarImageView: UIImageView = { let avatarImageView: UIImageView = {
let imageView = FLAnimatedImageView() let imageView = FLAnimatedImageView()
let placeholderImage = UIImage let placeholderImage = UIImage
.placeholder(size: ProfileHeaderView.avatarImageViewSize, color: Asset.Colors.Background.systemGroupedBackground.color) .placeholder(size: ProfileHeaderView.avatarImageViewSize, color: Asset.Theme.Mastodon.systemGroupedBackground.color)
.af.imageRounded(withCornerRadius: ProfileHeaderView.avatarImageViewCornerRadius, divideRadiusByImageScale: false) .af.imageRounded(withCornerRadius: ProfileHeaderView.avatarImageViewCornerRadius, divideRadiusByImageScale: false)
imageView.image = placeholderImage imageView.image = placeholderImage
return imageView return imageView

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import MastodonUI
final class ProfileRelationshipActionButton: RoundedEdgesButton { final class ProfileRelationshipActionButton: RoundedEdgesButton {

View File

@ -115,7 +115,7 @@ class ProfileViewModel: NSObject {
context.authenticationService.activeMastodonAuthenticationBox.eraseToAnyPublisher(), context.authenticationService.activeMastodonAuthenticationBox.eraseToAnyPublisher(),
pendingRetryPublisher.eraseToAnyPublisher() pendingRetryPublisher.eraseToAnyPublisher()
) )
.compactMap { mastodonUserID, activeMastodonAuthenticationBox, _ -> (String, AuthenticationService.MastodonAuthenticationBox)? in .compactMap { mastodonUserID, activeMastodonAuthenticationBox, _ -> (String, MastodonAuthenticationBox)? in
guard let mastodonUserID = mastodonUserID, let activeMastodonAuthenticationBox = activeMastodonAuthenticationBox else { return nil } guard let mastodonUserID = mastodonUserID, let activeMastodonAuthenticationBox = activeMastodonAuthenticationBox else { return nil }
guard mastodonUserID != activeMastodonAuthenticationBox.userID else { return nil } guard mastodonUserID != activeMastodonAuthenticationBox.userID else { return nil }
return (mastodonUserID, activeMastodonAuthenticationBox) return (mastodonUserID, activeMastodonAuthenticationBox)
@ -441,9 +441,9 @@ extension ProfileViewModel {
case .request: return Asset.Colors.brandBlue.color case .request: return Asset.Colors.brandBlue.color
case .pending: return Asset.Colors.brandBlue.color case .pending: return Asset.Colors.brandBlue.color
case .following: return Asset.Colors.brandBlue.color case .following: return Asset.Colors.brandBlue.color
case .muting: return Asset.Colors.Background.alertYellow.color case .muting: return Asset.Colors.alertYellow.color
case .blocked: return Asset.Colors.brandBlue.color case .blocked: return Asset.Colors.brandBlue.color
case .blocking: return Asset.Colors.Background.danger.color case .blocking: return Asset.Colors.danger.color
case .suspended: return Asset.Colors.brandBlue.color case .suspended: return Asset.Colors.brandBlue.color
case .edit: return Asset.Colors.brandBlue.color case .edit: return Asset.Colors.brandBlue.color
case .editing: return Asset.Colors.brandBlue.color case .editing: return Asset.Colors.brandBlue.color

View File

@ -17,7 +17,7 @@ extension ReportViewModel {
func requestRecentStatus( func requestRecentStatus(
domain: String, domain: String,
accountId: String, accountId: String,
authorizationBox: AuthenticationService.MastodonAuthenticationBox authorizationBox: MastodonAuthenticationBox
) { ) {
context.apiService.userTimeline( context.apiService.userTimeline(
domain: domain, domain: domain,

View File

@ -165,7 +165,7 @@ class ReportViewModel: NSObject {
.store(in: &disposeBag) .store(in: &disposeBag)
} }
func bindForStep2(input: Input, domain: String, activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox) -> AnyPublisher<(Bool, Error?), Never> { func bindForStep2(input: Input, domain: String, activeMastodonAuthenticationBox: MastodonAuthenticationBox) -> AnyPublisher<(Bool, Error?), Never> {
let skip = input.step2Skip.map { [weak self] value -> Void in let skip = input.step2Skip.map { [weak self] value -> Void in
guard let self = self else { return value } guard let self = self else { return value }
self.reportQuery.comment = nil self.reportQuery.comment = nil

View File

@ -17,7 +17,7 @@ extension SearchViewController {
let header = SearchRecommendCollectionHeader() let header = SearchRecommendCollectionHeader()
header.titleLabel.text = L10n.Scene.Search.Recommend.HashTag.title header.titleLabel.text = L10n.Scene.Search.Recommend.HashTag.title
header.descriptionLabel.text = L10n.Scene.Search.Recommend.HashTag.description header.descriptionLabel.text = L10n.Scene.Search.Recommend.HashTag.description
header.seeAllButton.addTarget(self, action: #selector(SearchViewController.hashtagSeeAllButtonPressed(_:)), for: .touchUpInside) header.seeAllButton.isHidden = true
stackView.addArrangedSubview(header) stackView.addArrangedSubview(header)
hashtagCollectionView.register(SearchRecommendTagsCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: SearchRecommendTagsCollectionViewCell.self)) hashtagCollectionView.register(SearchRecommendTagsCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: SearchRecommendTagsCollectionViewCell.self))

View File

@ -42,7 +42,7 @@ final class SearchViewModel: NSObject {
context.authenticationService.activeMastodonAuthenticationBox, context.authenticationService.activeMastodonAuthenticationBox,
viewDidAppeared viewDidAppeared
) )
.compactMap { activeMastodonAuthenticationBox, _ -> AuthenticationService.MastodonAuthenticationBox? in .compactMap { activeMastodonAuthenticationBox, _ -> MastodonAuthenticationBox? in
return activeMastodonAuthenticationBox return activeMastodonAuthenticationBox
} }
.throttle(for: 1, scheduler: DispatchQueue.main, latest: false) .throttle(for: 1, scheduler: DispatchQueue.main, latest: false)
@ -72,7 +72,7 @@ final class SearchViewModel: NSObject {
context.authenticationService.activeMastodonAuthenticationBox, context.authenticationService.activeMastodonAuthenticationBox,
viewDidAppeared viewDidAppeared
) )
.compactMap { activeMastodonAuthenticationBox, _ -> AuthenticationService.MastodonAuthenticationBox? in .compactMap { activeMastodonAuthenticationBox, _ -> MastodonAuthenticationBox? in
return activeMastodonAuthenticationBox return activeMastodonAuthenticationBox
} }
.throttle(for: 1, scheduler: DispatchQueue.main, latest: false) .throttle(for: 1, scheduler: DispatchQueue.main, latest: false)

View File

@ -63,7 +63,7 @@ extension PlayerContainerView {
extension PlayerContainerView.MediaTypeIndicatorView { extension PlayerContainerView.MediaTypeIndicatorView {
private func _init() { private func _init() {
backgroundColor = Asset.Colors.Background.mediaTypeIndicotor.color backgroundColor = Asset.Colors.mediaTypeIndicotor.color
layoutMargins = UIEdgeInsets(top: 3, left: 13, bottom: 0, right: 6) layoutMargins = UIEdgeInsets(top: 3, left: 13, bottom: 0, right: 6)
addSubview(label) addSubview(label)

View File

@ -29,7 +29,8 @@ final class PollOptionView: UIView {
let checkmarkBackgroundView: UIView = { let checkmarkBackgroundView: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.tertiarySystemBackground.color // FIXME: missing update trigger
view.backgroundColor = ThemeService.shared.currentTheme.value.tertiarySystemBackgroundColor
return view return view
}() }()

View File

@ -58,7 +58,6 @@ extension ThreadReplyLoaderTableViewCell {
func _init() { func _init() {
selectionStyle = .none selectionStyle = .none
backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
loadMoreButton.translatesAutoresizingMaskIntoConstraints = false loadMoreButton.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(loadMoreButton) contentView.addSubview(loadMoreButton)
@ -124,7 +123,7 @@ extension ThreadReplyLoaderTableViewCell {
} }
private func setupBackgroundColor(theme: Theme) { private func setupBackgroundColor(theme: Theme) {
loadMoreButton.backgroundColor = theme.secondarySystemGroupedBackgroundColor backgroundColor = theme.systemGroupedBackgroundColor
} }
} }

View File

@ -32,7 +32,7 @@ class SuggestionAccountViewController: UIViewController, NeedsDependency {
lazy var tableHeader: UIView = { lazy var tableHeader: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color view.backgroundColor = ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
view.frame = CGRect(origin: .zero, size: CGSize(width: tableView.frame.width, height: 156)) view.frame = CGRect(origin: .zero, size: CGSize(width: tableView.frame.width, height: 156))
return view return view
}() }()
@ -67,12 +67,12 @@ extension SuggestionAccountViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.backgroundColor = ThemeService.shared.currentTheme.value.systemBackgroundColor setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
ThemeService.shared.currentTheme ThemeService.shared.currentTheme
.receive(on: RunLoop.main) .receive(on: RunLoop.main)
.sink { [weak self] theme in .sink { [weak self] theme in
guard let self = self else { return } guard let self = self else { return }
self.view.backgroundColor = theme.systemBackgroundColor self.setupBackgroundColor(theme: theme)
} }
.store(in: &disposeBag) .store(in: &disposeBag)
@ -146,6 +146,11 @@ extension SuggestionAccountViewController {
tableView.tableHeaderView = tableHeader tableView.tableHeaderView = tableHeader
} }
private func setupBackgroundColor(theme: Theme) {
view.backgroundColor = theme.systemBackgroundColor
tableHeader.backgroundColor = theme.systemGroupedBackgroundColor
}
} }
extension SuggestionAccountViewController: UICollectionViewDelegateFlowLayout { extension SuggestionAccountViewController: UICollectionViewDelegateFlowLayout {

View File

@ -16,7 +16,7 @@ extension APIService {
func toggleBlock( func toggleBlock(
for mastodonUser: MastodonUser, for mastodonUser: MastodonUser,
activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox activeMastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light) let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
let notificationFeedbackGenerator = UINotificationFeedbackGenerator() let notificationFeedbackGenerator = UINotificationFeedbackGenerator()
@ -86,7 +86,7 @@ extension APIService {
// update database local and return block query update type for remote request // update database local and return block query update type for remote request
func blockUpdateLocal( func blockUpdateLocal(
mastodonUserObjectID: NSManagedObjectID, mastodonUserObjectID: NSManagedObjectID,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<(Mastodon.API.Account.BlockQueryType, MastodonUser.ID), Error> { ) -> AnyPublisher<(Mastodon.API.Account.BlockQueryType, MastodonUser.ID), Error> {
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain
let requestMastodonUserID = mastodonAuthenticationBox.userID let requestMastodonUserID = mastodonAuthenticationBox.userID
@ -132,7 +132,7 @@ extension APIService {
func blockUpdateRemote( func blockUpdateRemote(
blockQueryType: Mastodon.API.Account.BlockQueryType, blockQueryType: Mastodon.API.Account.BlockQueryType,
mastodonUserID: MastodonUser.ID, mastodonUserID: MastodonUser.ID,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization

View File

@ -17,7 +17,7 @@ extension APIService {
func getDomainblocks( func getDomainblocks(
domain: String, domain: String,
limit: Int = onceRequestDomainBlocksMaxCount, limit: Int = onceRequestDomainBlocksMaxCount,
authorizationBox: AuthenticationService.MastodonAuthenticationBox authorizationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<[String]>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<[String]>, Error> {
let authorization = authorizationBox.userAuthorization let authorization = authorizationBox.userAuthorization
@ -71,7 +71,7 @@ extension APIService {
func blockDomain( func blockDomain(
user: MastodonUser, user: MastodonUser,
authorizationBox: AuthenticationService.MastodonAuthenticationBox authorizationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> {
let authorization = authorizationBox.userAuthorization let authorization = authorizationBox.userAuthorization
@ -105,7 +105,7 @@ extension APIService {
func unblockDomain( func unblockDomain(
user: MastodonUser, user: MastodonUser,
authorizationBox: AuthenticationService.MastodonAuthenticationBox authorizationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Empty>, Error> {
let authorization = authorizationBox.userAuthorization let authorization = authorizationBox.userAuthorization

View File

@ -61,7 +61,7 @@ extension APIService {
func favorite( func favorite(
statusID: Mastodon.Entity.Status.ID, statusID: Mastodon.Entity.Status.ID,
favoriteKind: Mastodon.API.Favorites.FavoriteKind, favoriteKind: Mastodon.API.Favorites.FavoriteKind,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> {
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization
let requestMastodonUserID = mastodonAuthenticationBox.userID let requestMastodonUserID = mastodonAuthenticationBox.userID
@ -139,7 +139,7 @@ extension APIService {
func favoritedStatuses( func favoritedStatuses(
limit: Int = onceRequestStatusMaxCount, limit: Int = onceRequestStatusMaxCount,
maxID: String? = nil, maxID: String? = nil,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Status]>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Status]>, Error> {
let requestMastodonUserID = mastodonAuthenticationBox.userID let requestMastodonUserID = mastodonAuthenticationBox.userID

View File

@ -15,7 +15,7 @@ import MastodonSDK
extension APIService { extension APIService {
func filters( func filters(
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Filter]>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Filter]>, Error> {
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain

View File

@ -24,7 +24,7 @@ extension APIService {
/// - Returns: publisher for `Relationship` /// - Returns: publisher for `Relationship`
func toggleFollow( func toggleFollow(
for mastodonUser: MastodonUser, for mastodonUser: MastodonUser,
activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox activeMastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light) let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
@ -96,7 +96,7 @@ extension APIService {
// update database local and return follow query update type for remote request // update database local and return follow query update type for remote request
func followUpdateLocal( func followUpdateLocal(
mastodonUserObjectID: NSManagedObjectID, mastodonUserObjectID: NSManagedObjectID,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<(Mastodon.API.Account.FollowQueryType, MastodonUser.ID), Error> { ) -> AnyPublisher<(Mastodon.API.Account.FollowQueryType, MastodonUser.ID), Error> {
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain
let requestMastodonUserID = mastodonAuthenticationBox.userID let requestMastodonUserID = mastodonAuthenticationBox.userID
@ -156,7 +156,7 @@ extension APIService {
func followUpdateRemote( func followUpdateRemote(
followQueryType: Mastodon.API.Account.FollowQueryType, followQueryType: Mastodon.API.Account.FollowQueryType,
mastodonUserID: MastodonUser.ID, mastodonUserID: MastodonUser.ID,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization

View File

@ -17,7 +17,7 @@ import MastodonSDK
extension APIService { extension APIService {
func acceptFollowRequest( func acceptFollowRequest(
mastodonUserID: MastodonUser.ID, mastodonUserID: MastodonUser.ID,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization
@ -61,7 +61,7 @@ extension APIService {
func rejectFollowRequest( func rejectFollowRequest(
mastodonUserID: MastodonUser.ID, mastodonUserID: MastodonUser.ID,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error> {
let domain = mastodonAuthenticationBox.domain let domain = mastodonAuthenticationBox.domain
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization

Some files were not shown because too many files have changed in this diff Show More