diff --git a/Localization/StringsConvertor/input/Base.lproj/app.json b/Localization/StringsConvertor/input/Base.lproj/app.json index 96b2150ce..af3e7c6ae 100644 --- a/Localization/StringsConvertor/input/Base.lproj/app.json +++ b/Localization/StringsConvertor/input/Base.lproj/app.json @@ -449,6 +449,7 @@ "followers": "followers" }, "fields": { + "joined": "Joined", "add_row": "Add Row", "placeholder": { "label": "Label", diff --git a/Localization/app.json b/Localization/app.json index 96b2150ce..af3e7c6ae 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -449,6 +449,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..8a68cc51d 100644 --- a/Mastodon/Diffable/Profile/ProfileFieldSection.swift +++ b/Mastodon/Diffable/Profile/ProfileFieldSection.swift @@ -33,44 +33,57 @@ 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 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.configure(content: metaContent) } catch { - let content = PlaintextMetaContent(string: field.name.value) + let content = PlaintextMetaContent(string: key) cell.keyMetaLabel.configure(content: content) } // set value + let linkColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedLink.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 +91,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 +144,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 +165,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/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/MastodonLocalization/Generated/Strings.swift b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift index ee56f9e31..2d33abe5d 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift +++ b/MastodonSDK/Sources/MastodonLocalization/Generated/Strings.swift @@ -745,6 +745,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 2a3f1efbf..1b81a15b6 100644 --- a/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings +++ b/MastodonSDK/Sources/MastodonLocalization/Resources/Base.lproj/Localizable.strings @@ -268,6 +268,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 %@";