浏览代码

implement basic chat list with cells containing the initials badge

cyberta 5 年之前
父节点
当前提交
7efc419a30

+ 20 - 0
DcCore/DcCore.xcodeproj/project.pbxproj

@@ -23,6 +23,9 @@
 		30421988243F23E500516852 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421987243F23E500516852 /* Constants.swift */; };
 		306C324824460CDE001D89F3 /* DateUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 306C324724460CDE001D89F3 /* DateUtils.swift */; };
 		30E8F2212447357500CE2C90 /* DatabaseHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2202447357500CE2C90 /* DatabaseHelper.swift */; };
+		30E8F2482449C98600CE2C90 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */; };
+		30E8F24B2449CF6500CE2C90 /* InitialsBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */; };
+		30E8F24D2449D30200CE2C90 /* DcColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F24C2449D30200CE2C90 /* DcColors.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -55,6 +58,9 @@
 		30421987243F23E500516852 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
 		306C324724460CDE001D89F3 /* DateUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUtils.swift; sourceTree = "<group>"; };
 		30E8F2202447357500CE2C90 /* DatabaseHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseHelper.swift; sourceTree = "<group>"; };
+		30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = "<group>"; };
+		30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialsBadge.swift; sourceTree = "<group>"; };
+		30E8F24C2449D30200CE2C90 /* DcColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DcColors.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -100,6 +106,7 @@
 		30421933243DE0F200516852 /* DcCore */ = {
 			isa = PBXGroup;
 			children = (
+				30E8F2492449CF2F00CE2C90 /* Views */,
 				30421977243F1AF400516852 /* Helper */,
 				3042195E243E255000516852 /* Extensions */,
 				3042194B243DE15D00516852 /* DC */,
@@ -144,6 +151,7 @@
 			children = (
 				3042195F243E257100516852 /* UIColor+Extensions.swift */,
 				30421963243F0B8400516852 /* String+Extensions.swift */,
+				30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */,
 			);
 			path = Extensions;
 			sourceTree = "<group>";
@@ -155,10 +163,19 @@
 				30421987243F23E500516852 /* Constants.swift */,
 				3042195C243E23F100516852 /* DcUtils.swift */,
 				306C324724460CDE001D89F3 /* DateUtils.swift */,
+				30E8F24C2449D30200CE2C90 /* DcColors.swift */,
 			);
 			path = Helper;
 			sourceTree = "<group>";
 		};
