2021-05-27 07:56:55 +02:00
|
|
|
//
|
|
|
|
// ProfileFieldSection.swift
|
|
|
|
// Mastodon
|
|
|
|
//
|
|
|
|
// Created by MainasuK Cirno on 2021-5-25.
|
|
|
|
//
|
|
|
|
|
|
|
|
import os
|
|
|
|
import UIKit
|
|
|
|
import Combine
|
2022-11-11 21:34:26 +01:00
|
|
|
import MastodonAsset
|
2022-10-08 07:43:06 +02:00
|
|
|
import MastodonCore
|
2021-07-23 13:10:27 +02:00
|
|
|
import MastodonMeta
|
2022-01-27 14:23:39 +01:00
|
|
|
import MastodonLocalization
|
2021-05-27 07:56:55 +02:00
|
|
|
|
|
|
|
enum ProfileFieldSection: Equatable, Hashable {
|
|
|
|
case main
|
|
|
|
}
|
|
|
|
|
|
|
|
extension ProfileFieldSection {
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
struct Configuration {
|
|
|
|
weak var profileFieldCollectionViewCellDelegate: ProfileFieldCollectionViewCellDelegate?
|
|
|
|
weak var profileFieldEditCollectionViewCellDelegate: ProfileFieldEditCollectionViewCellDelegate?
|
|
|
|
}
|
|
|
|
|
|
|
|
static func diffableDataSource(
|
|
|
|
collectionView: UICollectionView,
|
|
|
|
context: AppContext,
|
|
|
|
configuration: Configuration
|
2021-05-27 07:56:55 +02:00
|
|
|
) -> UICollectionViewDiffableDataSource<ProfileFieldSection, ProfileFieldItem> {
|
2022-01-27 14:23:39 +01:00
|
|
|
collectionView.register(ProfileFieldCollectionViewHeaderFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: ProfileFieldCollectionViewHeaderFooterView.headerReuseIdentifer)
|
|
|
|
collectionView.register(ProfileFieldCollectionViewHeaderFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: ProfileFieldCollectionViewHeaderFooterView.footerReuseIdentifer)
|
|
|
|
|
|
|
|
let fieldCellRegistration = UICollectionView.CellRegistration<ProfileFieldCollectionViewCell, ProfileFieldItem> { cell, indexPath, item in
|
2022-11-17 02:26:26 +01:00
|
|
|
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
|
2023-04-21 23:24:48 +02:00
|
|
|
formatter.timeZone = TimeZone(identifier: "UTC")
|
2022-11-17 02:26:26 +01:00
|
|
|
value = formatter.string(from: date)
|
|
|
|
emojiMeta = [:]
|
|
|
|
verified = false
|
|
|
|
default: return
|
|
|
|
}
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
// set key
|
2022-11-17 02:44:55 +01:00
|
|
|
let keyColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color : Asset.Colors.Label.secondary.color
|
2022-01-27 14:23:39 +01:00
|
|
|
do {
|
2022-11-17 02:26:26 +01:00
|
|
|
let mastodonContent = MastodonContent(content: key, emojis: emojiMeta)
|
2022-01-27 14:23:39 +01:00
|
|
|
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
2022-11-17 02:44:55 +01:00
|
|
|
cell.keyMetaLabel.textAttributes[.foregroundColor] = keyColor
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.keyMetaLabel.configure(content: metaContent)
|
|
|
|
} catch {
|
2022-11-17 02:26:26 +01:00
|
|
|
let content = PlaintextMetaContent(string: key)
|
2022-11-17 02:44:55 +01:00
|
|
|
// cell.keyMetaLabel.textAttributes[.foregroundColor] = keyColor
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.keyMetaLabel.configure(content: content)
|
|
|
|
}
|
|
|
|
|
|
|
|
// set value
|
2023-06-02 09:52:12 +02:00
|
|
|
let linkColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color : Asset.Colors.Brand.blurple.color
|
2022-01-27 14:23:39 +01:00
|
|
|
do {
|
2022-11-17 02:26:26 +01:00
|
|
|
let mastodonContent = MastodonContent(content: value, emojis: emojiMeta)
|
2022-01-27 14:23:39 +01:00
|
|
|
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
2022-11-17 02:26:26 +01:00
|
|
|
cell.valueMetaLabel.linkAttributes[.foregroundColor] = linkColor
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.valueMetaLabel.configure(content: metaContent)
|
|
|
|
} catch {
|
2022-11-17 02:26:26 +01:00
|
|
|
let content = PlaintextMetaContent(string: value)
|
|
|
|
cell.valueMetaLabel.linkAttributes[.foregroundColor] = linkColor
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.valueMetaLabel.configure(content: content)
|
|
|
|
}
|
|
|
|
|
|
|
|
// set background
|
|
|
|
var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
|
2022-11-17 02:26:26 +01:00
|
|
|
backgroundConfiguration.backgroundColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedBackground.color : UIColor.secondarySystemBackground
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.backgroundConfiguration = backgroundConfiguration
|
2022-11-11 21:34:26 +01:00
|
|
|
|
2022-11-12 15:42:00 +01:00
|
|
|
// set checkmark and edit menu label
|
2022-11-17 02:26:26 +01:00
|
|
|
if case .field(let field) = item, let verifiedAt = field.verifiedAt.value {
|
2022-11-11 21:34:26 +01:00
|
|
|
cell.checkmark.isHidden = false
|
2022-11-12 02:53:12 +01:00
|
|
|
let formatter = DateFormatter()
|
|
|
|
formatter.dateStyle = .medium
|
|
|
|
formatter.timeStyle = .short
|
2022-11-12 15:42:00 +01:00
|
|
|
let dateString = formatter.string(from: verifiedAt)
|
2023-02-07 19:13:28 +01:00
|
|
|
let longLabel = L10n.Scene.Profile.Fields.Verified.long(dateString)
|
|
|
|
cell.checkmark.accessibilityLabel = longLabel
|
|
|
|
cell.accessibilityValue = "\(cell.valueMetaLabel.backedString), \(longLabel)"
|
2022-11-12 15:42:00 +01:00
|
|
|
cell.checkmarkPopoverString = L10n.Scene.Profile.Fields.Verified.short(dateString)
|
2022-11-17 02:26:26 +01:00
|
|
|
} else {
|
|
|
|
cell.checkmark.isHidden = true
|
|
|
|
cell.checkmarkPopoverString = nil
|
2023-02-07 19:13:28 +01:00
|
|
|
cell.accessibilityValue = cell.valueMetaLabel.backedString
|
2022-11-11 21:34:26 +01:00
|
|
|
}
|
2022-01-27 14:23:39 +01:00
|
|
|
|
2023-02-07 19:13:28 +01:00
|
|
|
cell.accessibilityLabel = cell.keyMetaLabel.backedString
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.delegate = configuration.profileFieldCollectionViewCellDelegate
|
|
|
|
}
|
|
|
|
|
|
|
|
let editFieldCellRegistration = UICollectionView.CellRegistration<ProfileFieldEditCollectionViewCell, ProfileFieldItem> { cell, indexPath, item in
|
|
|
|
guard case let .editField(field) = item else { return }
|
|
|
|
|
|
|
|
cell.keyTextField.text = field.name.value
|
|
|
|
cell.valueTextField.text = field.value.value
|
|
|
|
|
|
|
|
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.keyTextField)
|
|
|
|
.compactMap { $0.object as? UITextField }
|
|
|
|
.map { $0.text ?? "" }
|
|
|
|
.removeDuplicates()
|
|
|
|
.assign(to: \.value, on: field.name)
|
2021-05-27 08:57:20 +02:00
|
|
|
.store(in: &cell.disposeBag)
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.valueTextField)
|
|
|
|
.compactMap { $0.object as? UITextField }
|
|
|
|
.map { $0.text ?? "" }
|
|
|
|
.removeDuplicates()
|
|
|
|
.assign(to: \.value, on: field.value)
|
2021-05-27 07:56:55 +02:00
|
|
|
.store(in: &cell.disposeBag)
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
// set background
|
|
|
|
var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
|
|
|
|
backgroundConfiguration.backgroundColor = UIColor.secondarySystemBackground
|
|
|
|
cell.backgroundConfiguration = backgroundConfiguration
|
2021-05-27 07:56:55 +02:00
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
cell.delegate = configuration.profileFieldEditCollectionViewCellDelegate
|
|
|
|
}
|
|
|
|
|
|
|
|
let addEntryCellRegistration = UICollectionView.CellRegistration<ProfileFieldAddEntryCollectionViewCell, ProfileFieldItem> { cell, indexPath, item in
|
|
|
|
guard case .addEntry = item else { return }
|
|
|
|
|
|
|
|
var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
|
|
|
|
backgroundConfiguration.backgroundColorTransformer = .init { [weak cell] _ in
|
|
|
|
guard let cell = cell else {
|
|
|
|
return .secondarySystemBackground
|
|
|
|
}
|
|
|
|
let state = cell.configurationState
|
|
|
|
if state.isHighlighted || state.isSelected {
|
|
|
|
return .secondarySystemBackground.withAlphaComponent(0.5)
|
|
|
|
} else {
|
|
|
|
return .secondarySystemBackground
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cell.backgroundConfiguration = backgroundConfiguration
|
|
|
|
}
|
2022-11-17 02:26:26 +01:00
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
let dataSource = UICollectionViewDiffableDataSource<ProfileFieldSection, ProfileFieldItem>(collectionView: collectionView) { collectionView, indexPath, item in
|
|
|
|
switch item {
|
2022-11-17 02:26:26 +01:00
|
|
|
case .field, .createdAt:
|
2022-01-27 14:23:39 +01:00
|
|
|
return collectionView.dequeueConfiguredReusableCell(
|
|
|
|
using: fieldCellRegistration,
|
|
|
|
for: indexPath,
|
|
|
|
item: item
|
|
|
|
)
|
|
|
|
case .editField:
|
|
|
|
return collectionView.dequeueConfiguredReusableCell(
|
|
|
|
using: editFieldCellRegistration,
|
|
|
|
for: indexPath,
|
|
|
|
item: item
|
|
|
|
)
|
|
|
|
case .addEntry:
|
|
|
|
return collectionView.dequeueConfiguredReusableCell(
|
|
|
|
using: addEntryCellRegistration,
|
|
|
|
for: indexPath,
|
|
|
|
item: item
|
|
|
|
)
|
2021-05-27 07:56:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
|
|
|
|
switch kind {
|
|
|
|
case UICollectionView.elementKindSectionHeader:
|
|
|
|
let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ProfileFieldCollectionViewHeaderFooterView.headerReuseIdentifer, for: indexPath) as! ProfileFieldCollectionViewHeaderFooterView
|
2022-01-27 14:23:39 +01:00
|
|
|
reusableView.frame.size.height = 20
|
2021-05-27 07:56:55 +02:00
|
|
|
return reusableView
|
|
|
|
case UICollectionView.elementKindSectionFooter:
|
|
|
|
let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ProfileFieldCollectionViewHeaderFooterView.footerReuseIdentifer, for: indexPath) as! ProfileFieldCollectionViewHeaderFooterView
|
|
|
|
return reusableView
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return dataSource
|
|
|
|
}
|
|
|
|
}
|