mirror of
https://github.com/Finb/Bark.git
synced 2025-12-08 21:36:01 +00:00
Compare commits
2 Commits
1e89b6eb24
...
80732720ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80732720ce | ||
|
|
9219bcf757 |
@ -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 */,
|
||||
|
||||
@ -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" : {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
62
View/MessageList/MessageGroupHeaderView.swift
Normal file
62
View/MessageList/MessageGroupHeaderView.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
211
View/MessageList/MessageItemView.swift
Normal file
211
View/MessageList/MessageItemView.swift
Normal 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 {
|
||||
/// 相对时间,例如 1分钟前、1小时前
|
||||
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
|
||||
}
|
||||
}
|
||||
40
View/MessageList/MessageTableViewCell.swift
Normal file
40
View/MessageList/MessageTableViewCell.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
@ -11,13 +11,6 @@ import Foundation
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
enum MessageListCellDateStyle {
|
||||
/// 相对时间,例如 1分钟前、1小时前
|
||||
case relative
|
||||
/// 精确时间,例如 2024-01-01 12:00
|
||||
case exact
|
||||
}
|
||||
|
||||
class MessageTableViewCellViewModel: ViewModel {
|
||||
// 不要在删除消息后,再次使用这个对象,否则会crash
|
||||
let message: Message
|
||||
188
View/MessageList/ShowLessAndClearView.swift
Normal file
188
View/MessageList/ShowLessAndClearView.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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 方便用户一键更改服务器。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user