Skip to content

Commit a70c627

Browse files
committed
Added table cell. Added diffable assets. Clean code.
1 parent 767263e commit a70c627

10 files changed

+252
-52
lines changed

Package.swift

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,11 @@ let package = Package(
1515
)
1616
],
1717
dependencies: [
18-
.package(
19-
name: "SPAlert",
20-
url: "https://github.com/ivanvorobei/SPAlert", .upToNextMajor(from: "4.2.0")
21-
),
22-
.package(
23-
name: "NativeUIKit",
24-
url: "https://github.com/ivanvorobei/NativeUIKit", .upToNextMajor(from: "1.3.7")
25-
),
26-
.package(
27-
name: "SPFirebase",
28-
url: "https://github.com/ivanvorobei/SPFirebase", .upToNextMajor(from: "1.0.6")
29-
),
30-
.package(
31-
name: "SPSafeSymbols",
32-
url: "https://github.com/sparrowcode/SPSafeSymbols", .upToNextMajor(from: "1.0.5")
33-
),
34-
.package(
35-
name: "Nuke",
36-
url: "https://github.com/kean/Nuke", .upToNextMajor(from: "10.7.1")
37-
)
18+
.package(url: "https://github.com/ivanvorobei/SPAlert", .upToNextMajor(from: "4.2.0")),
19+
.package(url: "https://github.com/ivanvorobei/NativeUIKit", .upToNextMajor(from: "1.3.7")),
20+
.package(url: "https://github.com/ivanvorobei/SPFirebase", .upToNextMajor(from: "1.0.6")),
21+
.package(url: "https://github.com/sparrowcode/SPSafeSymbols", .upToNextMajor(from: "1.0.5")),
22+
.package(url: "https://github.com/kean/Nuke", .upToNextMajor(from: "10.7.1"))
3823
],
3924
targets: [
4025
.target(

Sources/SPProfiling/Data/Texts.swift

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ enum Texts {
2626
public static var save: String { NSLocalizedString("save", bundle: .module, comment: "") }
2727
public static var cancel: String { NSLocalizedString("cancel", bundle: .module, comment: "") }
2828

29+
// MARK: - Error
30+
2931
enum Error {
3032

3133
static var not_found: String { NSLocalizedString("error not found", bundle: .module, comment: "") }
@@ -39,29 +41,32 @@ enum Texts {
3941
static var cant_present: String { NSLocalizedString("error auth cant present", bundle: .module, comment: "") }
4042
static var cant_prepare_requerid_data: String { NSLocalizedString("error auth cant prepare requerid data", bundle: .module, comment: "") }
4143
}
42-
}
43-
44-
enum Profile {
45-
46-
static var name_didnt_set: String { NSLocalizedString("profile name didint set", bundle: .module, comment: "") }
4744

48-
enum Error {
45+
enum Profile {
4946

50-
static var name_short: String { NSLocalizedString("profile error name short", bundle: .module, comment: "") }
51-
static var name_long: String { NSLocalizedString("profile error name long", bundle: .module, comment: "") }
52-
static var empty_name: String { NSLocalizedString("profile error empty name", bundle: .module, comment: "") }
53-
static var big_avatar_width: String { NSLocalizedString("profile error big avatar width", bundle: .module, comment: "") }
54-
static var big_avatar_size: String { NSLocalizedString("profile error big avatar size", bundle: .module, comment: "") }
47+
static var name_short: String { NSLocalizedString("error profile name short", bundle: .module, comment: "") }
48+
static var name_long: String { NSLocalizedString("error profile name long", bundle: .module, comment: "") }
49+
static var empty_name: String { NSLocalizedString("error profile empty name", bundle: .module, comment: "") }
50+
static var big_avatar_width: String { NSLocalizedString("error profile big avatar width", bundle: .module, comment: "") }
51+
static var big_avatar_size: String { NSLocalizedString("error profile big avatar size", bundle: .module, comment: "") }
5552
}
53+
}
54+
55+
// MARK: - Auth
56+
57+
enum Auth {
5658

57-
enum Auth {
58-
59-
static var continue_anonymously: String { NSLocalizedString("profile auth continue anonymously", bundle: .module, comment: "") }
60-
61-
static var title: String { NSLocalizedString("profile auth title", bundle: .module, comment: "") }
62-
static var description: String { NSLocalizedString("profile auth description", bundle: .module, comment: "") }
63-
static var footer_description: String { NSLocalizedString("profile auth footer description", bundle: .module, comment: "") }
64-
}
59+
static var sign_in: String { NSLocalizedString("auth sign in", bundle: .module, comment: "") }
60+
static var continue_anonymously: String { NSLocalizedString("auth continue anonymously", bundle: .module, comment: "") }
61+
static var description: String { NSLocalizedString("auth description", bundle: .module, comment: "") }
62+
static var footer_description: String { NSLocalizedString("auth footer description", bundle: .module, comment: "") }
63+
}
64+
65+
// MARK: - Profile
66+
67+
enum Profile {
68+
69+
static var placeholder_name: String { NSLocalizedString("profile placeholder name", bundle: .module, comment: "") }
6570

6671
enum Devices {
6772

Sources/SPProfiling/Interface/Profile/CurrentProfileController+Actions.swift renamed to Sources/SPProfiling/Interface/Profile/ProfileController+Actions.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@
1919
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
// SOFTWARE.
2121

22-
2322
import UIKit
2423
import SparrowKit
2524
import NativeUIKit
2625
import SPDiffable
2726
import SPAlert
2827

29-
extension CurrentProfileController {
28+
extension ProfileController {
3029

3130
internal func showTextFieldToChangeName() {
3231
let alertController = UIAlertController(title: Texts.Profile.Actions.Rename.alert_title, message: Texts.Profile.Actions.Rename.alert_description, preferredStyle: .alert)

Sources/SPProfiling/Interface/Profile/CurrentProfileController+Internal.swift renamed to Sources/SPProfiling/Interface/Profile/ProfileController+Internal.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import SPDiffable
2626
import SPSafeSymbols
2727
import SPAlert
2828

29-
extension CurrentProfileController {
29+
extension ProfileController {
3030

3131
internal func configureHeader() {
3232
setProfile(profileModel, completion: { [weak self] in
@@ -99,7 +99,7 @@ extension CurrentProfileController {
9999

100100
internal func setProfile(_ profileModel: ProfileModel, completion: (()->())? = nil) {
101101
headerView.nameLabel.text = profileModel.name
102-
headerView.namePlaceholderLabel.text = Texts.Profile.name_didnt_set
102+
headerView.namePlaceholderLabel.text = Texts.Profile.placeholder_name
103103
if let email = profileModel.email { headerView.emailButton.setTitle(email) }
104104
headerView.layoutSubviews()
105105

Sources/SPProfiling/Interface/Profile/CurrentProfileController.swift renamed to Sources/SPProfiling/Interface/Profile/ProfileController.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
// SOFTWARE.
2121

22-
2322
import UIKit
2423
import SparrowKit
2524
import NativeUIKit
2625
import SPDiffable
2726
import SPSafeSymbols
2827
import SPAlert
2928

30-
class CurrentProfileController: NativeProfileController {
29+
class ProfileController: NativeProfileController {
3130

3231
internal var profileModel: ProfileModel
3332

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// The MIT License (MIT)
2+
// Copyright © 2022 Ivan Vorobei ([email protected])
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
// copies of the Software, and to permit persons to whom the Software is
9+
// furnished to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all
12+
// copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
// SOFTWARE.
21+
22+
import UIKit
23+
import SPDiffable
24+
25+
extension SPDiffableTableDataSource.CellProvider {
26+
27+
public static var profile: SPDiffableTableDataSource.CellProvider {
28+
return SPDiffableTableDataSource.CellProvider() { (tableView, indexPath, item) -> UITableViewCell? in
29+
guard let _ = item as? DiffableProfileItem else { return nil }
30+
let cell = tableView.dequeueReusableCell(withClass: ProfileTableViewCell.self, for: indexPath)
31+
return cell
32+
}
33+
}
34+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// The MIT License (MIT)
2+
// Copyright © 2022 Ivan Vorobei ([email protected])
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
// copies of the Software, and to permit persons to whom the Software is
9+
// furnished to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all
12+
// copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
// SOFTWARE.
21+
22+
import UIKit
23+
import SPDiffable
24+
25+
open class DiffableProfileItem: SPDiffableActionableItem {
26+
27+
public static let id: String = "spprofiling-profile-item"
28+
29+
public init(action: SPDiffableActionableItem.Action? = nil) {
30+
super.init(id: Self.id, action: action)
31+
}
32+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// The MIT License (MIT)
2+
// Copyright © 2022 Ivan Vorobei ([email protected])
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
// copies of the Software, and to permit persons to whom the Software is
9+
// furnished to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all
12+
// copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
// SOFTWARE.
21+
22+
import UIKit
23+
import NativeUIKit
24+
import SparrowKit
25+
26+
open class ProfileTableViewCell: SPTableViewCell {
27+
28+
public let titleLabel = SPLabel().do {
29+
$0.numberOfLines = 1
30+
$0.font = UIFont.preferredFont(forTextStyle: .title2, weight: .semibold)
31+
$0.textColor = .label
32+
}
33+
34+
public let descriptionLabel = SPLabel().do {
35+
$0.numberOfLines = 1
36+
$0.font = UIFont.preferredFont(forTextStyle: .footnote, weight: .regular)
37+
$0.textColor = .secondaryLabel
38+
}
39+
40+
public let avatarView = NativeAvatarView().do {
41+
$0.isEditable = false
42+
$0.placeholderImage = UIImage.system("person.crop.circle.fill", font: .systemFont(ofSize: 52, weight: .medium))
43+
$0.avatarAppearance = .placeholder
44+
}
45+
46+
// MARK: - Init
47+
48+
open override func commonInit() {
49+
super.commonInit()
50+
higlightStyle = .content
51+
contentView.addSubviews(avatarView, titleLabel, descriptionLabel)
52+
accessoryType = .disclosureIndicator
53+
updateAppearance()
54+
}
55+
56+
open override func prepareForReuse() {
57+
super.prepareForReuse()
58+
configureObservers()
59+
updateAppearance()
60+
}
61+
62+
deinit {
63+
NotificationCenter.default.removeObserver(self)
64+
}
65+
66+
// MARK: - Ovveride
67+
68+
open override func setHighlighted(_ highlighted: Bool, animated: Bool) {
69+
super.setHighlighted(highlighted, animated: animated)
70+
let higlightContent = (higlightStyle == .content)
71+
if higlightContent {
72+
[avatarView, titleLabel, descriptionLabel].forEach({ $0?.alpha = highlighted ? 0.6 : 1 })
73+
}
74+
}
75+
76+
// MARK: - Layout
77+
78+
open override func layoutSubviews() {
79+
super.layoutSubviews()
80+
avatarView.sizeToFit()
81+
avatarView.setXToSuperviewLeftMargin()
82+
avatarView.frame.origin.y = contentView.layoutMargins.top
83+
84+
let avatarRightSpace: CGFloat = NativeLayout.Spaces.default
85+
let labelVerticalSpace: CGFloat = NativeLayout.Spaces.step / 2
86+
let labelWidth = contentView.layoutWidth - avatarView.frame.width - avatarRightSpace
87+
titleLabel.layoutDynamicHeight(width: labelWidth)
88+
descriptionLabel.layoutDynamicHeight(width: labelWidth)
89+
90+
titleLabel.frame.origin.x = avatarView.frame.maxX + avatarRightSpace
91+
descriptionLabel.frame.origin.x = titleLabel.frame.origin.x
92+
93+
let labelHeight = titleLabel.frame.height + labelVerticalSpace + descriptionLabel.frame.height
94+
if (avatarView.frame.origin.y + labelHeight) > avatarView.frame.maxY {
95+
titleLabel.frame.origin.y = contentView.layoutMargins.top
96+
} else {
97+
titleLabel.frame.origin.y = contentView.layoutMargins.top + (contentView.layoutHeight - labelHeight) / 2
98+
}
99+
100+
descriptionLabel.frame.origin.y = titleLabel.frame.maxY + labelVerticalSpace
101+
}
102+
103+
open override func sizeThatFits(_ size: CGSize) -> CGSize {
104+
layoutSubviews()
105+
return .init(width: size.width, height: max(avatarView.frame.maxY, descriptionLabel.frame.maxY) + contentView.layoutMargins.bottom)
106+
}
107+
108+
// MARK: - Internal
109+
110+
internal func configureObservers() {
111+
// Clean
112+
NotificationCenter.default.removeObserver(self)
113+
114+
// Configure New
115+
NotificationCenter.default.addObserver(forName: SPProfiling.didChangedAuthState, object: nil, queue: nil) { [weak self] _ in
116+
guard let self = self else { return }
117+
self.updateAppearance()
118+
}
119+
120+
NotificationCenter.default.addObserver(forName: SPProfiling.didReloadedProfile, object: nil, queue: nil) { [weak self] _ in
121+
guard let self = self else { return }
122+
self.updateAppearance()
123+
}
124+
}
125+
126+
internal func updateAppearance() {
127+
if ProfileModel.isAuthed, let profileModel = ProfileModel.currentProfile {
128+
setProfile(profileModel, completion: nil)
129+
} else {
130+
setAuthAppearance()
131+
}
132+
}
133+
134+
internal func setAuthAppearance() {
135+
avatarView.avatarAppearance = .placeholder
136+
titleLabel.text = Texts.Auth.sign_in
137+
titleLabel.textColor = .tint
138+
}
139+
140+
internal func setProfile(_ profileModel: ProfileModel, completion: (()->())? = nil) {
141+
titleLabel.text = profileModel.name ?? profileModel.email ?? Texts.Profile.placeholder_name
142+
avatarView.setAvatar(of: profileModel) {
143+
completion?()
144+
}
145+
}
146+
}

Sources/SPProfiling/Models/Middleware/ProfileMiddlewareError.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ public enum ProfileMiddlewareError: LocalizedError {
3232

3333
public var errorDescription: String? {
3434
switch self {
35-
case .nameShort: return Texts.Profile.Error.name_short
36-
case .nameLong: return Texts.Profile.Error.name_long
37-
case .emptyName: return Texts.Profile.Error.empty_name
38-
case .invalidEmail: return Texts.Profile.Error.empty_name
39-
case .avatarBigWidth: return Texts.Profile.Error.big_avatar_width
40-
case .avatarBigSize: return Texts.Profile.Error.big_avatar_size
35+
case .nameShort: return Texts.Error.Profile.name_short
36+
case .nameLong: return Texts.Error.Profile.name_long
37+
case .emptyName: return Texts.Error.Profile.empty_name
38+
case .invalidEmail: return Texts.Error.Profile.empty_name
39+
case .avatarBigWidth: return Texts.Error.Profile.big_avatar_width
40+
case .avatarBigSize: return Texts.Error.Profile.big_avatar_size
4141
}
4242
}
4343
}

Sources/SPProfiling/ProfileModelExtension.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ extension ProfileModel {
8080
public static func showCurrentProfile(on viewController: UIViewController) {
8181
guard currentProfile != nil else { return }
8282

83-
let controller = CurrentProfileController()
83+
let controller = ProfileController()
8484
let navigationController = controller.wrapToNavigationController(prefersLargeTitles: false)
8585
controller.navigationItem.rightBarButtonItem = controller.closeBarButtonItem
8686

0 commit comments

Comments
 (0)