+		30E8F2492449CF2F00CE2C90 /* Views */ = {
+			isa = PBXGroup;
+			children = (
+				30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */,
+			);
+			path = Views;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -292,15 +309,18 @@
 			buildActionMask = 2147483647;
 			files = (
 				30421988243F23E500516852 /* Constants.swift in Sources */,
+				30E8F24D2449D30200CE2C90 /* DcColors.swift in Sources */,
 				30421962243E26C800516852 /* Logger.swift in Sources */,
 				30421986243F209E00516852 /* events.swift in Sources */,
 				30421951243DE15D00516852 /* Wrapper.swift in Sources */,
 				306C324824460CDE001D89F3 /* DateUtils.swift in Sources */,
 				30421952243DE15D00516852 /* wrapper.c in Sources */,
+				30E8F24B2449CF6500CE2C90 /* InitialsBadge.swift in Sources */,
 				30E8F2212447357500CE2C90 /* DatabaseHelper.swift in Sources */,
 				3042195D243E23F100516852 /* DcUtils.swift in Sources */,
 				30421964243F0B8400516852 /* String+Extensions.swift in Sources */,
 				30421960243E257100516852 /* UIColor+Extensions.swift in Sources */,
+				30E8F2482449C98600CE2C90 /* UIView+Extensions.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 3 - 0
DcCore/DcCore/Extensions/UIColor+Extensions.swift

@@ -60,5 +60,8 @@ public extension UIColor {
         return UIColor(hexString: lightHex)
     }
 
+    static func rgb(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
+        return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1)
+    }
 
 }

+ 230 - 0
DcCore/DcCore/Extensions/UIView+Extensions.swift

@@ -0,0 +1,230 @@
+import UIKit
+
+// swiftlint:disable explicit_acl
+public extension UIView {
+
+    func makeBorder(color: UIColor = UIColor.red) {
+        self.layer.borderColor = color.cgColor
+        self.layer.borderWidth = 2
+    }
+
+    func alignLeadingToAnchor(_ anchor: NSLayoutXAxisAnchor, paddingLeading: CGFloat = 0.0) {
+        self.leadingAnchor.constraint(equalTo: anchor, constant: paddingLeading).isActive = true
+    }
+
+    func alignTrailingToAnchor(_ anchor: NSLayoutXAxisAnchor, paddingTrailing: CGFloat = 0.0) {
+        self.trailingAnchor.constraint(equalTo: anchor, constant: -paddingTrailing).isActive = true
+    }
+
+    func alignTopToAnchor(_ anchor: NSLayoutYAxisAnchor, paddingTop: CGFloat = 0.0) {
+        self.topAnchor.constraint(equalTo: anchor, constant: paddingTop).isActive = true
+    }
+
+    func alignBottomToAnchor(_ anchor: NSLayoutYAxisAnchor, paddingBottom: CGFloat = 0.0) {
+        self.bottomAnchor.constraint(equalTo: anchor, constant: -paddingBottom).isActive = true
+    }
+
+    func fill(view: UIView, paddingLeading: CGFloat? = 0.0, paddingTrailing: CGFloat? = 0.0, paddingTop: CGFloat? = 0.0, paddingBottom: CGFloat? = 0.0) {
+        alignLeadingToAnchor(view.leadingAnchor, paddingLeading: paddingLeading ??  0.0)
+        alignTrailingToAnchor(view.trailingAnchor, paddingTrailing: paddingTrailing ?? 0.0)
+        alignTopToAnchor(view.topAnchor, paddingTop: paddingTop ?? 0.0)
+        alignBottomToAnchor(view.bottomAnchor, paddingBottom: paddingBottom ?? 0.0)
+    }
+
+    func constraintAlignTopTo(_ view: UIView) -> NSLayoutConstraint {
+        return constraintAlignTopTo(view, paddingTop: 0.0)
+    }
+
+    func constraintAlignTopTo(_ view: UIView, paddingTop: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .top,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .top,
+            multiplier: 1.0,
+            constant: paddingTop)
+    }
+
+    func constraintAlignBottomTo(_ view: UIView, paddingBottom: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .bottom,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .bottom,
+            multiplier: 1.0,
+            constant: -paddingBottom)
+    }
+
+    func constraintAlignLeadingTo(_ view: UIView, paddingLeading: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .leading,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .leading,
+            multiplier: 1.0,
+            constant: paddingLeading)
+    }
+
+    func constraintAlignTrailingTo(_ view: UIView, paddingTrailing: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .trailing,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .trailing,
+            multiplier: 1.0,
+            constant: -paddingTrailing)
+    }
+
+    func constraintToBottomOf(_ view: UIView, paddingTop: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .top,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .bottom,
+            multiplier: 1.0,
+            constant: paddingTop)
+    }
+
+    func constraintToTrailingOf(_ view: UIView, paddingLeading: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .leading,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .trailing,
+            multiplier: 1.0,
+            constant: paddingLeading)
+    }
+
+
+    func constraintCenterXTo(_ view: UIView, paddingX: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(item: self,
+                                  attribute: .centerX,
+                                  relatedBy: .equal,
+                                  toItem: view,
+                                  attribute: .centerX,
+                                  multiplier: 1.0,
+                                  constant: paddingX)
+    }
+
+    func constraintCenterYTo(_ view: UIView, paddingY: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(item: self,
+                                  attribute: .centerY,
+                                  relatedBy: .equal,
+                                  toItem: view,
+                                  attribute: .centerY,
+                                  multiplier: 1.0,
+                                  constant: paddingY)
+    }
+
+    func constraintHeightTo(_ height: CGFloat) -> NSLayoutConstraint {
+        return heightAnchor.constraint(equalToConstant: height)
+    }
+
+    func constraintWidthTo(_ width: CGFloat) -> NSLayoutConstraint {
+       return  widthAnchor.constraint(equalToConstant: width)
+    }
+
+    func fillSuperview() {
+        guard let superview = self.superview else {
+            return
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+
+        let constraints: [NSLayoutConstraint] = [
+            leftAnchor.constraint(equalTo: superview.leftAnchor),
+            rightAnchor.constraint(equalTo: superview.rightAnchor),
+            topAnchor.constraint(equalTo: superview.topAnchor),
+            bottomAnchor.constraint(equalTo: superview.bottomAnchor)]
+        NSLayoutConstraint.activate(constraints)
+    }
+
+    func centerInSuperview() {
+        guard let superview = self.superview else {
+            return
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+        let constraints: [NSLayoutConstraint] = [
+            centerXAnchor.constraint(equalTo: superview.centerXAnchor),
+            centerYAnchor.constraint(equalTo: superview.centerYAnchor)
+        ]
+        NSLayoutConstraint.activate(constraints)
+    }
+
+    func constraint(equalTo size: CGSize) {
+        guard superview != nil else { return }
+        translatesAutoresizingMaskIntoConstraints = false
+        let constraints: [NSLayoutConstraint] = [
+            widthAnchor.constraint(equalToConstant: size.width),
+            heightAnchor.constraint(equalToConstant: size.height)
+        ]
+        NSLayoutConstraint.activate(constraints)
+
+    }
+
+    @discardableResult
+    func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil, centerX: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, centerYConstant: CGFloat = 0, centerXConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
+
+        if self.superview == nil {
+            return []
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+
+        var constraints = [NSLayoutConstraint]()
+
+        if let top = top {
+            let constraint = topAnchor.constraint(equalTo: top, constant: topConstant)
+            constraint.identifier = "top"
+            constraints.append(constraint)
+        }
+        if let left = left {
+            let constraint = leftAnchor.constraint(equalTo: left, constant: leftConstant)
+            constraint.identifier = "left"
+            constraints.append(constraint)
+        }
+
+        if let bottom = bottom {
+            let constraint = bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant)
+            constraint.identifier = "bottom"
+            constraints.append(constraint)
+        }
+
+        if let right = right {
+            let constraint = rightAnchor.constraint(equalTo: right, constant: -rightConstant)
+            constraint.identifier = "right"
+            constraints.append(constraint)
+        }
+
+        if let centerY = centerY {
+            let constraint = centerYAnchor.constraint(equalTo: centerY, constant: centerYConstant)
+            constraint.identifier = "centerY"
+            constraints.append(constraint)
+        }
+
+        if let centerX = centerX {
+            let constraint = centerXAnchor.constraint(equalTo: centerX, constant: centerXConstant)
+            constraint.identifier = "centerX"
+            constraints.append(constraint)
+        }
+
+        if widthConstant > 0 {
+            let constraint = widthAnchor.constraint(equalToConstant: widthConstant)
+            constraint.identifier = "width"
+            constraints.append(constraint)
+        }
+
+        if heightConstant > 0 {
+            let constraint = heightAnchor.constraint(equalToConstant: heightConstant)
+            constraint.identifier = "height"
+            constraints.append(constraint)
+        }
+
+        NSLayoutConstraint.activate(constraints)
+        return constraints
+    }
+}

+ 59 - 0
DcCore/DcCore/Helper/DcColors.swift

@@ -0,0 +1,59 @@
+import UIKit
+
+public struct DcColors {
+    public static let primary = UIColor.systemBlue
+    public static let colorDisabled = UIColor.themeColor(light: UIColor(white: 0.9, alpha: 1), dark: UIColor(white: 0.2, alpha: 1))
+    public static let messagePrimaryColor = UIColor.themeColor(light: UIColor.rgb(red: 220, green: 248, blue: 198),
+                                                        dark: UIColor.init(hexString: "224508"))
+    public static let messageSecondaryColor = UIColor.themeColor(light: UIColor.init(hexString: "ebebed"),
+                                                          dark: UIColor.init(hexString: "333333"))
+    public static let contactCellBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
+    public static let defaultBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
+    public static let chatBackgroundColor = UIColor.themeColor(light: UIColor(red: 255, green: 255, blue: 255, alpha: 0), dark: .black)
+    public static let checkmarkGreen = UIColor.themeColor(light: UIColor.rgb(red: 112, green: 177, blue: 92))
+    public static let defaultTextColor = UIColor.themeColor(light: .darkText, dark: .white)
+    public static let grayTextColor = UIColor.themeColor(light: .darkGray, dark: .lightGray)
+    public static let grayDateColor = UIColor.themeColor(lightHex: "999999", darkHex: "bbbbbb") // slight variations of lightGray (#aaaaaa)
+    public static let middleGray = UIColor(hexString: "848ba7")
+    public static let secondaryTextColor = UIColor.themeColor(lightHex: "848ba7", darkHex: "a5abc0")
+    public static let inputFieldColor =  UIColor.themeColor(light: UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1),
+                                                     dark: UIColor(red: 10 / 255, green: 10 / 255, blue: 10 / 255, alpha: 1))
+    public static let placeholderColor = UIColor.themeColor(light: UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1),
+                                                     dark: UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1))
+    public static let providerPreparationBackground = UIColor.themeColor(lightHex: "#fffdf7b2", darkHex: "##fffdf7b2")
+    public static let providerBrokenBackground = UIColor.themeColor(light: SystemColor.red.uiColor, dark: SystemColor.red.uiColor)
+    public static let systemMessageBackgroundColor = UIColor.themeColor(light: UIColor.rgb(red: 248, green: 248, blue: 248), dark: UIColor(white: 0.2, alpha: 0.5))
+    public static let deaddropBackground = UIColor.themeColor(light: UIColor.init(hexString: "ebebec"), dark: UIColor.init(hexString: "1a1a1c"))
+}
+
+public enum SystemColor {
+    case red
+    case orange
+    case yellow
+    case green
+    case tealBlue
+    case blue
+    case purple
+    case pink
+
+    public var uiColor: UIColor {
+        switch self {
+        case .red:
+            return UIColor(red: 255 / 255, green: 59 / 255, blue: 48 / 255, alpha: 1)
+        case .orange:
+            return UIColor(red: 255 / 255, green: 149 / 255, blue: 0 / 255, alpha: 1)
+        case .yellow:
+            return UIColor(red: 255 / 255, green: 204 / 255, blue: 0 / 255, alpha: 1)
+        case .green:
+            return UIColor(red: 76 / 255, green: 217 / 255, blue: 100 / 255, alpha: 1)
+        case .tealBlue:
+            return UIColor(red: 90 / 255, green: 200 / 255, blue: 250 / 255, alpha: 1)
+        case .blue:
+            return UIColor(red: 0 / 255, green: 122 / 255, blue: 255 / 255, alpha: 1)
+        case .purple:
+            return UIColor(red: 88 / 255, green: 86 / 255, blue: 214 / 255, alpha: 1)
+        case .pink:
+            return UIColor(red: 255 / 255, green: 45 / 255, blue: 85 / 255, alpha: 1)
+        }
+    }
+}

+ 8 - 0
DcCore/DcCore/Helper/DcUtils.swift

@@ -3,6 +3,14 @@ import UIKit
 
 public struct DcUtils {
 
+    public static func getInitials(inputName: String) -> String {
+        if let firstLetter = inputName.first {
+            return firstLetter.uppercased()
+        } else {
+            return ""
+        }
+    }
+
     static func copyAndFreeArray(inputArray: OpaquePointer?) -> [Int] {
         var acc: [Int] = []
         let len = dc_array_get_cnt(inputArray)

+ 13 - 11
deltachat-ios/View/InitialsBadge.swift → DcCore/DcCore/Views/InitialsBadge.swift

@@ -1,6 +1,6 @@
 import UIKit
 
-class InitialsBadge: UIView {
+public class InitialsBadge: UIView {
 
     private let verificationViewPadding: CGFloat = 2
     private let size: CGFloat
@@ -31,18 +31,20 @@ class InitialsBadge: UIView {
         return imageViewContainer
     }()
 
-    convenience init(name: String, color: UIColor, size: CGFloat, accessibilityLabel: String? = nil) {
+
+    public convenience init(name: String, color: UIColor, size: CGFloat, accessibilityLabel: String? = nil) {
         self.init(size: size, accessibilityLabel: accessibilityLabel)
         setName(name)
         setColor(color)
     }
 
-    convenience init (image: UIImage, size: CGFloat, accessibilityLabel: String? = nil) {
+
+    public convenience init (image: UIImage, size: CGFloat, accessibilityLabel: String? = nil) {
         self.init(size: size, accessibilityLabel: accessibilityLabel)
         setImage(image)
     }
 
-    init(size: CGFloat, accessibilityLabel: String? = nil) {
+    public init(size: CGFloat, accessibilityLabel: String? = nil) {
         self.size = size
         super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
         self.accessibilityLabel = accessibilityLabel
@@ -80,32 +82,32 @@ class InitialsBadge: UIView {
         fatalError("init(coder:) has not been implemented")
     }
 
-    func setName(_ name: String) {
-        label.text = Utils.getInitials(inputName: name)
+    public func setName(_ name: String) {
+        label.text = DcUtils.getInitials(inputName: name)
         label.isHidden = name.isEmpty
         imageView.isHidden = !name.isEmpty
     }
 
-    func setLabelFont(_ font: UIFont) {
+    public func setLabelFont(_ font: UIFont) {
         label.font = font
     }
 
-    func setImage(_ image: UIImage) {
+    public func setImage(_ image: UIImage) {
         self.imageView.image = image
         self.imageView.contentMode = UIView.ContentMode.scaleAspectFill
         self.imageView.isHidden = false
         self.label.isHidden = true
     }
 
-    func showsInitials() -> Bool {
+    public func showsInitials() -> Bool {
         return !label.isHidden
     }
 
-    func setColor(_ color: UIColor) {
+    public func setColor(_ color: UIColor) {
         backgroundColor = color
     }
 
-    func setVerified(_ verified: Bool) {
+    public func setVerified(_ verified: Bool) {
         verifiedView.isHidden = !verified
     }
 }

+ 97 - 0
DcShare/ChatListCell.swift

@@ -0,0 +1,97 @@
+import Foundation
+import UIKit
+import DcCore
+
+class ChatListCell: UITableViewCell {
+
+    let badgeSize: CGFloat = 54
+
+    lazy var avatar: InitialsBadge = {
+        let badge = InitialsBadge(size: badgeSize)
+        badge.setColor(UIColor.lightGray)
+        badge.isAccessibilityElement = false
+        return badge
+    }()
+
+    let titleLabel: UILabel = {
+        let label = UILabel()
+        label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
+        label.lineBreakMode = .byTruncatingTail
+        label.textColor = DcColors.defaultTextColor
+        label.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1), for: NSLayoutConstraint.Axis.horizontal)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        return label
+    }()
+
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        selectionStyle = .none
+        backgroundColor = DcColors.contactCellBackgroundColor
+        contentView.backgroundColor = DcColors.contactCellBackgroundColor
+        setupSubviews()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    private func setupSubviews() {
+        let margin: CGFloat = 10
+
+        avatar.translatesAutoresizingMaskIntoConstraints = false
+        contentView.addSubview(avatar)
+
+        contentView.addConstraints([
+            avatar.constraintWidthTo(badgeSize),
+            avatar.constraintHeightTo(badgeSize),
+            avatar.constraintAlignLeadingTo(contentView, paddingLeading: badgeSize / 4),
+            avatar.constraintCenterYTo(contentView),
+        ])
+
+        contentView.addSubview(titleLabel)
+        contentView.addConstraints([
+            titleLabel.constraintCenterYTo(contentView),
+            titleLabel.constraintToTrailingOf(avatar, paddingLeading: margin),
+            titleLabel.constraintAlignTrailingTo(contentView)
+        ])
+    }
+
+    private func setImage(_ img: UIImage) {
+        avatar.setImage(img)
+    }
+
+    private func resetBackupImage() {
+        avatar.setColor(UIColor.clear)
+        avatar.setName("")
+    }
+
+    private func setBackupImage(name: String, color: UIColor) {
+        avatar.setColor(color)
+        avatar.setName(name)
+    }
+
+    private func setColor(_ color: UIColor) {
+          avatar.setColor(color)
+      }
+
+    // use this update-method to update cell in cellForRowAt whenever it is possible - other set-methods will be set private in progress
+    func updateCell(chatId: Int) {
+        let chat = DcContext.shared.getChat(chatId: chatId)
+        titleLabel.text = chat.name
+
+        if chat.visibility == DC_CHAT_VISIBILITY_PINNED {
+            backgroundColor = DcColors.deaddropBackground
+            contentView.backgroundColor = DcColors.deaddropBackground
+        } else {
+            backgroundColor = DcColors.contactCellBackgroundColor
+            contentView.backgroundColor = DcColors.contactCellBackgroundColor
+        }
+
+        if let img = chat.profileImage {
+            resetBackupImage()
+            setImage(img)
+        } else {
+            setBackupImage(name: chat.name, color: chat.color)
+        }
+    }
+}

+ 5 - 4
DcShare/ChatListController.swift

@@ -25,7 +25,8 @@ class ChatListController: UITableViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         chatList = dcContext.getChatlist(flags: DC_GCL_ADD_ALLDONE_HINT | DC_GCL_FOR_FORWARDING | DC_GCL_NO_SPECIALS, queryString: nil, queryId: 0)
-        tableView.register(UITableViewCell.self, forCellReuseIdentifier: contactCellReuseIdentifier)
+        tableView.register(ChatListCell.self, forCellReuseIdentifier: contactCellReuseIdentifier)
+        tableView.rowHeight = 80
     }
 
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
@@ -33,12 +34,12 @@ class ChatListController: UITableViewController {
     }
 
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        guard let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier) else {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as? ChatListCell else {
             fatalError("could not deque TableViewCell")
         }
+
         if let chatList = chatList {
-            let chat = dcContext.getChat(chatId: chatList.getChatId(index: indexPath.row))
-            cell.textLabel?.text = chat.name
+            cell.updateCell(chatId: chatList.getChatId(index: indexPath.row))
         }
 
         return cell

+ 0 - 1
DcShare/ShareViewController.swift

@@ -32,7 +32,6 @@ class ShareViewController: SLComposeServiceViewController {
     var selectedChatId: Int?
     var selectedChat: DcChat?
 
-
     override func viewDidLoad() {
         super.viewDidLoad()
         // workaround for iOS13 bug

+ 4 - 8
deltachat-ios.xcodeproj/project.pbxproj

@@ -100,6 +100,7 @@
 		30E8F2252447622300CE2C90 /* DcCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; };
 		30E8F2262447622300CE2C90 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		30E8F2422448B77600CE2C90 /* ChatListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2412448B77600CE2C90 /* ChatListController.swift */; };
+		30E8F2442449C64100CE2C90 /* ChatListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2432449C64100CE2C90 /* ChatListCell.swift */; };
 		30F9B9EC235F2116006E7ACF /* MessageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F9B9EB235F2116006E7ACF /* MessageCounter.swift */; };
 		6795F63A82E94FF7CD2CEC0F /* Pods_deltachat_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F7009234DB9408201A6CDCB /* Pods_deltachat_iosTests.framework */; };
 		7070FB9B2101ECBB000DC258 /* NewGroupController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7070FB9A2101ECBB000DC258 /* NewGroupController.swift */; };
