Skip to content

Commit ba10b0b

Browse files
committed
Added Client.unreadCount.
1 parent 3d078b1 commit ba10b0b

File tree

8 files changed

+109
-18
lines changed

8 files changed

+109
-18
lines changed

Example/ChatExample/Base.lproj/Main.storyboard

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
8484
<subviews>
8585
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="fQM-5J-VyU">
86-
<rect key="frame" x="16" y="64" width="343" height="250"/>
86+
<rect key="frame" x="16" y="64" width="343" height="299"/>
8787
<subviews>
8888
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lMz-sX-zDG">
8989
<rect key="frame" x="0.0" y="0.0" width="128" height="30"/>
@@ -130,21 +130,34 @@
130130
<constraint firstAttribute="height" constant="1" id="w5I-Ww-kht"/>
131131
</constraints>
132132
</view>
133+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Total unread count: &lt;Disabled&gt;" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tp6-sE-CBi">
134+
<rect key="frame" x="0.0" y="183" width="215" height="18"/>
135+
<fontDescription key="fontDescription" type="system" pointSize="15"/>
136+
<nil key="textColor"/>
137+
<nil key="highlightedColor"/>
138+
</label>
139+
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="p8S-Fb-tWu">
140+
<rect key="frame" x="0.0" y="216" width="343" height="1"/>
141+
<color key="backgroundColor" red="0.83741801979999997" green="0.83743780850000005" blue="0.83742713930000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
142+
<constraints>
143+
<constraint firstAttribute="height" constant="1" id="RCM-pe-Xap"/>
144+
</constraints>
145+
</view>
133146
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Online members: &lt;Disabled&gt;" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LML-Ms-XJg">
134-
<rect key="frame" x="0.0" y="183" width="199" height="18"/>
147+
<rect key="frame" x="0.0" y="232" width="199" height="18"/>
135148
<fontDescription key="fontDescription" type="system" pointSize="15"/>
136149
<nil key="textColor"/>
137150
<nil key="highlightedColor"/>
138151
</label>
139152
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Mr8-ak-oSo">
140-
<rect key="frame" x="0.0" y="216" width="343" height="1"/>
153+
<rect key="frame" x="0.0" y="265" width="343" height="1"/>
141154
<color key="backgroundColor" red="0.83741801979999997" green="0.83743780850000005" blue="0.83742713930000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
142155
<constraints>
143156
<constraint firstAttribute="height" constant="1" id="2lJ-fr-F2c"/>
144157
</constraints>
145158
</view>
146159
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Push notifications:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MVq-bk-kzB">
147-
<rect key="frame" x="0.0" y="232" width="130.5" height="18"/>
160+
<rect key="frame" x="0.0" y="281" width="130.5" height="18"/>
148161
<fontDescription key="fontDescription" type="system" pointSize="15"/>
149162
<nil key="textColor"/>
150163
<nil key="highlightedColor"/>
@@ -155,6 +168,7 @@
155168
<constraint firstItem="gbU-nu-nAs" firstAttribute="width" secondItem="fQM-5J-VyU" secondAttribute="width" id="FUK-FQ-UjH"/>
156169
<constraint firstItem="NfR-jx-apB" firstAttribute="width" secondItem="fQM-5J-VyU" secondAttribute="width" id="W57-dd-fAC"/>
157170
<constraint firstItem="Mr8-ak-oSo" firstAttribute="width" secondItem="fQM-5J-VyU" secondAttribute="width" id="Z3W-3U-xbk"/>
171+
<constraint firstItem="p8S-Fb-tWu" firstAttribute="width" secondItem="fQM-5J-VyU" secondAttribute="width" id="e2b-zi-XFR"/>
158172
</constraints>
159173
</stackView>
160174
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eTn-JY-iPB">
@@ -201,19 +215,22 @@ badge</string>
201215
<nil key="highlightedColor"/>
202216
</label>
203217
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="jrn-MT-IQv">
204-
<rect key="frame" x="310" y="240.5" width="51" height="31"/>
218+
<rect key="frame" x="310" y="289.5" width="51" height="31"/>
205219
</switch>
206220
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="H0k-lx-tmL">
207-
<rect key="frame" x="310" y="289.5" width="51" height="31"/>
221+
<rect key="frame" x="310" y="338.5" width="51" height="31"/>
222+
</switch>
223+
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="1lG-cg-5R8">
224+
<rect key="frame" x="310" y="240.5" width="51" height="31"/>
208225
</switch>
209226
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="It doesn't hold the state and to disable Push notifications you need to toggle it twice." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="B0u-JV-sSq">
210-
<rect key="frame" x="16" y="319" width="283" height="29"/>
227+
<rect key="frame" x="16" y="368" width="283" height="29"/>
211228
<fontDescription key="fontDescription" type="system" pointSize="12"/>
212229
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
213230
<nil key="highlightedColor"/>
214231
</label>
215232
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7rg-Xd-yMa">
216-
<rect key="frame" x="0.0" y="363" width="375" height="1"/>
233+
<rect key="frame" x="0.0" y="412" width="375" height="1"/>
217234
<color key="backgroundColor" red="0.83741801979999997" green="0.83743780850000005" blue="0.83742713930000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
218235
<constraints>
219236
<constraint firstAttribute="height" constant="1" id="b5l-qn-iGf"/>
@@ -236,6 +253,7 @@ Stream Swift SDK v.1.0.0</string>
236253
<constraint firstItem="7rg-Xd-yMa" firstAttribute="leading" secondItem="5vB-qP-Wuo" secondAttribute="leading" id="BrI-Wb-SCz"/>
237254
<constraint firstItem="OkG-5O-emD" firstAttribute="centerY" secondItem="jQp-EN-Voh" secondAttribute="centerY" id="EtX-0u-umn"/>
238255
<constraint firstItem="B0u-JV-sSq" firstAttribute="leading" secondItem="MVq-bk-kzB" secondAttribute="leading" id="G4B-f4-p79"/>
256+
<constraint firstItem="1lG-cg-5R8" firstAttribute="centerY" secondItem="tp6-sE-CBi" secondAttribute="centerY" id="I6Y-59-cBo"/>
239257
<constraint firstItem="jQp-EN-Voh" firstAttribute="trailing" secondItem="fQM-5J-VyU" secondAttribute="trailing" id="JHC-nQ-XuY"/>
240258
<constraint firstItem="eTn-JY-iPB" firstAttribute="centerY" secondItem="5Of-ZP-Ztv" secondAttribute="centerY" constant="-5" id="Jfi-ED-ACl"/>
241259
<constraint firstItem="H0k-lx-tmL" firstAttribute="centerY" secondItem="MVq-bk-kzB" secondAttribute="centerY" id="JiW-cq-VUP"/>
@@ -252,6 +270,7 @@ Stream Swift SDK v.1.0.0</string>
252270
<constraint firstItem="fQM-5J-VyU" firstAttribute="top" secondItem="pUb-7p-fIc" secondAttribute="top" constant="20" id="bmR-kQ-ZPs"/>
253271
<constraint firstItem="jrn-MT-IQv" firstAttribute="centerY" secondItem="LML-Ms-XJg" secondAttribute="centerY" id="hcw-6a-JAC"/>
254272
<constraint firstItem="B0u-JV-sSq" firstAttribute="top" secondItem="MVq-bk-kzB" secondAttribute="bottom" constant="5" id="qS7-8L-5W1"/>
273+
<constraint firstItem="1lG-cg-5R8" firstAttribute="trailing" secondItem="fQM-5J-VyU" secondAttribute="trailing" id="qTd-Gz-MV4"/>
255274
<constraint firstItem="pUb-7p-fIc" firstAttribute="bottom" secondItem="Fyw-02-BDi" secondAttribute="bottom" constant="20" id="vik-Ho-cFz"/>
256275
<constraint firstItem="7rg-Xd-yMa" firstAttribute="top" secondItem="B0u-JV-sSq" secondAttribute="bottom" constant="15" id="zGc-6l-Vej"/>
257276
</constraints>
@@ -264,6 +283,8 @@ Stream Swift SDK v.1.0.0</string>
264283
<outlet property="notificationsSwitch" destination="H0k-lx-tmL" id="MCi-8p-JQM"/>
265284
<outlet property="onlineSwitch" destination="jrn-MT-IQv" id="cmf-96-hY1"/>
266285
<outlet property="onlinelabel" destination="LML-Ms-XJg" id="Lgb-US-mZB"/>
286+
<outlet property="totalUnreadCountLabel" destination="tp6-sE-CBi" id="Kfe-Qa-uQ7"/>
287+
<outlet property="totalUnreadCountSwitch" destination="1lG-cg-5R8" id="4Mk-4p-lEG"/>
267288
<outlet property="versionLabel" destination="Fyw-02-BDi" id="n0D-xi-gJO"/>
268289
</connections>
269290
</viewController>
@@ -485,7 +506,7 @@ Stream Swift SDK v.1.0.0</string>
485506
</scenes>
486507
<inferredMetricsTieBreakers>
487508
<segue reference="Z6X-IU-Iab"/>
488-
<segue reference="a5b-Jp-cN7"/>
509+
<segue reference="elX-bx-MbU"/>
489510
<segue reference="seW-P6-fEX"/>
490511
</inferredMetricsTieBreakers>
491512
</document>

