diff --git a/Localization/StringsConvertor/input/Base.lproj/app.json b/Localization/StringsConvertor/input/Base.lproj/app.json index 8d4a5eae4..f09837498 100644 --- a/Localization/StringsConvertor/input/Base.lproj/app.json +++ b/Localization/StringsConvertor/input/Base.lproj/app.json @@ -453,6 +453,7 @@ "followers": "followers" }, "fields": { + "joined": "Joined", "add_row": "Add Row", "placeholder": { "label": "Label", diff --git a/Localization/app.json b/Localization/app.json index 2abc74f74..f884d6004 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -467,6 +467,7 @@ "followers": "followers" }, "fields": { + "joined": "Joined", "add_row": "Add Row", "placeholder": { "label": "Label", diff --git a/Mastodon/Diffable/Profile/ProfileFieldItem.swift b/Mastodon/Diffable/Profile/ProfileFieldItem.swift index e33a2f883..37d08bc52 100644 --- a/Mastodon/Diffable/Profile/ProfileFieldItem.swift +++ b/Mastodon/Diffable/Profile/ProfileFieldItem.swift @@ -11,10 +11,10 @@ import MastodonSDK import MastodonMeta enum ProfileFieldItem: Hashable { + case createdAt(date: Date) case field(field: FieldValue) case editField(field: FieldValue) case addEntry - case noResult } extension ProfileFieldItem { diff --git a/Mastodon/Diffable/Profile/ProfileFieldSection.swift b/Mastodon/Diffable/Profile/ProfileFieldSection.swift index 6e57e6af9..10946915d 100644 --- a/Mastodon/Diffable/Profile/ProfileFieldSection.swift +++ b/Mastodon/Diffable/Profile/ProfileFieldSection.swift @@ -33,44 +33,60 @@ extension ProfileFieldSection { collectionView.register(ProfileFieldCollectionViewHeaderFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: ProfileFieldCollectionViewHeaderFooterView.footerReuseIdentifer) let fieldCellRegistration = UICollectionView.CellRegistration { cell, indexPath, item in - guard case let .field(field) = item else { return } + let key, value: String + let emojiMeta: MastodonContent.Emojis + let verified: Bool + + switch item { + case .field(field: let field): + key = field.name.value + value = field.value.value + emojiMeta = field.emojiMeta + verified = field.verifiedAt.value != nil + case .createdAt(date: let date): + key = L10n.Scene.Profile.Fields.joined + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .none + value = formatter.string(from: date) + emojiMeta = [:] + verified = false + default: return + } // set key + let keyColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color : Asset.Colors.Label.secondary.color do { - let mastodonContent = MastodonContent(content: field.name.value, emojis: field.emojiMeta) + let mastodonContent = MastodonContent(content: key, emojis: emojiMeta) let metaContent = try MastodonMetaContent.convert(document: mastodonContent) + cell.keyMetaLabel.textAttributes[.foregroundColor] = keyColor cell.keyMetaLabel.configure(content: metaContent) } catch { - let content = PlaintextMetaContent(string: field.name.value) + let content = PlaintextMetaContent(string: key) +// cell.keyMetaLabel.textAttributes[.foregroundColor] = keyColor cell.keyMetaLabel.configure(content: content) } // set value + let linkColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color : Asset.Colors.brand.color do { - let mastodonContent = MastodonContent(content: field.value.value, emojis: field.emojiMeta) + let mastodonContent = MastodonContent(content: value, emojis: emojiMeta) let metaContent = try MastodonMetaContent.convert(document: mastodonContent) - cell.valueMetaLabel.linkAttributes[.foregroundColor] = Asset.Colors.brand.color - if field.verifiedAt.value != nil { - cell.valueMetaLabel.linkAttributes[.foregroundColor] = Asset.Scene.Profile.About.bioAboutFieldVerifiedLink.color - } + cell.valueMetaLabel.linkAttributes[.foregroundColor] = linkColor cell.valueMetaLabel.configure(content: metaContent) } catch { - let content = PlaintextMetaContent(string: field.value.value) + let content = PlaintextMetaContent(string: value) + cell.valueMetaLabel.linkAttributes[.foregroundColor] = linkColor cell.valueMetaLabel.configure(content: content) } // set background var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell() - backgroundConfiguration.backgroundColor = UIColor.secondarySystemBackground - if (field.verifiedAt.value != nil) { - backgroundConfiguration.backgroundColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedBackground.color - } + backgroundConfiguration.backgroundColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedBackground.color : UIColor.secondarySystemBackground cell.backgroundConfiguration = backgroundConfiguration // set checkmark and edit menu label - cell.checkmark.isHidden = true - cell.checkmarkPopoverString = nil - if let verifiedAt = field.verifiedAt.value { + if case .field(let field) = item, let verifiedAt = field.verifiedAt.value { cell.checkmark.isHidden = false let formatter = DateFormatter() formatter.dateStyle = .medium @@ -78,6 +94,9 @@ extension ProfileFieldSection { let dateString = formatter.string(from: verifiedAt) cell.checkmark.accessibilityLabel = L10n.Scene.Profile.Fields.Verified.long(dateString) cell.checkmarkPopoverString = L10n.Scene.Profile.Fields.Verified.short(dateString) + } else { + cell.checkmark.isHidden = true + cell.checkmarkPopoverString = nil } cell.delegate = configuration.profileFieldCollectionViewCellDelegate @@ -128,26 +147,10 @@ extension ProfileFieldSection { } cell.backgroundConfiguration = backgroundConfiguration } - - let noResultCellRegistration = UICollectionView.CellRegistration { cell, indexPath, item in - guard case .noResult = item else { return } - - var contentConfiguration = cell.defaultContentConfiguration() - contentConfiguration.text = L10n.Scene.Search.Searching.EmptyState.noResults // FIXME: - contentConfiguration.textProperties.alignment = .center - cell.contentConfiguration = contentConfiguration - - - var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell() - backgroundConfiguration.backgroundColorTransformer = .init { _ in - return .secondarySystemBackground - } - cell.backgroundConfiguration = backgroundConfiguration - } - + let dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item in switch item { - case .field: + case .field, .createdAt: return collectionView.dequeueConfiguredReusableCell( using: fieldCellRegistration, for: indexPath, @@ -165,12 +168,6 @@ extension ProfileFieldSection { for: indexPath, item: item ) - case .noResult: - return collectionView.dequeueConfiguredReusableCell( - using: noResultCellRegistration, - for: indexPath, - item: item - ) } } diff --git a/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift b/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift index 068da2ed5..171ce1ac3 100644 --- a/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift +++ b/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift @@ -58,7 +58,7 @@ extension ProfileFieldCollectionViewCell { private func _init() { // Setup colors - checkmark.tintColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedCheckmark.color; + checkmark.tintColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color; // Setup gestures tapGesture.addTarget(self, action: #selector(ProfileFieldCollectionViewCell.didTapCheckmark(_:))) diff --git a/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift b/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift index 0a11a71f6..eb7a6aaae 100644 --- a/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift +++ b/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift @@ -50,23 +50,33 @@ extension ProfileAboutViewModel { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) diffableDataSource.apply(snapshot) - - Publishers.CombineLatest4( + + let fields = Publishers.CombineLatest3( $isEditing.removeDuplicates(), profileInfo.$fields.removeDuplicates(), - profileInfoEditing.$fields.removeDuplicates(), + profileInfoEditing.$fields.removeDuplicates() + ).map { isEditing, displayFields, editingFields in + isEditing ? editingFields : displayFields + } + + + Publishers.CombineLatest4( + $isEditing.removeDuplicates(), + $createdAt.removeDuplicates(), + fields, $emojiMeta.removeDuplicates() ) .throttle(for: 0.3, scheduler: DispatchQueue.main, latest: true) - .sink { [weak self] isEditing, displayFields, editingFields, emojiMeta in + .sink { [weak self] isEditing, createdAt, fields, emojiMeta in guard let self = self else { return } guard let diffableDataSource = self.diffableDataSource else { return } var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) - let fields: [ProfileFieldItem.FieldValue] = isEditing ? editingFields : displayFields - var items: [ProfileFieldItem] = fields.map { field in + var items: [ProfileFieldItem] = [ + .createdAt(date: createdAt), + ] + fields.map { field in if isEditing { return ProfileFieldItem.editField(field: field) } else { @@ -78,10 +88,6 @@ extension ProfileAboutViewModel { items.append(.addEntry) } - if !isEditing, items.isEmpty { - items.append(.noResult) - } - snapshot.appendItems(items, toSection: .main) diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil) diff --git a/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift b/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift index 044894b8a..8ab427c39 100644 --- a/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift +++ b/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift @@ -31,6 +31,7 @@ final class ProfileAboutViewModel { @Published var fields: [MastodonField] = [] @Published var emojiMeta: MastodonContent.Emojis = [:] + @Published var createdAt: Date = Date() init(context: AppContext) { self.context = context @@ -46,6 +47,11 @@ final class ProfileAboutViewModel { .compactMap { $0 } .flatMap { $0.publisher(for: \.fields) } .assign(to: &$fields) + + $user + .compactMap { $0 } + .flatMap { $0.publisher(for: \.createdAt) } + .assign(to: &$createdAt) Publishers.CombineLatest( $fields, diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.link.colorset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.link.colorset/Contents.json deleted file mode 100644 index f5112f04f..000000000 --- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.link.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.371", - "green" : "0.565", - "red" : "0.290" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.603", - "green" : "0.742", - "red" : "0.476" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.checkmark.colorset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.text.colorset/Contents.json similarity index 100% rename from MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.checkmark.colorset/Contents.json rename to MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.text.colorset/Contents.json diff --git a/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift b/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift index 607fe4390..27914629d 100644 --- a/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift +++ b/MastodonSDK/Sources/MastodonAsset/Generated/Assets.swift @@ -175,8 +175,7 @@ public enum Asset { public enum Profile { public enum About { public static let bioAboutFieldVerifiedBackground = ColorAsset(name: "Scene/Profile/About/bio.about.field.verified.background") - public static let bioAboutFieldVerifiedCheckmark = ColorAsset(name: "Scene/Profile/About/bio.about.field.verified.checkmark") - public static let bioAboutFieldVerifiedLink = ColorAsset(name: "Scene/Profile/About/bio.about.field.verified.link") + public static let bioAboutFieldVerifiedText = ColorAsset(name: "Scene/Profile/About/bio.about.field.verified.text") } public enum Banner { public static let bioEditBackgroundGray = ColorAsset(name: "Scene/Profile/Banner/bio.edit.background.gray") diff --git a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index 984396754..40d847460 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -799,6 +799,8 @@ public enum L10n { public enum Fields { /// Add Row public static let addRow = L10n.tr("Localizable", "Scene.Profile.Fields.AddRow", fallback: "Add Row") + /// Joined + public static let joined = L10n.tr("Localizable", "Scene.Profile.Fields.Joined", fallback: "Joined") public enum Placeholder { /// Content public static let content = L10n.tr("Localizable", "Scene.Profile.Fields.Placeholder.Content", fallback: "Content") diff --git a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings index 84480c3aa..a7c26c86e 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -286,6 +286,7 @@ uploaded to Mastodon."; "Scene.Profile.Dashboard.Following" = "following"; "Scene.Profile.Dashboard.Posts" = "posts"; "Scene.Profile.Fields.AddRow" = "Add Row"; +"Scene.Profile.Fields.Joined" = "Joined"; "Scene.Profile.Fields.Placeholder.Content" = "Content"; "Scene.Profile.Fields.Placeholder.Label" = "Label"; "Scene.Profile.Fields.Verified.Long" = "Ownership of this link was checked on %@";