@@ -130,7 +131,6 @@
 		AE1988AB23EB3C7600B4CD5F /* Assets in Resources */ = {isa = PBXBuildFile; fileRef = AE1988AA23EB3C7600B4CD5F /* Assets */; };
 		AE25F09022807AD800CDEA66 /* AvatarSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */; };
 		AE38B31822672DFC00EC37A1 /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B31722672DFC00EC37A1 /* ActionCell.swift */; };
-		AE38B31A2267328200EC37A1 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B3192267328200EC37A1 /* Colors.swift */; };
 		AE406EF0240FF8FF005F7022 /* ProfileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE406EEF240FF8FF005F7022 /* ProfileCell.swift */; };
 		AE4AEE3522B1030D000AA495 /* PreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4AEE3422B1030D000AA495 /* PreviewController.swift */; };
 		AE52EA19229EB53C00C586C9 /* ContactDetailHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */; };
@@ -144,7 +144,6 @@
 		AE851AC5227C755A00ED86F0 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC4227C755A00ED86F0 /* Protocols.swift */; };
 		AE851AC7227C776400ED86F0 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC6227C776400ED86F0 /* Location.swift */; };
 		AE851AC9227C77CF00ED86F0 /* Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC8227C77CF00ED86F0 /* Media.swift */; };
