Skip to content

Commit 84bbae2

Browse files
implemented typing indicator view in the channel list item
1 parent 1ed409f commit 84bbae2

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
lines changed

Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ public struct ChatChannelListItem: View {
4646
Spacer()
4747
}
4848
} else {
49-
SubtitleText(text: subtitleText)
49+
HStack(spacing: 4) {
50+
if !channel.currentlyTypingUsers.isEmpty {
51+
TypingIndicatorView()
52+
}
53+
SubtitleText(text: subtitleText)
54+
Spacer()
55+
}
5056
}
5157
}
5258

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// Copyright © 2022 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import SwiftUI
6+
7+
/// View shown when other users are typing.
8+
struct TypingIndicatorView: View {
9+
10+
@State private var isTyping = false
11+
12+
private let animationDuration: CGFloat = 0.75
13+
14+
var body: some View {
15+
HStack(spacing: 4) {
16+
TypingIndicatorCircle(isTyping: isTyping)
17+
.animation(
18+
.easeOut(duration: animationDuration)
19+
.repeatForever(autoreverses: true), value: isTyping
20+
)
21+
TypingIndicatorCircle(isTyping: isTyping)
22+
.animation(
23+
.easeInOut(duration: animationDuration)
24+
.repeatForever(autoreverses: true), value: isTyping
25+
)
26+
TypingIndicatorCircle(isTyping: isTyping)
27+
.animation(
28+
.easeIn(duration: animationDuration)
29+
.repeatForever(autoreverses: true), value: isTyping
30+
)
31+
}
32+
.onAppear {
33+
isTyping = true
34+
}
35+
}
36+
}
37+
38+
/// View that represents one circle of the typing indicator view.
39+
private struct TypingIndicatorCircle: View {
40+
41+
private let circleWidth: CGFloat = 4
42+
private let circleHeight: CGFloat = 4
43+
private let yOffset: CGFloat = 1.5
44+
private let minOpacity: CGFloat = 0.1
45+
private let maxOpacity: CGFloat = 1.0
46+
47+
var isTyping: Bool
48+
49+
var body: some View {
50+
Circle()
51+
.frame(width: circleWidth, height: circleHeight)
52+
.opacity(isTyping ? maxOpacity : minOpacity)
53+
.offset(y: isTyping ? yOffset : -yOffset)
54+
}
55+
}

StreamChatSwiftUI.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
8465FDD72746A95800AF091E /* Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8465FD692746A95700AF091E /* Appearance.swift */; };
153153
8465FDD82746AA2700AF091E /* StreamChatSwiftUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 8465FD602746A95700AF091E /* StreamChatSwiftUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
154154
8465FDDD2747A14700AF091E /* CustomComposerAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8465FDDC2747A14700AF091E /* CustomComposerAttachmentView.swift */; };
155+
846608E3278C303800D3D7B3 /* TypingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846608E2278C303800D3D7B3 /* TypingIndicatorView.swift */; };
155156
848399EA275FB3E9003075E4 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 848399E9275FB3E9003075E4 /* SnapshotTesting */; };
156157
848399EC275FB41B003075E4 /* ChatChannelListView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848399EB275FB41B003075E4 /* ChatChannelListView_Tests.swift */; };
157158
848399F227601231003075E4 /* ReactionsOverlayView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848399F127601231003075E4 /* ReactionsOverlayView_Tests.swift */; };
@@ -453,6 +454,7 @@
453454
8465FD682746A95700AF091E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
454455
8465FD692746A95700AF091E /* Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Appearance.swift; sourceTree = "<group>"; };
455456
8465FDDC2747A14700AF091E /* CustomComposerAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomComposerAttachmentView.swift; sourceTree = "<group>"; };
457+
846608E2278C303800D3D7B3 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
456458
848399EB275FB41B003075E4 /* ChatChannelListView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatChannelListView_Tests.swift; sourceTree = "<group>"; };
457459
848399F127601231003075E4 /* ReactionsOverlayView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsOverlayView_Tests.swift; sourceTree = "<group>"; };
458460
849CDD932768E0E1003C7A51 /* MessageActionsResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActionsResolver.swift; sourceTree = "<group>"; };
@@ -771,6 +773,7 @@
771773
8434E582277088D9001E1B83 /* TitleWithCloseButton.swift */,
772774
84AB7B1C2771F4AA00631A10 /* DiscardButtonView.swift */,
773775
8465FD4B2746A95600AF091E /* LoadingView.swift */,
776+
846608E2278C303800D3D7B3 /* TypingIndicatorView.swift */,
774777
);
775778
path = CommonViews;
776779
sourceTree = "<group>";
@@ -1445,6 +1448,7 @@
14451448
8465FDAB2746A95700AF091E /* StringExtensions.swift in Sources */,
14461449
8465FDA52746A95700AF091E /* Modifiers.swift in Sources */,
14471450
8465FDBB2746A95700AF091E /* LoadingView.swift in Sources */,
1451+
846608E3278C303800D3D7B3 /* TypingIndicatorView.swift in Sources */,
14481452
8465FD9C2746A95700AF091E /* MessageActionsView.swift in Sources */,
14491453
8465FD6E2746A95700AF091E /* DependencyInjection.swift in Sources */,
14501454
841B64D62775FDA00016FF3B /* InstantCommandsHandler.swift in Sources */,

0 commit comments

Comments
 (0)