Skip to content

Commit b624d14

Browse files
authored
[Enhancement]Handle moderation events for blur and warning (#987)
1 parent 612a8c1 commit b624d14

File tree

6 files changed

+130
-0
lines changed

6 files changed

+130
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

55
# Upcoming
66

7+
### ✅ Added
8+
- SwiftUI modifiers that surface moderation blur and warning events in `CallView`. [#987](https://github.com/GetStream/stream-video-swift/pull/987)
9+
710
### 🔄 Changed
811

912
# [1.34.2](https://github.com/GetStream/stream-video-swift/releases/tag/1.34.2)

Sources/StreamVideoSwiftUI/CallContainer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public struct CallContainer<Factory: ViewFactory>: View {
6666
}
6767
.frame(maxWidth: .infinity, maxHeight: .infinity)
6868
.toastView(toast: $viewModel.toast)
69+
.moderationWarning(call: viewModel.call)
6970
.overlay(overlayView)
7071
.onReceive(viewModel.$callingState) { _ in
7172
if viewModel.callingState == .idle || viewModel.callingState == .inCall {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
import StreamVideo
7+
import SwiftUI
8+
9+
/// A view modifier that blurs a participant when moderation blur toggles.
10+
struct ModerationBlurViewModifier: ViewModifier {
11+
12+
var call: Call?
13+
var participant: CallParticipant
14+
var blurRadius: Float
15+
16+
@State var isBlurred: Bool = true
17+
18+
func body(content: Content) -> some View {
19+
Group {
20+
if isBlurred {
21+
content
22+
.blur(radius: .init(blurRadius))
23+
} else {
24+
content
25+
}
26+
}
27+
.onReceive(
28+
call?
29+
.eventPublisher(for: CallModerationBlurEvent.self)
30+
.filter { $0.userId == participant.userId }
31+
.map { _ in !isBlurred }
32+
.removeDuplicates()
33+
.receive(on: DispatchQueue.main)
34+
) { isBlurred = $0 }
35+
}
36+
}
37+
38+
extension View {
39+
40+
/// Applies a moderation blur effect that responds to moderation events.
41+
@ViewBuilder
42+
public func moderationBlur(
43+
call: Call?,
44+
participant: CallParticipant,
45+
blurRadius: Float = 30
46+
) -> some View {
47+
modifier(
48+
ModerationBlurViewModifier(
49+
call: call,
50+
participant: participant,
51+
blurRadius: blurRadius
52+
)
53+
)
54+
}
55+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
import StreamVideo
7+
import SwiftUI
8+
9+
/// A view modifier that shows a toast when moderation warnings arrive.
10+
struct ModerationWarningViewModifier: ViewModifier {
11+
12+
var call: Call?
13+
var placement: ToastPlacement
14+
var duration: TimeInterval
15+
16+
@State var toast: Toast?
17+
18+
func body(content: Content) -> some View {
19+
content
20+
.toastView(toast: $toast)
21+
.onReceive(
22+
call?
23+
.eventPublisher(for: CallModerationWarningEvent.self)
24+
.map {
25+
Toast(
26+
style: .warning,
27+
message: $0.message,
28+
placement: placement,
29+
duration: duration
30+
)
31+
}
32+
.receive(on: DispatchQueue.main)
33+
) { toast = $0 }
34+
}
35+
}
36+
37+
extension View {
38+
39+
/// Presents a moderation warning toast driven by moderation events.
40+
@ViewBuilder
41+
public func moderationWarning(
42+
call: Call?,
43+
placement: ToastPlacement = .top,
44+
duration: TimeInterval = 2.5
45+
) -> some View {
46+
modifier(
47+
ModerationWarningViewModifier(
48+
call: call,
49+
placement: placement,
50+
duration: duration
51+
)
52+
)
53+
}
54+
}

Sources/StreamVideoSwiftUI/ViewFactory.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ extension ViewFactory {
273273
customData: customData,
274274
call: call
275275
)
276+
.moderationBlur(call: call, participant: participant)
276277
}
277278

278279
public func makeVideoCallParticipantModifier(

StreamVideo.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,8 @@
515515
40A0FFBB2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFBA2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift */; };
516516
40A0FFBE2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFBD2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift */; };
517517
40A0FFC02EA6418000F39D8F /* Sequence+AsyncReduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A0FFBF2EA6418000F39D8F /* Sequence+AsyncReduce.swift */; };
518+
40A317E82EB504C900733948 /* ModerationBlurViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A317E72EB504C900733948 /* ModerationBlurViewModifier.swift */; };
519+
40A317EB2EB5081500733948 /* ModerationWarningViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A317EA2EB5081500733948 /* ModerationWarningViewModifier.swift */; };
518520
40A7C5B52E099B4600EEDF9C /* ParticipantEventResetAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A7C5B22E099B1000EEDF9C /* ParticipantEventResetAdapter.swift */; };
519521
40A7C5B82E099D6200EEDF9C /* ParticipantEventResetAdapter_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A7C5B72E099D6200EEDF9C /* ParticipantEventResetAdapter_Tests.swift */; };
520522
40A9416E2B4D959F006D6965 /* StreamPictureInPictureAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A9416D2B4D959F006D6965 /* StreamPictureInPictureAdapter.swift */; };
@@ -2210,6 +2212,8 @@
22102212
40A0FFBA2EA63E9A00F39D8F /* BatteryStore+DefaultReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+DefaultReducer.swift"; sourceTree = "<group>"; };
22112213
40A0FFBD2EA63FE500F39D8F /* BatteryStore+ObservationMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BatteryStore+ObservationMiddleware.swift"; sourceTree = "<group>"; };
22122214
40A0FFBF2EA6418000F39D8F /* Sequence+AsyncReduce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+AsyncReduce.swift"; sourceTree = "<group>"; };
2215+
40A317E72EB504C900733948 /* ModerationBlurViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerationBlurViewModifier.swift; sourceTree = "<group>"; };
2216+
40A317EA2EB5081500733948 /* ModerationWarningViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerationWarningViewModifier.swift; sourceTree = "<group>"; };
22132217
40A7C5B22E099B1000EEDF9C /* ParticipantEventResetAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantEventResetAdapter.swift; sourceTree = "<group>"; };
22142218
40A7C5B72E099D6200EEDF9C /* ParticipantEventResetAdapter_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantEventResetAdapter_Tests.swift; sourceTree = "<group>"; };
22152219
40A9416D2B4D959F006D6965 /* StreamPictureInPictureAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamPictureInPictureAdapter.swift; sourceTree = "<group>"; };
@@ -4910,6 +4914,15 @@
49104914
path = Middleware;
49114915
sourceTree = "<group>";
49124916
};
4917+
40A317E62EB504B900733948 /* Moderation */ = {
4918+
isa = PBXGroup;
4919+
children = (
4920+
40A317E72EB504C900733948 /* ModerationBlurViewModifier.swift */,
4921+
40A317EA2EB5081500733948 /* ModerationWarningViewModifier.swift */,
4922+
);
4923+
path = Moderation;
4924+
sourceTree = "<group>";
4925+
};
49134926
40A7C5B42E099B1600EEDF9C /* ParticipantEventResetAdapter */ = {
49144927
isa = PBXGroup;
49154928
children = (
@@ -5559,6 +5572,7 @@
55595572
40C7B82A2B612D5100FB9DB2 /* ViewModifiers */ = {
55605573
isa = PBXGroup;
55615574
children = (
5575+
40A317E62EB504B900733948 /* Moderation */,
55625576
403EFC9E2BDBFE050057C248 /* CallEndedViewModifier.swift */,
55635577
408D29A02B6D208700885473 /* Snapshot */,
55645578
409145E92B68FDD2007F3C17 /* ReadableContentGuide */,
@@ -9474,6 +9488,7 @@
94749488
84F3B0DE28913E0F0088751D /* CallControlsView.swift in Sources */,
94759489
8435EB9029CDAADA00E02651 /* ParticipantsGridLayout.swift in Sources */,
94769490
8434C52D289AA41D0001490A /* ImageExtensions.swift in Sources */,
9491+
40A317E82EB504C900733948 /* ModerationBlurViewModifier.swift in Sources */,
94779492
849EDA8B297AFCC80072A12D /* PreJoiningView.swift in Sources */,
94789493
84D425082AA61E9900473150 /* LivestreamPlayer.swift in Sources */,
94799494
40A941762B4D9F16006D6965 /* PictureInPictureSourceView.swift in Sources */,
@@ -9487,6 +9502,7 @@
94879502
843697D228C7A25F00839D99 /* ParticipantsGridView.swift in Sources */,
94889503
840042CF2A70212D00917B30 /* ScreensharingControls.swift in Sources */,
94899504
40C7B8342B613A8200FB9DB2 /* ControlBadgeView.swift in Sources */,
9505+
40A317EB2EB5081500733948 /* ModerationWarningViewModifier.swift in Sources */,
94909506
8406269A2A37A5E2004B8748 /* CallEvents.swift in Sources */,
94919507
40C7B8362B613C7800FB9DB2 /* ParticipantsListButton.swift in Sources */,
94929508
40AA2EE22AE0137E000DCA5C /* ClipCorners.swift in Sources */,

0 commit comments

Comments
 (0)