-		AE851ACE227CA54400ED86F0 /* InitialsBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */; };
 		AE851AD0227DF50900ED86F0 /* GroupChatDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */; };
 		AE9DAF0D22C1215D004C9591 /* EditContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0C22C1215D004C9591 /* EditContactController.swift */; };
 		AE9DAF0F22C278C6004C9591 /* ChatTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */; };
@@ -378,6 +377,7 @@
 		30E8F2172447285600CE2C90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		30E8F21F24472AAE00CE2C90 /* DcShare.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DcShare.entitlements; sourceTree = "<group>"; };
 		30E8F2412448B77600CE2C90 /* ChatListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListController.swift; sourceTree = "<group>"; };
+		30E8F2432449C64100CE2C90 /* ChatListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListCell.swift; sourceTree = "<group>"; };
 		30F9B9EB235F2116006E7ACF /* MessageCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCounter.swift; sourceTree = "<group>"; };
 		6241BE1534A653E79AD5D01D /* Pods_deltachat_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_deltachat_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		7070FB9A2101ECBB000DC258 /* NewGroupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewGroupController.swift; sourceTree = "<group>"; };
@@ -413,7 +413,6 @@
 		AE1988AA23EB3C7600B4CD5F /* Assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Assets; sourceTree = "<group>"; };
 		AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSelectionCell.swift; sourceTree = "<group>"; };
 		AE38B31722672DFC00EC37A1 /* ActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCell.swift; sourceTree = "<group>"; };
-		AE38B3192267328200EC37A1 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
 		AE406EEF240FF8FF005F7022 /* ProfileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCell.swift; sourceTree = "<group>"; };
 		AE4AEE3422B1030D000AA495 /* PreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewController.swift; sourceTree = "<group>"; };
 		AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailHeader.swift; sourceTree = "<group>"; };
@@ -429,7 +428,6 @@
 		AE851AC4227C755A00ED86F0 /* Protocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; };
 		AE851AC6227C776400ED86F0 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
 		AE851AC8227C77CF00ED86F0 /* Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Media.swift; sourceTree = "<group>"; };
-		AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialsBadge.swift; sourceTree = "<group>"; };
 		AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatDetailViewController.swift; sourceTree = "<group>"; };
 		AE9DAF0C22C1215D004C9591 /* EditContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactController.swift; sourceTree = "<group>"; };
 		AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTitleView.swift; sourceTree = "<group>"; };
@@ -690,6 +688,7 @@
 				30E8F2412448B77600CE2C90 /* ChatListController.swift */,
 				30E8F2142447285600CE2C90 /* MainInterface.storyboard */,
 				30E8F2172447285600CE2C90 /* Info.plist */,
