Skip to content

Commit

Permalink
Cleanup and refactoring (#2530)
Browse files Browse the repository at this point in the history
- Replace layout
- Refactor (and rename) Webxdc-Grid-Cell
- Update translations
  • Loading branch information
zeitschlag committed Jan 24, 2025
1 parent 7b54c2e commit 493ee1e
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 169 deletions.
4 changes: 1 addition & 3 deletions deltachat-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,6 @@
3059620D234614E700C80F33 /* DcContact+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DcContact+Extension.swift"; sourceTree = "<group>"; };
3059620F2346154D00C80F33 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
305DDD8625DD97BF00974489 /* DynamicFontButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicFontButton.swift; sourceTree = "<group>"; };
305FE03523A81B4C0053BE90 /* EmptyStateLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyStateLabel.swift; sourceTree = "<group>"; };
3060119D22DDE24000C1CE6F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
3060119F22DDE24500C1CE6F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
306011A022DDE24700C1CE6F /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Localizable.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1024,7 +1023,6 @@
isa = PBXGroup;
children = (
AE0AA951247800E700D42A7F /* GalleryCell.swift */,
30238CFC28A5028300EF14AC /* WebxdcGridCell.swift */,
AE8DD450249D1DFB009A4BC1 /* DocumentGalleryFileCell.swift */,
);
path = Cell;
Expand Down Expand Up @@ -1140,7 +1138,6 @@
AE728F14229D5C390047565B /* PhotoPickerAlertAction.swift */,
AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */,
AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */,
305FE03523A81B4C0053BE90 /* EmptyStateLabel.swift */,
AEFBE22E23FEF23D0045327A /* ProviderInfoCell.swift */,
AED62BCD247687E6009E220D /* LocationStreamingIndicator.swift */,
30F4BFED252E3E020006B9B3 /* PaddingTextView.swift */,
Expand Down Expand Up @@ -1213,6 +1210,7 @@
D83226FC2D3AC91100C239C5 /* Apps */ = {
isa = PBXGroup;
children = (
30238CFC28A5028300EF14AC /* WebxdcGridCell.swift */,
D83226FF2D3AC9BF00C239C5 /* AppPickerViewController.swift */,
D86DF20B2D30318B00141B3E /* WebxdcStoreViewController.swift */,
D83227012D3AC9F300C239C5 /* RecentWebxdcAppsViewController.swift */,
Expand Down
6 changes: 3 additions & 3 deletions deltachat-ios/Chat/Apps/AppPickerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class AppPickerViewController: UIViewController {
// MARK: - Actions

@objc func showLoading() {
title = String.localized("Downloading...")
title = String.localized("app_picker_downloading")
navigationItem.titleView = nil
downloadingView.isHidden = false
downloadingView.activityIndicator.startAnimating()
Expand Down Expand Up @@ -133,8 +133,8 @@ extension AppPickerViewController: WebxdcStoreViewControllerDelegate {
}
}

extension AppPickerViewController: WebxdcSelectorDelegate {
func onWebxdcFromFilesSelected(url: URL) {
extension AppPickerViewController: RecentWebxdcAppsViewControllerDelegate {
func webxdcFileSelected(_ viewController: RecentWebxdcAppsViewController, url: URL) {
// we need do duplicate/copy the file to `caches` due to ... core-reasons
guard let data = try? Data(contentsOf: url),
let path = FileHelper.saveData(data: data,
Expand Down
121 changes: 55 additions & 66 deletions deltachat-ios/Chat/Apps/RecentWebxdcAppsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,75 @@ import UIKit
import DcCore
import QuickLook

protocol WebxdcSelectorDelegate: AnyObject {
func onWebxdcFromFilesSelected(url: URL)
protocol RecentWebxdcAppsViewControllerDelegate: AnyObject {
func webxdcFileSelected(_ viewController: RecentWebxdcAppsViewController, url: URL)
}

// TODO: Rewrite
class RecentWebxdcAppsViewController: UIViewController {

private let dcContext: DcContext
// MARK: - data
private var deduplicatedMessageIds: [Int] = []
private var items: [Int: GalleryItem] = [:]

// MARK: - subview specs
private let gridDefaultSpacing: CGFloat = 5
weak var delegate: WebxdcSelectorDelegate?
weak var delegate: RecentWebxdcAppsViewControllerDelegate?

private lazy var gridLayout: GridCollectionViewFlowLayout = {
let layout = GridCollectionViewFlowLayout()
layout.minimumLineSpacing = gridDefaultSpacing
layout.minimumInteritemSpacing = gridDefaultSpacing
layout.format = .rect(ratio: 1.3)
return layout
}()
private let collectionView: UICollectionView
private let emptyStateLabel: EmptyStateLabel

private lazy var grid: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: gridLayout)
init(context: DcContext) {
collectionView = UICollectionView(frame: .zero, collectionViewLayout: Self.layout())
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(WebxdcGridCell.self, forCellWithReuseIdentifier: WebxdcGridCell.reuseIdentifier)
collectionView.contentInset = UIEdgeInsets(top: gridDefaultSpacing, left: gridDefaultSpacing, bottom: gridDefaultSpacing, right: gridDefaultSpacing)
collectionView.register(RecentWebxdcAppsCell.self, forCellWithReuseIdentifier: RecentWebxdcAppsCell.reuseIdentifier)
collectionView.backgroundColor = DcColors.defaultBackgroundColor
collectionView.delaysContentTouches = false
collectionView.alwaysBounceVertical = true
collectionView.isPrefetchingEnabled = true
collectionView.prefetchDataSource = self
return collectionView
}()

private lazy var emptyStateView: EmptyStateLabel = {
let label = EmptyStateLabel()
label.text = String.localized("one_moment")
return label
}()
emptyStateLabel = EmptyStateLabel(text: String.localized("one_moment"))

init(context: DcContext) {
self.dcContext = context
super.init(nibName: nil, bundle: nil)

collectionView.dataSource = self
collectionView.delegate = self
view.backgroundColor = .systemBackground
setupSubviews()
view.addSubview(collectionView)

setupConstraints()
deduplicateWebxdcs()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

// MARK: - setup
private func setupSubviews() {
view.addSubview(grid)
private func setupConstraints() {
let constraints = [
grid.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
grid.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
view.trailingAnchor.constraint(equalTo: grid.safeAreaLayoutGuide.trailingAnchor),
view.bottomAnchor.constraint(equalTo: grid.safeAreaLayoutGuide.bottomAnchor),
collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
view.trailingAnchor.constraint(equalTo: collectionView.safeAreaLayoutGuide.trailingAnchor),
view.bottomAnchor.constraint(equalTo: collectionView.safeAreaLayoutGuide.bottomAnchor),
]

NSLayoutConstraint.activate(constraints)

emptyStateView.addCenteredTo(parentView: view)
emptyStateLabel.addCenteredTo(parentView: view)
}

private static func layout() -> UICollectionViewCompositionalLayout {

let layout = UICollectionViewCompositionalLayout { _, environment in
let itemsPerRow = environment.traitCollection.horizontalSizeClass == .compact ? 4 : 8
let fractionalWidth: CGFloat = 1 / CGFloat(itemsPerRow)

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(fractionalWidth), heightDimension: .fractionalHeight(1))
let layoutItem = NSCollectionLayoutItem(layoutSize: itemSize)
layoutItem.contentInsets = .init(top: 4, leading: 4, bottom: 4, trailing: 4)

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(fractionalWidth * 1.3))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [layoutItem])

let section = NSCollectionLayoutSection(group: group)
return section
}

return layout
}

func deduplicateWebxdcs() {
Expand All @@ -86,39 +85,29 @@ class RecentWebxdcAppsViewController: UIViewController {
self.deduplicatedMessageIds.append(id)
}
}
}

DispatchQueue.main.async {
if self.deduplicatedMessageIds.isEmpty {
self.emptyStateView.text = String.localized("webxdc_selector_empty_hint")
} else {
self.emptyStateView.isHidden = true
DispatchQueue.main.async {
if self.deduplicatedMessageIds.isEmpty {
self.emptyStateLabel.text = String.localized("webxdc_selector_empty_hint")
} else {
self.emptyStateLabel.isHidden = true
}
self.collectionView.reloadData()
}
self.grid.reloadData()
}
}
}

extension RecentWebxdcAppsViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
indexPaths.forEach { if items[$0.row] == nil {
let message = dcContext.getMessage(id: deduplicatedMessageIds[$0.row])
let item = GalleryItem(msg: message)
items[$0.row] = item
}}
}
}

// MARK: - UICollectionViewDataSource, UICollectionViewDelegate
extension RecentWebxdcAppsViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return deduplicatedMessageIds.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let webxdcGridCell = collectionView.dequeueReusableCell(
withReuseIdentifier: WebxdcGridCell.reuseIdentifier,
for: indexPath) as? WebxdcGridCell else {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: RecentWebxdcAppsCell.reuseIdentifier,
for: indexPath) as? RecentWebxdcAppsCell else {
return UICollectionViewCell()
}

Expand All @@ -132,15 +121,15 @@ extension RecentWebxdcAppsViewController: UICollectionViewDataSource, UICollecti
items[indexPath.row] = galleryItem
item = galleryItem
}
webxdcGridCell.update(item: item)
return webxdcGridCell
cell.update(item: item)
return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let msgId = deduplicatedMessageIds[indexPath.row]
let message = dcContext.getMessage(id: msgId)
if let fileURL = message.fileURL {
delegate?.onWebxdcFromFilesSelected(url: fileURL)
delegate?.webxdcFileSelected(self, url: fileURL)
}
collectionView.deselectItem(at: indexPath, animated: true)
}
Expand Down
106 changes: 106 additions & 0 deletions deltachat-ios/Chat/Apps/WebxdcGridCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import UIKit
import DcCore
import SDWebImage

class RecentWebxdcAppsCell: UICollectionViewCell {
static let reuseIdentifier = "RecentWebxdcAppsCell"

weak var item: GalleryItem?

private var font: UIFont {
let regularFont = UIFont.preferredFont(forTextStyle: .subheadline)
if regularFont.pointSize > 28 {
return UIFont.systemFont(ofSize: 28)
}
return regularFont
}

private let contentStackView: UIStackView
private let imageView: SDAnimatedImageView
private let descriptionLabel: UILabel

override init(frame: CGRect) {
imageView = SDAnimatedImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.isAccessibilityElement = false
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 6

descriptionLabel = UILabel()
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
descriptionLabel.lineBreakMode = .byTruncatingTail
descriptionLabel.textColor = DcColors.defaultInverseColor
descriptionLabel.backgroundColor = DcColors.defaultTransparentBackgroundColor
descriptionLabel.textAlignment = .center

contentStackView = UIStackView(arrangedSubviews: [imageView, descriptionLabel])
contentStackView.translatesAutoresizingMaskIntoConstraints = false
contentStackView.spacing = 4
contentStackView.axis = .vertical
contentStackView.alignment = .center

super.init(frame: frame)
contentView.addSubview(contentStackView)
backgroundColor = DcColors.defaultBackgroundColor
descriptionLabel.font = font

setupConstraints()
}

required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

override func prepareForReuse() {
super.prepareForReuse()
item?.onImageLoaded = nil
item = nil
imageView.image = nil
descriptionLabel.text = nil
}

private func setupConstraints() {
let constraints = [
contentStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
contentStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
contentStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
descriptionLabel.widthAnchor.constraint(lessThanOrEqualTo: imageView.widthAnchor),
]

NSLayoutConstraint.activate(constraints)

// imageView.constraintAlignLeadingToAnchor(contentView.leadingAnchor),
// imageView.constraintAlignTrailingToAnchor(contentView.trailingAnchor),
// imageView.constraintAlignTopToAnchor(contentView.topAnchor),
// ,
// descriptionLabel.constraintAlignTrailingMaxTo(contentView),
// descriptionLabel.constraintCenterXTo(contentView),
// ,
// descriptionLabel.constraintToBottomOf(imageView, paddingTop: 4),
// descriptionLabel.constraintToBottomOf(contentView)
}

func update(item: GalleryItem) {
self.item = item
item.onImageLoaded = { [weak self] image in
self?.imageView.image = image
}
imageView.image = item.thumbnailImage
descriptionLabel.text = item.msg.getWebxdcInfoDict()["name"] as? String ?? "ErrName"
}

override var isSelected: Bool {
willSet {
// to provide visual feedback on select events
imageView.alpha = newValue ? 0.75 : 1.0
}
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if previousTraitCollection?.preferredContentSizeCategory !=
traitCollection.preferredContentSizeCategory {
descriptionLabel.font = font
}
}
}
Loading

0 comments on commit 493ee1e

Please sign in to comment.