Compare commits

...

2 Commits

Author SHA1 Message Date
Fin
80732720ce 重构消息列表,将支持按组折叠。 2024-12-24 16:10:24 +08:00
Fin
9219bcf757 update doc 2024-12-18 15:53:43 +08:00
11 changed files with 567 additions and 171 deletions

View File

@ -95,6 +95,9 @@
0661A54D204FDA4100965E4E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0661A54B204FDA4100965E4E /* LaunchScreen.storyboard */; };
0667D192247D162C005DE2ED /* MessageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0667D191247D162C005DE2ED /* MessageTableViewCell.swift */; };
0667D194247D1BA0005DE2ED /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0667D193247D1BA0005DE2ED /* Date+Extension.swift */; };
066890082D1946D500E106F2 /* MessageItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 066890072D1946D500E106F2 /* MessageItemView.swift */; };
0668900B2D19525400E106F2 /* ShowLessAndClearView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0668900A2D19525400E106F2 /* ShowLessAndClearView.swift */; };
0668900D2D19582400E106F2 /* MessageGroupHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0668900C2D19582400E106F2 /* MessageGroupHeaderView.swift */; };
066E0C8C2BB6AC9A00873838 /* AddSoundCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 066E0C8B2BB6AC9A00873838 /* AddSoundCell.swift */; };
0672CB06256903F700570C9D /* MessageListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0672CB05256903F700570C9D /* MessageListViewModel.swift */; };
06787C392A710568008ABDD7 /* GesturePassTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06787C382A710568008ABDD7 /* GesturePassTextView.swift */; };
@ -332,6 +335,9 @@
0661A54E204FDA4100965E4E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0667D191247D162C005DE2ED /* MessageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTableViewCell.swift; sourceTree = "<group>"; };
0667D193247D1BA0005DE2ED /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
066890072D1946D500E106F2 /* MessageItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageItemView.swift; sourceTree = "<group>"; };
0668900A2D19525400E106F2 /* ShowLessAndClearView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowLessAndClearView.swift; sourceTree = "<group>"; };
0668900C2D19582400E106F2 /* MessageGroupHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageGroupHeaderView.swift; sourceTree = "<group>"; };
066E0C8B2BB6AC9A00873838 /* AddSoundCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSoundCell.swift; sourceTree = "<group>"; };
0672CB05256903F700570C9D /* MessageListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewModel.swift; sourceTree = "<group>"; };
06787C382A710568008ABDD7 /* GesturePassTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GesturePassTextView.swift; sourceTree = "<group>"; };
@ -487,11 +493,10 @@
0604F7DC20620D3400B32F09 /* View */ = {
isa = PBXGroup;
children = (
066890092D19495400E106F2 /* MessageList */,
06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */,
0603706820E1F89500F4CA05 /* PreviewCardCell.swift */,
064CAB9D256BE9090018155C /* PreviewCardCellViewModel.swift */,
0667D191247D162C005DE2ED /* MessageTableViewCell.swift */,
067B2EB425693E38008B6BE1 /* MessageTableViewCellViewModel.swift */,
06C5952C2480E3F8006B98F3 /* LabelCell.swift */,
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */,
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */,
@ -667,6 +672,18 @@
path = Bark;
sourceTree = "<group>";
};
066890092D19495400E106F2 /* MessageList */ = {
isa = PBXGroup;
children = (
066890072D1946D500E106F2 /* MessageItemView.swift */,
0667D191247D162C005DE2ED /* MessageTableViewCell.swift */,
067B2EB425693E38008B6BE1 /* MessageTableViewCellViewModel.swift */,
0668900A2D19525400E106F2 /* ShowLessAndClearView.swift */,
0668900C2D19582400E106F2 /* MessageGroupHeaderView.swift */,
);
path = MessageList;
sourceTree = "<group>";
};
06CF784121C7A50300A052D7 /* NotificationServiceExtension */ = {
isa = PBXGroup;
children = (
@ -1205,6 +1222,7 @@
1EFB545D2C314A6800B8E51B /* BarkSplitViewController.swift in Sources */,
06BBB8C92567B6730076F63E /* Operators.swift in Sources */,
0603706920E1F89500F4CA05 /* PreviewCardCell.swift in Sources */,
066890082D1946D500E106F2 /* MessageItemView.swift in Sources */,
0627DABB298B6EA2002F3F69 /* DropBoxView.swift in Sources */,
06AE311C266F54A500B39FBB /* GroupTableViewCell.swift in Sources */,
0672CB06256903F700570C9D /* MessageListViewModel.swift in Sources */,
@ -1218,6 +1236,7 @@
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */,
06F08EA429B098DD006AB9CA /* CryptoSettingManager.swift in Sources */,
0668900B2D19525400E106F2 /* ShowLessAndClearView.swift in Sources */,
06BD4DAA2901352E003364DB /* Object+Dictionary.swift in Sources */,
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */,
@ -1277,6 +1296,7 @@
065BE4462563D7E5002A8CA4 /* ViewModelType.swift in Sources */,
0603706B20E20A7C00F4CA05 /* String+Extension.swift in Sources */,
068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */,
0668900D2D19582400E106F2 /* MessageGroupHeaderView.swift in Sources */,
1E73F99E2C282822002BF649 /* SectionViewController-iPad.swift in Sources */,
0689CF4C2C7484A7007203A6 /* BarkTabBarController.swift in Sources */,
06BBB8B72567AC140076F63E /* MessageSettingsViewModel.swift in Sources */,

View File

@ -2714,6 +2714,29 @@
}
}
},
"showLess" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Show less"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Katlamak"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "折叠"
}
}
}
},
"sourceCode" : {
"extractionState" : "manual",
"localizations" : {

View File

@ -176,7 +176,7 @@ class MessageListViewController: BaseViewController<MessageListViewModel> {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(MessageTableViewCell.self)") as? MessageTableViewCell else {
return UITableViewCell()
}
cell.bindViewModel(model: item)
cell.message = item.message
return cell
}, canEditRowAtIndexPath: { _, _ in
true

View File

@ -12,6 +12,13 @@ import RxCocoa
import RxDataSources
import RxSwift
enum MessageListCellItem {
///
case message(model: Message)
///
case messageGroup(group: [Message])
}
class MessageListViewModel: ViewModel, ViewModelType {
struct Input {
var refresh: Driver<Void>

View File

@ -0,0 +1,62 @@
//
// MessageGroupHeaderView.swift
// Bark
//
// Created by huangfeng on 12/23/24.
// Copyright © 2024 Fin. All rights reserved.
//
import UIKit
class MessageGroupHeaderView: UIView {
private let groupNameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.preferredFont(ofSize: 16, weight: .semibold)
label.textColor = BKColor.grey.darken4
return label
}()
private let showLessAndClearView: ShowLessAndClearView = {
let view = ShowLessAndClearView()
return view
}()
var groupName: String? {
didSet {
groupNameLabel.text = groupName
}
}
var showLessAction: (() -> Void)? {
didSet {
showLessAndClearView.showLessAction = showLessAction
}
}
var clearAction: (() -> Void)? {
didSet {
showLessAndClearView.clearAction = clearAction
}
}
init() {
super.init(frame: .zero)
addSubview(groupNameLabel)
addSubview(showLessAndClearView)
groupNameLabel.snp.makeConstraints { make in
make.left.equalTo(8)
make.top.equalTo(10)
make.bottom.equalTo(-10)
}
showLessAndClearView.snp.makeConstraints { make in
make.right.equalTo(-8)
make.centerY.equalTo(groupNameLabel)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,211 @@
//
// MessageItemView.swift
// Bark
//
// Created by huangfeng on 12/23/24.
// Copyright © 2024 Fin. All rights reserved.
//
import UIKit
enum MessageListCellDateStyle {
/// 11
case relative
/// 2024-01-01 12:00
case exact
}
class MessageItemView: UIView {
let panel: UIView = {
let view = UIView()
view.layer.cornerRadius = 10
view.backgroundColor = BKColor.background.secondary
return view
}()
let blackMaskView: UIView = {
let view = UIView()
view.layer.cornerRadius = 10
view.backgroundColor = UIColor.black
view.isUserInteractionEnabled = false
view.alpha = 0
return view
}()
let bodyLabel: UITextView = {
let label = UITextView()
label.backgroundColor = UIColor.clear
label.isEditable = false
label.dataDetectorTypes = [.phoneNumber, .link]
label.isScrollEnabled = false
label.textContainerInset = .zero
label.textContainer.lineFragmentPadding = 0
label.font = UIFont.preferredFont(ofSize: 14)
label.adjustsFontForContentSizeCategory = true
label.textColor = BKColor.grey.darken4
return label
}()
let dateLabel: UILabel = {
let label = BKLabel()
label.hitTestSlop = UIEdgeInsets(top: -5, left: -5, bottom: -5, right: -5)
label.font = UIFont.preferredFont(ofSize: 11, weight: .medium)
label.adjustsFontForContentSizeCategory = true
label.textColor = BKColor.grey.base
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer())
return label
}()
var message: Message? = nil {
didSet {
guard let message else {
return
}
setMessage(message: message)
}
}
var dateStyle: MessageListCellDateStyle = .relative {
didSet {
guard let message else {
return
}
switch dateStyle {
case .relative:
dateLabel.text = message.createDate?.agoFormatString()
case .exact:
dateLabel.text = message.createDate?.formatString(format: "yyyy-MM-dd HH:mm")
}
}
}
var maskAlpha: CGFloat = 0 {
didSet {
blackMaskView.alpha = maskAlpha
}
}
var isShowShadow: Bool = false {
didSet {
panel.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor
panel.layer.shadowOffset = CGSize(width: 0, height: 5)
panel.layer.shadowRadius = 5
panel.layer.shadowOpacity = isShowShadow ? 0.05 : 0
}
}
init() {
super.init(frame: .zero)
self.backgroundColor = BKColor.background.primary
self.addSubview(panel)
panel.addSubview(bodyLabel)
panel.addSubview(dateLabel)
panel.addSubview(blackMaskView)
layoutView()
//
dateLabel.gestureRecognizers?.first?.rx.event.subscribe(onNext: { [weak self] _ in
guard let self else { return }
if self.dateStyle != .exact {
self.dateStyle = .exact
} else {
self.dateStyle = .relative
}
}).disposed(by: rx.disposeBag)
}
convenience init(isShowShadow: Bool) {
self.init()
defer {
self.isShowShadow = isShowShadow
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func layoutView() {
bodyLabel.snp.makeConstraints { make in
make.top.equalTo(16)
make.left.equalTo(12)
make.right.equalTo(-12)
}
dateLabel.snp.makeConstraints { make in
make.left.equalTo(bodyLabel)
make.top.equalTo(bodyLabel.snp.bottom).offset(12)
make.bottom.equalTo(panel).offset(-12)
}
panel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.equalToSuperview().offset(-16)
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
blackMaskView.snp.makeConstraints { make in
make.edges.equalTo(panel)
}
}
}
extension MessageItemView {
func setMessage(message: Message) {
let title = message.title ?? ""
let subtitle = message.subtitle ?? ""
let body = message.body ?? ""
let url = message.url ?? ""
let text = NSMutableAttributedString(
string: body,
attributes: [.font: UIFont.preferredFont(ofSize: 14), .foregroundColor: BKColor.grey.darken4]
)
if subtitle.count > 0 {
// spacer
text.insert(NSAttributedString(
string: "\n",
attributes: [.font: UIFont.systemFont(ofSize: 6, weight: .medium)]
), at: 0)
text.insert(NSAttributedString(
string: subtitle + "\n",
attributes: [.font: UIFont.preferredFont(ofSize: 16, weight: .medium), .foregroundColor: BKColor.grey.darken4]
), at: 0)
}
if title.count > 0 {
// spacer
text.insert(NSAttributedString(
string: "\n",
attributes: [.font: UIFont.systemFont(ofSize: 6, weight: .medium)]
), at: 0)
text.insert(NSAttributedString(
string: title + "\n",
attributes: [.font: UIFont.preferredFont(ofSize: 16, weight: .medium), .foregroundColor: BKColor.grey.darken4]
), at: 0)
}
if url.count > 0 {
// spacer
text.append(NSAttributedString(
string: "\n ",
attributes: [.font: UIFont.systemFont(ofSize: 8, weight: .medium)]
))
text.append(NSAttributedString(string: "\n\(url)", attributes: [
.font: UIFont.preferredFont(ofSize: 14),
.foregroundColor: BKColor.grey.darken4,
.link: url
]))
}
self.bodyLabel.attributedText = text
self.dateStyle = .relative
}
}

View File

@ -0,0 +1,40 @@
//
// MessageTableViewCell.swift
// Bark
//
// Created by huangfeng on 2020/5/26.
// Copyright © 2020 Fin. All rights reserved.
//
import Material
import RxSwift
import UIKit
/// cell
class MessageTableViewCell: UITableViewCell {
let messageView = MessageItemView()
var message: Message? {
get {
return messageView.message
}
set {
messageView.message = newValue
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.contentView.backgroundColor = BKColor.background.primary
self.contentView.addSubview(messageView)
messageView.snp.makeConstraints { make in
make.left.top.right.equalToSuperview()
make.bottom.equalTo(-10)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -11,13 +11,6 @@ import Foundation
import RxCocoa
import RxDataSources
enum MessageListCellDateStyle {
/// 11
case relative
/// 2024-01-01 12:00
case exact
}
class MessageTableViewCellViewModel: ViewModel {
// 使crash
let message: Message

View File

@ -0,0 +1,188 @@
//
// ShowLessAndClearView.swift
// Bark
//
// Created by huangfeng on 12/23/24.
// Copyright © 2024 Fin. All rights reserved.
//
import SnapKit
import UIKit
class ShowLessAndClearView: UIView {
private let showLessView = ShowLessView()
private let clearView = ClearView()
private var clearViewWidthConstraint: Constraint? = nil
var showLessAction: (() -> Void)?
var clearAction: (() -> Void)?
init() {
super.init(frame: .zero)
self.addSubview(showLessView)
self.addSubview(clearView)
showLessView.snp.makeConstraints { make in
make.left.top.bottom.equalToSuperview()
make.right.equalTo(-28 - 5)
}
clearView.snp.makeConstraints { make in
make.centerY.right.equalToSuperview()
make.height.equalTo(28)
self.clearViewWidthConstraint = make.width.equalTo(28).constraint
}
clearView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(clearTap)))
showLessView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showLesstTap)))
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func clearTap() {
if clearView.isExpanded {
self.clearAction?()
return
}
self.buttonExpandAnimation(isClearViewExpanded: true)
}
@objc private func showLesstTap() {
if showLessView.isExpanded {
self.showLessAction?()
return
}
self.buttonExpandAnimation(isClearViewExpanded: false)
}
private func buttonExpandAnimation(isClearViewExpanded: Bool) {
UIView.animate(withDuration: 0.3) {
self.showLessView.isExpanded = !isClearViewExpanded
self.clearView.isExpanded = isClearViewExpanded
self.clearViewWidthConstraint?.update(offset: isClearViewExpanded ? self.bounds.width - 28 - 5 : 28)
self.layoutIfNeeded()
}
}
}
private class ShowLessView: UIView {
let panel: UIView = {
let view = UIView()
view.backgroundColor = BKColor.grey.lighten5
view.layer.cornerRadius = 28 / 2
view.clipsToBounds = true
return view
}()
let downArrow: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "baseline_keyboard_arrow_down_black_24pt")?.withRenderingMode(.alwaysTemplate))
imageView.tintColor = BKColor.grey.darken2
return imageView
}()
let showLessLabel: UILabel = {
let label = UILabel()
label.font = UIFont.preferredFont(ofSize: 12)
label.textColor = BKColor.grey.darken3
label.text = NSLocalizedString("showLess")
return label
}()
var isExpanded: Bool = true {
didSet {
refreshPanelLayout()
}
}
init() {
super.init(frame: .zero)
self.addSubview(panel)
panel.addSubview(downArrow)
panel.addSubview(showLessLabel)
downArrow.snp.makeConstraints { make in
make.left.equalTo(self).offset(2)
make.top.equalTo(self).offset(2)
make.bottom.equalTo(self).offset(-2)
make.width.height.equalTo(24)
}
showLessLabel.snp.makeConstraints { make in
make.left.equalTo(downArrow.snp.right).offset(2)
make.centerY.equalTo(self)
make.right.equalTo(self).offset(-10)
}
refreshPanelLayout()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func refreshPanelLayout() {
// panel
panel.snp.remakeConstraints { make in
make.left.top.bottom.equalToSuperview()
if isExpanded {
make.right.equalToSuperview()
} else {
make.width.equalTo(self.snp.height)
}
}
}
}
private class ClearView: UIView {
let clearIcon: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "baseline_close_white_48pt")?.withRenderingMode(.alwaysTemplate))
imageView.tintColor = BKColor.grey.darken2
imageView.contentMode = .scaleAspectFit
return imageView
}()
let clearLabel: UILabel = {
let label = UILabel()
label.font = UIFont.preferredFont(ofSize: 12)
label.textColor = BKColor.grey.darken3
label.text = NSLocalizedString("clear")
label.alpha = 0
return label
}()
var isExpanded: Bool = false {
didSet {
self.clearLabel.alpha = self.isExpanded ? 1 : 0
self.clearIcon.alpha = self.isExpanded ? 0 : 1
}
}
init() {
super.init(frame: .zero)
self.backgroundColor = BKColor.grey.lighten5
self.layer.cornerRadius = 28 / 2
self.clipsToBounds = true
self.addSubview(clearLabel)
self.addSubview(clearIcon)
clearIcon.snp.remakeConstraints { make in
make.right.equalToSuperview().offset(-4)
make.top.equalToSuperview().offset(4)
make.bottom.equalToSuperview().offset(-4)
make.width.height.equalTo(20)
}
clearLabel.snp.remakeConstraints { make in
make.center.equalToSuperview()
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -1,160 +0,0 @@
//
// MessageTableViewCell.swift
// Bark
//
// Created by huangfeng on 2020/5/26.
// Copyright © 2020 Fin. All rights reserved.
//
import Material
import RxSwift
import UIKit
class MessageTableViewCell: BaseTableViewCell<MessageTableViewCellViewModel> {
let backgroundPanel: UIView = {
let view = UIView()
view.layer.cornerRadius = 10
view.clipsToBounds = true
view.backgroundColor = BKColor.background.secondary
return view
}()
let bodyLabel: UITextView = {
let label = UITextView()
label.backgroundColor = UIColor.clear
label.isEditable = false
label.dataDetectorTypes = [.phoneNumber, .link]
label.isScrollEnabled = false
label.textContainerInset = .zero
label.textContainer.lineFragmentPadding = 0
label.font = UIFont.preferredFont(ofSize: 14)
label.adjustsFontForContentSizeCategory = true
label.textColor = BKColor.grey.darken4
return label
}()
let dateLabel: UILabel = {
let label = BKLabel()
label.hitTestSlop = UIEdgeInsets(top: -5, left: -5, bottom: -5, right: -5)
label.font = UIFont.preferredFont(ofSize: 11, weight: .medium)
label.adjustsFontForContentSizeCategory = true
label.textColor = BKColor.grey.base
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer())
return label
}()
let separatorLine: UIImageView = {
let imageView = UIImageView()
imageView.backgroundColor = BKColor.background.primary
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.backgroundColor = BKColor.background.primary
contentView.addSubview(backgroundPanel)
contentView.addSubview(bodyLabel)
contentView.addSubview(dateLabel)
contentView.addSubview(separatorLine)
layoutView()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func layoutView() {
bodyLabel.snp.remakeConstraints { make in
make.top.equalTo(16)
make.left.equalTo(28)
make.right.equalTo(-28)
}
dateLabel.snp.remakeConstraints { make in
make.left.equalTo(bodyLabel)
make.top.equalTo(bodyLabel.snp.bottom).offset(12)
}
separatorLine.snp.remakeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalTo(dateLabel.snp.bottom).offset(12)
make.height.equalTo(10)
}
backgroundPanel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.equalToSuperview().offset(-16)
make.top.equalToSuperview()
make.bottom.equalTo(separatorLine.snp.top)
}
}
override func bindViewModel(model: MessageTableViewCellViewModel) {
super.bindViewModel(model: model)
Observable.combineLatest(model.title, model.subtitle, model.body, model.url).subscribe { [weak self] title, subtitle, body, url in
guard let self else { return }
let text = NSMutableAttributedString(
string: body,
attributes: [.font: UIFont.preferredFont(ofSize: 14), .foregroundColor: BKColor.grey.darken4]
)
if subtitle.count > 0 {
// spacer
text.insert(NSAttributedString(
string: "\n",
attributes: [.font: UIFont.systemFont(ofSize: 6, weight: .medium)]
), at: 0)
text.insert(NSAttributedString(
string: subtitle + "\n",
attributes: [.font: UIFont.preferredFont(ofSize: 16, weight: .medium), .foregroundColor: BKColor.grey.darken4]
), at: 0)
}
if title.count > 0 {
// spacer
text.insert(NSAttributedString(
string: "\n",
attributes: [.font: UIFont.systemFont(ofSize: 6, weight: .medium)]
), at: 0)
text.insert(NSAttributedString(
string: title + "\n",
attributes: [.font: UIFont.preferredFont(ofSize: 16, weight: .medium), .foregroundColor: BKColor.grey.darken4]
), at: 0)
}
if url.count > 0 {
// spacer
text.append(NSAttributedString(
string: "\n ",
attributes: [.font: UIFont.systemFont(ofSize: 8, weight: .medium)]
))
text.append(NSAttributedString(string: "\n\(url)", attributes: [
.font: UIFont.preferredFont(ofSize: 14),
.foregroundColor: BKColor.grey.darken4,
.link: url
]))
}
self.bodyLabel.attributedText = text
}.disposed(by: rx.reuseBag)
model.date.bind(to: self.dateLabel.rx.text).disposed(by: rx.reuseBag)
//
dateLabel.gestureRecognizers?.first?.rx.event.subscribe(onNext: { _ in
if model.dateStyle.value != .exact {
model.dateStyle.accept(.exact)
} else {
model.dateStyle.accept(.relative)
}
}).disposed(by: rx.reuseBag)
}
}

View File

@ -1,6 +1,18 @@
### 个人用户
暂不支持单个请求推送到多台设备,可通过分别发送的方式实现多设备推送。
批量推送仅支持Json请求需 bark-server 更新至 v2.1.9。([https://api.day.app](https://api.day.app) 暂时不会更新至 v2.1.9, 目前还不支持批量推送)<br />
用法:
```sh
curl -X "POST" "https://api.day.app/push" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"title": "Title",
"body": "Body",
"sound": "minuet.caf",
"group": "test",
"device_keys": ["key1", "key2", ... ]
}'
```
### 中间服务
如果你的服务需要大批量且及时地向用户发送推送,建议自建服务端。可以提供 Url Scheme 方便用户一键更改服务器。