+				30E8F2432449C64100CE2C90 /* ChatListCell.swift */,
 			);
 			path = DcShare;
 			sourceTree = "<group>";
@@ -880,7 +879,6 @@
 			children = (
 				AEACE2E21FB32B5C00DCDD78 /* Constants.swift */,
 				AEACE2E41FB32E1900DCDD78 /* Utils.swift */,
-				AE38B3192267328200EC37A1 /* Colors.swift */,
 				AE851AC4227C755A00ED86F0 /* Protocols.swift */,
 				3095A350237DD1F700AB07F7 /* MediaPicker.swift */,
 				30AC265E237F1807002A943F /* AvatarHelper.swift */,
@@ -904,7 +902,6 @@
 				78E45E3B21D3D03700D4B15E /* TextFieldTableViewCell.swift */,
 				789E879C21D6DF86003ED1C5 /* ProgressHud.swift */,
 				AE38B31722672DFC00EC37A1 /* ActionCell.swift */,
-				AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */,
 				AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */,
 				AE728F14229D5C390047565B /* PhotoPickerAlertAction.swift */,
 				AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */,
@@ -1248,6 +1245,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				30E8F2442449C64100CE2C90 /* ChatListCell.swift in Sources */,
 				30E8F2132447285600CE2C90 /* ShareViewController.swift in Sources */,
 				30E8F2422448B77600CE2C90 /* ChatListController.swift in Sources */,
 			);