Example/ChatExample/RootViewController.swift

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import StreamChatCore
1313

1414
final class RootViewController: UIViewController {
1515

16+
@IBOutlet weak var totalUnreadCountLabel: UILabel!
17+
@IBOutlet weak var totalUnreadCountSwitch: UISwitch!
1618
@IBOutlet weak var badgeLabel: UILabel!
1719
@IBOutlet weak var badgeSwitch: UISwitch!
1820
@IBOutlet weak var onlinelabel: UILabel!
@@ -21,6 +23,7 @@ final class RootViewController: UIViewController {
2123
@IBOutlet weak var versionLabel: UILabel!
2224

2325
let disposeBag = DisposeBag()
26+
var totalUnreadCountDisposeBag = DisposeBag()
2427
var badgeDisposeBag = DisposeBag()
2528
var onlineDisposeBag = DisposeBag()
2629
let channel = Channel(type: .messaging, id: "general")
@@ -29,7 +32,7 @@ final class RootViewController: UIViewController {
2932
super.viewDidLoad()
3033
setupNotifications()
3134
navigationController?.navigationBar.prefersLargeTitles = true
32-
35+
3336
if let user = User.current {
3437
navigationItem.rightBarButtonItem = UIBarButtonItem(title: user.name.capitalized,
3538
style: .plain,
@@ -42,6 +45,17 @@ final class RootViewController: UIViewController {
4245

4346
versionLabel.text = "Demo Project\nStream Swift SDK v.\(Client.version)"
4447

48+
totalUnreadCountSwitch.rx.isOn.changed
49+
.subscribe(onNext: { [weak self] isOn in
50+
if isOn {
51+
self?.subscribeForTotalUnreadCount()
52+
} else {
53+
self?.totalUnreadCountDisposeBag = DisposeBag()
54+
self?.totalUnreadCountLabel.text = "Total unread count: <Disabled>"
55+
}
56+
})
57+
.disposed(by: disposeBag)
58+
4559
badgeSwitch.rx.isOn.changed
4660
.subscribe(onNext: { [weak self] isOn in
4761
if isOn {
@@ -65,11 +79,19 @@ final class RootViewController: UIViewController {
6579
.disposed(by: disposeBag)
6680
}
6781

82+
func subscribeForTotalUnreadCount() {
83+
Client.shared.unreadCount
84+
.drive(onNext: { [weak self] unreadCount in
85+
self?.totalUnreadCountLabel.text = "Unread channels \(unreadCount.0), messages: \(unreadCount.1)"
86+
UIApplication.shared.applicationIconBadgeNumber = unreadCount.messages
87+
})
88+
.disposed(by: totalUnreadCountDisposeBag)
89+
}
90+
6891
func subscribeForUnreadCount() {
6992
channel.unreadCount
70-
.drive(onNext: { [weak self] count in
71-
self?.badgeLabel.text = "\(count == 100 ? "99+" : String(count)) "
72-
UIApplication.shared.applicationIconBadgeNumber = count
93+
.drive(onNext: { [weak self] unreadCount in
94+
self?.badgeLabel.text = "\(unreadCount == 100 ? "99+" : String(unreadCount)) "
7395
})
7496
.disposed(by: badgeDisposeBag)
7597
}
@@ -102,7 +124,7 @@ final class RootViewController: UIViewController {
102124

103125
func setupNotifications() {
104126
notificationsSwitch.rx.isOn.changed
105-
.flatMapLatest { isOn -> Observable<Void> in
127+
.flatMapLatest({ isOn -> Observable<Void> in
106128
if isOn {
107129
Notifications.shared.askForPermissionsIfNeeded()
108130
return .empty()
@@ -113,7 +135,7 @@ final class RootViewController: UIViewController {
113135
}
114136

115137
return .empty()
116-
}
138+
})
117139
.subscribe()
118140
.disposed(by: disposeBag)
119141
}

Sources/Core/Client/Client+Events.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import Foundation
1010
import RxSwift
11+
import RxCocoa
1112

1213
// MARK: - Events
1314

@@ -76,3 +77,41 @@ public extension Client {
7677
.share()
7778
}
7879
}
80+
81+
// MARK: - Unread Count
82+
83+
extension Client {
84+
public typealias UnreadCount = (channels: Int, messages: Int)
85+
86+
/// Observe an unread count of messages in the channel.
87+
///
88+
/// - Note: Be sure the current user is a member of the channel.
89+
/// - Note: 100 is the maximum unread count of messages.
90+
public var unreadCount: Driver<UnreadCount> {
91+
return Client.shared.connection.connected()
92+
// Subscribe for new messages and read events.
93+
.flatMapLatest({ [weak self] _ in
94+
Client.shared.webSocket.response
95+
.filter { self?.updateUnreadCount($0) ?? false }
96+
.map { _ in self?.unreadCountAtomic.get() }
97+
.startWith(self?.unreadCountAtomic.get())
98+
.unwrap()
99+
})
100+
.startWith((0, 0))
101+
.map { "\($0.0), \($0.1)" }
102+
.distinctUntilChanged()
103+
.map { [weak self] _ in self?.unreadCountAtomic.get() ?? (0, 0) }
104+
.asDriver(onErrorJustReturn: (0, 0))
105+
}
106+
107+
func updateUnreadCount(_ response: WebSocket.Response) -> Bool {
108+
switch response.event {
109+
case .notificationMarkRead(_, let messagesUnreadCount, let channelsUnreadCount, _),
110+
.messageNew(_, let messagesUnreadCount, let channelsUnreadCount, _, _):
111+
unreadCountAtomic.set((channelsUnreadCount, messagesUnreadCount))
112+
return true
113+
default:
114+
return false
115+
}
116+
}
117+
}

Sources/Core/Client/Client.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public final class Client {
4646
/// The current user.
4747
public var user: User?
4848

49+
var unreadCountAtomic = Atomic<UnreadCount>((0, 0))
50+
4951
/// An observable client web socket connection.
5052
///
5153
/// The connection is responsible for:

Sources/Core/Client/WebSocket/WebSocket.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ extension WebSocket {
191191
let user = healthCheckUser {
192192
lastConnectionId = connectionId
193193
handshakeTimer.resume()
194+
Client.shared.unreadCountAtomic.set((user.channelsUnreadCount, user.messagesUnreadCount))
194195
logger?.log("🥰 Connected")
195196
return .connected(connectionId, user)
196197
} else if lastJSONError != nil {

Sources/Core/Model/Channel+Events.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ extension Channel {
8787
}
8888
.startWith(0)
8989
.distinctUntilChanged()
90-
.share(replay: 1)
9190
.asDriver(onErrorJustReturn: 0)
9291
}
9392

@@ -199,7 +198,6 @@ extension Channel {
199198
return []
200199
}
201200
.distinctUntilChanged()
202-
.share(replay: 1)
203201
.asDriver(onErrorJustReturn: [])
204202
}
205203
}

Sources/Core/Model/Event.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public enum Event: Decodable {
8989
case channel
9090
case message
9191
case reaction
92-
case unreadCount = "total_unread_count"
92+
case unreadCount = "unread_count"
9393
case unreadChannels = "unread_channels"
9494
case created = "created_at"
9595
}

0 commit comments

Comments
 (0)