@@ -1362,14 +1360,12 @@
 				305961F32346125100C80F33 /* MediaMessageCell.swift in Sources */,
 				305962102346154D00C80F33 /* String+Extension.swift in Sources */,
 				78E45E4C21D404AE00D4B15E /* InfoMessageCell.swift in Sources */,
-				AE38B31A2267328200EC37A1 /* Colors.swift in Sources */,
 				789E879621D6CB58003ED1C5 /* QrCodeReaderController.swift in Sources */,
 				305961D22346125100C80F33 /* CGRect+Extensions.swift in Sources */,
 				305961E12346125100C80F33 /* LocationItem.swift in Sources */,
 				305961E72346125100C80F33 /* AccessoryPosition.swift in Sources */,
 				30260CA7238F02F700D8D52C /* MultilineTextFieldCell.swift in Sources */,
 				305961DE2346125100C80F33 /* MessageType.swift in Sources */,
-				AE851ACE227CA54400ED86F0 /* InitialsBadge.swift in Sources */,
 				305961DA2346125100C80F33 /* MediaItem.swift in Sources */,
 				305961EB2346125100C80F33 /* MessageKitError.swift in Sources */,
 				70B8882E2091B8550074812E /* ContactCell.swift in Sources */,

+ 1 - 1
deltachat-ios/Controller/ChatListController.swift

@@ -279,7 +279,7 @@ class ChatListController: UITableViewController {
         }
         archiveCell.textLabel?.textAlignment = .center
         archiveCell.textLabel?.text = title
-        archiveCell.textLabel?.textColor = .systemBlue
+        archiveCell.textLabel?.textColor = UIColor.systemBlue
         return archiveCell
     }
 

+ 2 - 2
deltachat-ios/Controller/ChatViewController.swift

@@ -707,7 +707,7 @@ extension ChatViewController: MessagesDataSource {
     func avatar(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> Avatar {
         let message = messageList[indexPath.section]
         let contact = message.fromContact
-        return Avatar(image: contact.profileImage, initials: Utils.getInitials(inputName: contact.displayName))
+        return Avatar(image: contact.profileImage, initials: DcUtils.getInitials(inputName: contact.displayName))
     }
 
     func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
@@ -1076,7 +1076,7 @@ extension ChatViewController: MessagesDisplayDelegate {
     func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) {
         let message = messageList[indexPath.section]
         let contact = message.fromContact
-        let avatar = Avatar(image: contact.profileImage, initials: Utils.getInitials(inputName: contact.displayName))
+        let avatar = Avatar(image: contact.profileImage, initials: DcUtils.getInitials(inputName: contact.displayName))
         avatarView.set(avatar: avatar)
         avatarView.isHidden = isAvatarHidden(at: indexPath)
         avatarView.backgroundColor = contact.color

+ 4 - 0
deltachat-ios/Extensions/UIColor+Extensions.swift

@@ -35,5 +35,9 @@ internal extension UIColor {
     static let inputBarGray = UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
     static let playButtonLightGray = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1.0)
     static let sendButtonBlue = UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0)
+
+    static var systemBlue: UIColor {
+        return UIButton(type: .system).tintColor
+    }
     
 }

+ 0 - 69
deltachat-ios/Helper/Colors.swift

@@ -1,69 +0,0 @@
-import UIKit
-
-struct DcColors {
-    static let primary = UIColor.systemBlue
-    static let colorDisabled = UIColor.themeColor(light: UIColor(white: 0.9, alpha: 1), dark: UIColor(white: 0.2, alpha: 1))
-    static let messagePrimaryColor = UIColor.themeColor(light: UIColor.rgb(red: 220, green: 248, blue: 198),
-                                                        dark: UIColor.init(hexString: "224508"))
-    static let messageSecondaryColor = UIColor.themeColor(light: UIColor.init(hexString: "ebebed"),
-                                                          dark: UIColor.init(hexString: "333333"))
-    static let contactCellBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
-    static let defaultBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
-    static let chatBackgroundColor = UIColor.themeColor(light: UIColor(red: 255, green: 255, blue: 255, alpha: 0), dark: .black)
-    static let checkmarkGreen = UIColor.themeColor(light: UIColor.rgb(red: 112, green: 177, blue: 92))
-    static let defaultTextColor = UIColor.themeColor(light: .darkText, dark: .white)
-    static let grayTextColor = UIColor.themeColor(light: .darkGray, dark: .lightGray)
-    static let grayDateColor = UIColor.themeColor(lightHex: "999999", darkHex: "bbbbbb") // slight variations of lightGray (#aaaaaa)
-    static let middleGray = UIColor(hexString: "848ba7")
-    static let secondaryTextColor = UIColor.themeColor(lightHex: "848ba7", darkHex: "a5abc0")
-    static let inputFieldColor =  UIColor.themeColor(light: UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1),
-                                                     dark: UIColor(red: 10 / 255, green: 10 / 255, blue: 10 / 255, alpha: 1))
-    static let placeholderColor = UIColor.themeColor(light: UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1),
-                                                     dark: UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1))
-    static let providerPreparationBackground = UIColor.themeColor(lightHex: "#fffdf7b2", darkHex: "##fffdf7b2")
-    static let providerBrokenBackground = UIColor.themeColor(light: SystemColor.red.uiColor, dark: SystemColor.red.uiColor)
-    static let systemMessageBackgroundColor = UIColor.themeColor(light: UIColor.rgb(red: 248, green: 248, blue: 248), dark: UIColor(white: 0.2, alpha: 0.5))
-    static let deaddropBackground = UIColor.themeColor(light: UIColor.init(hexString: "ebebec"), dark: UIColor.init(hexString: "1a1a1c"))
-}
-
-enum SystemColor {
-    case red
-    case orange
-    case yellow
-    case green
-    case tealBlue
-    case blue
-    case purple
-    case pink
-
-    var uiColor: UIColor {
-        switch self {
-        case .red:
-            return UIColor(red: 255 / 255, green: 59 / 255, blue: 48 / 255, alpha: 1)
-        case .orange:
-            return UIColor(red: 255 / 255, green: 149 / 255, blue: 0 / 255, alpha: 1)
-        case .yellow:
-            return UIColor(red: 255 / 255, green: 204 / 255, blue: 0 / 255, alpha: 1)
-        case .green:
-            return UIColor(red: 76 / 255, green: 217 / 255, blue: 100 / 255, alpha: 1)
-        case .tealBlue:
-            return UIColor(red: 90 / 255, green: 200 / 255, blue: 250 / 255, alpha: 1)
-        case .blue:
-            return UIColor(red: 0 / 255, green: 122 / 255, blue: 255 / 255, alpha: 1)
-        case .purple:
-            return UIColor(red: 88 / 255, green: 86 / 255, blue: 214 / 255, alpha: 1)
-        case .pink:
-            return UIColor(red: 255 / 255, green: 45 / 255, blue: 85 / 255, alpha: 1)
-        }
-    }
-}
-
-extension UIColor {
-    static func rgb(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
-        return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1)
-    }
-
-    static var systemBlue: UIColor {
-        return UIButton(type: .system).tintColor
-    }
-}

+ 0 - 8
deltachat-ios/Helper/Utils.swift

@@ -5,14 +5,6 @@ import DcCore
 
 struct Utils {
 
-    static func getInitials(inputName: String) -> String {
-        if let firstLetter = inputName.first {
-            return firstLetter.uppercased()
-        } else {
-            return ""
-        }
-    }
-
     static func isValid(email: String) -> Bool {
         let emailRegEx = "(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}"
             + "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\"

+ 1 - 0
deltachat-ios/MessageKit/Views/Cells/InfoMessageCell.swift

@@ -1,4 +1,5 @@
 import UIKit
+import DcCore
 
 open class InfoMessageCell: UICollectionViewCell {
     let label = MessageLabel()

+ 1 - 0
deltachat-ios/View/ChatTitleView.swift

@@ -1,4 +1,5 @@
 import UIKit
+import DcCore
 
 class ChatTitleView: UIView {
 

+ 1 - 0
deltachat-ios/View/ContactDetailHeader.swift

@@ -1,4 +1,5 @@
 import UIKit
+import DcCore
 
 class ContactDetailHeader: UIView {
 

+ 1 - 0
deltachat-ios/View/ProviderInfoCell.swift

@@ -1,4 +1,5 @@
 import UIKit
+import DcCore
 
 enum ProviderInfoStatus: Int {
     case preparation = 2

+ 1 - 1
deltachat-ios/ViewModel/ContactCellViewModel.swift

@@ -45,7 +45,7 @@ class ContactCellViewModel: AvatarCellViewModel {
     }
 
     var avartarTitle: String {
-        return Utils.getInitials(inputName: title)
+        return DcUtils.getInitials(inputName: title)
     }
 
     var titleHighlightIndexes: [Int]