RtcManager:新增TRTC语音服务
This commit is contained in:
2
Podfile
2
Podfile
@@ -35,6 +35,8 @@ target 'xplan-ios' do
|
||||
pod 'MarqueeLabel-ObjC'
|
||||
#声网
|
||||
pod 'AgoraRtcEngine_iOS', '~> 3.0.1'
|
||||
#TRTC
|
||||
pod 'TXLiteAVSDK_TRTC', :podspec => 'http://pod-1252463788.cosgz.myqcloud.com/liteavsdkspec/TXLiteAVSDK_TRTC.podspec'
|
||||
#pop动画
|
||||
pod 'pop', '~> 1.0.12'
|
||||
#云信
|
||||
|
@@ -68,6 +68,7 @@ PODS:
|
||||
- SVGAPlayer/ProtoFiles (2.5.7):
|
||||
- Protobuf (~> 3.4)
|
||||
- SZTextView (1.3.0)
|
||||
- TXLiteAVSDK_TRTC (8.7.10102)
|
||||
- UMCommon (7.3.5):
|
||||
- UMDevice
|
||||
- UMDevice (2.0.5)
|
||||
@@ -102,6 +103,7 @@ DEPENDENCIES:
|
||||
- SSKeychain
|
||||
- SVGAPlayer (~> 2.3)
|
||||
- SZTextView
|
||||
- TXLiteAVSDK_TRTC (from `http://pod-1252463788.cosgz.myqcloud.com/liteavsdkspec/TXLiteAVSDK_TRTC.podspec`)
|
||||
- UMCommon
|
||||
- UMDevice
|
||||
- YYText
|
||||
@@ -140,6 +142,10 @@ SPEC REPOS:
|
||||
- UMDevice
|
||||
- YYText
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
TXLiteAVSDK_TRTC:
|
||||
:podspec: http://pod-1252463788.cosgz.myqcloud.com/liteavsdkspec/TXLiteAVSDK_TRTC.podspec
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
|
||||
AgoraRtcEngine_iOS: 8ccceaaecff2e80ab28fcd33f3dfd2b417eb5365
|
||||
@@ -169,10 +175,11 @@ SPEC CHECKSUMS:
|
||||
SSZipArchive: e7b4f3d9e780c2acc1764cd88fbf2de28f26e5b2
|
||||
SVGAPlayer: 318b85a78b61292d6ae9dfcd651f3f0d1cdadd86
|
||||
SZTextView: 094dc6acc9beec537685c545d6e3e0d4975174e1
|
||||
TXLiteAVSDK_TRTC: e78365f430926a1064e175fd8d97601559e7d3d1
|
||||
UMCommon: ab4d875ddefe1b06c60b577e4a58bc4d433ee067
|
||||
UMDevice: c13bbb2e8ca6c67d1e23e03162553e3ec5a8b5b0
|
||||
YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
|
||||
|
||||
PODFILE CHECKSUM: 5784f27468bf1fc975791ded5ab665168b6d1611
|
||||
PODFILE CHECKSUM: 90bfc18d3332c11bf6da588fa02ae11025118de0
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
@@ -102,6 +102,7 @@
|
||||
18EE401A2754BA9F00A452BF /* NIMMessageMaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 18EE40182754BA9F00A452BF /* NIMMessageMaker.m */; };
|
||||
18F403CB2758C66800A6C548 /* MessageContentText.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F403CA2758C66800A6C548 /* MessageContentText.m */; };
|
||||
18F403EE2758CF2F00A6C548 /* MessageContentImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F403ED2758CF2F00A6C548 /* MessageContentImage.m */; };
|
||||
18F4043A275E20D900A6C548 /* TRTCRtcImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F40439275E20D900A6C548 /* TRTCRtcImpl.m */; };
|
||||
73FFADDC93E195344047A2EC /* Pods_xplan_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACF623970097D653132D69A /* Pods_xplan_ios.framework */; };
|
||||
9B0E1C5926E77022005D4442 /* BaseNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E1C5826E77022005D4442 /* BaseNavigationController.m */; };
|
||||
9B7D804A2753783D003DAC0C /* SessionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B7D80492753783D003DAC0C /* SessionViewController.m */; };
|
||||
@@ -496,6 +497,8 @@
|
||||
18F403CA2758C66800A6C548 /* MessageContentText.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MessageContentText.m; sourceTree = "<group>"; };
|
||||
18F403EC2758CF2F00A6C548 /* MessageContentImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageContentImage.h; sourceTree = "<group>"; };
|
||||
18F403ED2758CF2F00A6C548 /* MessageContentImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MessageContentImage.m; sourceTree = "<group>"; };
|
||||
18F40438275E20D900A6C548 /* TRTCRtcImpl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TRTCRtcImpl.h; sourceTree = "<group>"; };
|
||||
18F40439275E20D900A6C548 /* TRTCRtcImpl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TRTCRtcImpl.m; sourceTree = "<group>"; };
|
||||
7DB00EC07F1D0ADFF900B38D /* Pods-xplan-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-xplan-ios.debug.xcconfig"; path = "Target Support Files/Pods-xplan-ios/Pods-xplan-ios.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9B0E1C5726E77022005D4442 /* BaseNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseNavigationController.h; sourceTree = "<group>"; };
|
||||
9B0E1C5826E77022005D4442 /* BaseNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseNavigationController.m; sourceTree = "<group>"; };
|
||||
@@ -951,6 +954,8 @@
|
||||
18486234271EB794005FC5DC /* AgoraRtcImpl.m */,
|
||||
184862CD27213FD7005FC5DC /* ZegoRtcImpl.h */,
|
||||
184862CC27213FD7005FC5DC /* ZegoRtcImpl.m */,
|
||||
18F40438275E20D900A6C548 /* TRTCRtcImpl.h */,
|
||||
18F40439275E20D900A6C548 /* TRTCRtcImpl.m */,
|
||||
E81D587B271FBC3B003063FE /* RtcInterface.h */,
|
||||
9BB865B4272076140029CDE0 /* RtcImplDelegate.h */,
|
||||
);
|
||||
@@ -2863,6 +2868,7 @@
|
||||
E878893F273A54F500BF1D57 /* XPGiftPresenter.m in Sources */,
|
||||
E8AEAEED27141AE20017FCE0 /* XPRoomBackContainerView.m in Sources */,
|
||||
E88B5CC126FB407B00DA9178 /* XPMineUserInfoViewController.m in Sources */,
|
||||
18F4043A275E20D900A6C548 /* TRTCRtcImpl.m in Sources */,
|
||||
E824545926F5E65900BE8163 /* XPMineVerifIdentityView.m in Sources */,
|
||||
189DD74026E21C3F00AB55B1 /* YYUtility+App.m in Sources */,
|
||||
189DD74526E21CCC00AB55B1 /* YYReachability.m in Sources */,
|
||||
|
@@ -21,6 +21,7 @@ typedef NS_ENUM(NSUInteger, KeyType) {
|
||||
KeyType_WechatAppid,///微信的 appid
|
||||
KeyType_WechatSecret,///微信的 secret
|
||||
KeyType_Agora,///声网 key
|
||||
KeyType_TRTC,///TRTC key
|
||||
KeyType_NetEase,///云信的key
|
||||
KeyType_APNSCer,///推送证书的名字
|
||||
keyType_YiDunBussinessId,///易盾的id
|
||||
|
@@ -30,13 +30,14 @@ NSString * const KeyWithType(KeyType type) {
|
||||
@(KeyType_WechatAppid) : @"wx3f0462eb7eccd64f",
|
||||
@(KeyType_WechatSecret) : @"1c07949e3f53433f1c6038bfcdd54c40",
|
||||
@(KeyType_Agora) : @"7ae1a8dabe7a44a9a67c829faa409e70",
|
||||
@(KeyType_TRTC) : @"1400600174",
|
||||
@(KeyType_NetEase) : @"14ef7a0d0a84cb49bae1c22d78cf1ddf",
|
||||
@(KeyType_APNSCer) : @"yinyouApnsRelease",
|
||||
@(keyType_YiDunBussinessId) : @"2eda1894214da27d5ab7aec146fed2a2",
|
||||
@(keyType_UMengAppKey) : @"5ff6bc6dadb42d5826a1cbc4",
|
||||
@(keyType_UMengAppChannel) : @"App Store",
|
||||
},
|
||||
///测试环境
|
||||
///测试环境
|
||||
@(NO):@{
|
||||
@(KeyType_PasswordEncode) : @"1ea53d260ecf11e7b56e00163e046a26",
|
||||
@(KeyType_NTESQuickLoginBusinessId) : @"09c1214706c34f4798d3f05d86148608",
|
||||
@@ -45,6 +46,7 @@ NSString * const KeyWithType(KeyType type) {
|
||||
@(KeyType_WechatAppid) : @"wx3f0462eb7eccd64f",
|
||||
@(KeyType_WechatSecret) : @"1c07949e3f53433f1c6038bfcdd54c40",
|
||||
@(KeyType_Agora) : @"7ae1a8dabe7a44a9a67c829faa409e70",
|
||||
@(KeyType_TRTC) : @"1400600174",
|
||||
@(KeyType_NetEase) : @"82a8d602aacbbb27a1c0fc809052286e",
|
||||
@(KeyType_APNSCer) : @"yinyouApnsDebug",
|
||||
@(keyType_YiDunBussinessId) : @"7ed4c5f72673dfc2d813a27bd5854874",
|
||||
|
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// Created by zu on 2021/11/25.
|
||||
//
|
||||
// 请注意,这是一次冒险。
|
||||
// 请注意,这是一次冒险。😱
|
||||
//
|
||||
|
||||
#import "BaseViewController.h"
|
||||
|
@@ -10,6 +10,7 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol RtcInterface <NSObject>
|
||||
@required
|
||||
/**
|
||||
加入频道(房间)
|
||||
*/
|
||||
@@ -34,6 +35,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
销毁引擎
|
||||
*/
|
||||
- (void)destory;
|
||||
|
||||
@optional
|
||||
/**
|
||||
加入频道(房间),TRTC 进房需要动态签名。
|
||||
*/
|
||||
- (BOOL)joinChannel:(NSString *)channelId sign:(NSString *)sign completion:(void(^ __nullable)(void))completion;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
16
xplan-ios/Main/RTC/RtcImpl/TRTCRtcImpl.h
Normal file
16
xplan-ios/Main/RTC/RtcImpl/TRTCRtcImpl.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// TRTCRtcImpl.h
|
||||
// xplan-ios
|
||||
//
|
||||
// Created by zu on 2021/12/6.
|
||||
//
|
||||
|
||||
#import "BaseRtcImpl.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TRTCRtcImpl : BaseRtcImpl
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
104
xplan-ios/Main/RTC/RtcImpl/TRTCRtcImpl.m
Normal file
104
xplan-ios/Main/RTC/RtcImpl/TRTCRtcImpl.m
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// TRTCRtcImpl.m
|
||||
// xplan-ios
|
||||
//
|
||||
// Created by zu on 2021/12/6.
|
||||
//
|
||||
|
||||
#import "TRTCRtcImpl.h"
|
||||
#import "XPConstant.h"
|
||||
#import <TXLiteAVSDK_TRTC/TRTCCloud.h>
|
||||
|
||||
@interface TRTCRtcImpl()<TRTCCloudDelegate>
|
||||
|
||||
@property (strong, nonatomic) TRTCCloud *engine;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TRTCRtcImpl
|
||||
|
||||
- (instancetype)initWithDelegate:(id<RtcImplDelegate>)delegate {
|
||||
self = [super initWithDelegate:delegate];
|
||||
if (self) {
|
||||
_engine = [TRTCCloud sharedInstance];
|
||||
[_engine enableAudioVolumeEvaluation:900];
|
||||
_engine.delegate = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - RtcInterface impl
|
||||
- (BOOL)joinChannel:(NSString *)channelId sign:(nonnull NSString *)sign completion:(void (^)(void))completion {
|
||||
TRTCParams *params = [[TRTCParams alloc] init];
|
||||
UInt32 appId;
|
||||
sscanf([KeyWithType(KeyType_TRTC) UTF8String], "%u", &appId);
|
||||
params.sdkAppId = appId;
|
||||
UInt32 roomId;
|
||||
sscanf([channelId UTF8String], "%u", &roomId);
|
||||
params.roomId = roomId;
|
||||
params.userId = [[AccountInfoStorage instance] getUid];
|
||||
params.userSig = sign;
|
||||
params.role = TRTCRoleAudience;
|
||||
[self.engine enterRoom:params appScene:TRTCAppSceneLIVE];
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)muteRemote:(BOOL)mute {
|
||||
[self.engine muteAllRemoteAudio:mute];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)broadcast:(BOOL)on {
|
||||
[self.engine switchRole:on ? TRTCRoleAnchor : TRTCRoleAudience];
|
||||
if (on) {
|
||||
[self.engine startLocalAudio:TRTCAudioQualityDefault];
|
||||
} else {
|
||||
[self.engine stopLocalAudio];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)muteLocal:(BOOL)mute {
|
||||
[self.engine muteLocalAudio:mute];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)exitChannel:(void (^)(void))completion {
|
||||
/**
|
||||
* 1.2 离开房间
|
||||
*
|
||||
* 调用 exitRoom() 接口会执行退出房间的相关逻辑,例如释放音视频设备资源和编解码器资源等。
|
||||
* 待资源释放完毕,SDK 会通过 TRTCCloudDelegate 中的 onExitRoom() 回调通知到您。
|
||||
*
|
||||
* 如果您要再次调用 enterRoom() 或者切换到其他的音视频 SDK,请等待 onExitRoom() 回调到来之后再执行相关操作。
|
||||
* 否则可能会遇到摄像头或麦克风(例如 iOS 里的 AudioSession)被占用等各种异常问题。
|
||||
*/
|
||||
[self.engine exitRoom];
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destory {
|
||||
[TRTCCloud destroySharedIntance];
|
||||
}
|
||||
|
||||
#pragma mark - TRTCCloudDelegate
|
||||
- (void)onUserVoiceVolume:(NSArray<TRTCVolumeInfo *> *)userVolumes totalVolume:(NSInteger)totalVolume {
|
||||
NSMutableArray *uids = [NSMutableArray array];
|
||||
for (TRTCVolumeInfo *userInfo in userVolumes) {
|
||||
NSString *uid = userInfo.userId;
|
||||
if (userInfo.volume > 15){
|
||||
if (uid.integerValue == 0) {
|
||||
[uids addObject:[[AccountInfoStorage instance] getUid]];
|
||||
}else {
|
||||
[uids addObject:uid];
|
||||
}
|
||||
}
|
||||
}
|
||||
[self.delegate usersSpeaking:uids];
|
||||
}
|
||||
|
||||
@end
|
@@ -97,7 +97,7 @@
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Zego
|
||||
#pragma mark - ZegoAudioRoomDelegate
|
||||
- (void)onStreamUpdated:(ZegoAudioStreamType)type stream:(ZegoAudioStream *)stream {
|
||||
switch (type) {
|
||||
case ZEGO_AUDIO_STREAM_ADD:
|
||||
|
@@ -11,9 +11,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
RtcEngineType_Agora = 1001, // 声网
|
||||
RtcEngineType_Zego, // 即构
|
||||
RtcEngineType_Zego, // 即构
|
||||
RtcEngineType_WJ, // 无界
|
||||
RtcEngineType_AgoraFast, // 声网急速
|
||||
RtcEngineType_TRTC, // 腾讯TRTC
|
||||
} RtcEngineType;
|
||||
|
||||
/** 音频服务管理单例,对所有音频服务 SDK 的封装。
|
||||
@@ -74,6 +75,11 @@ typedef enum : NSUInteger {
|
||||
*/
|
||||
- (BOOL)enterRoom:(NSString *)roomUid;
|
||||
|
||||
/**
|
||||
* 加入频道(房间),TRTC 进房需要动态签名。
|
||||
*/
|
||||
- (BOOL)enterRoom:(NSString *)roomUid trtcSign:(NSString *)sign;
|
||||
|
||||
/**
|
||||
* 上下麦(说话)
|
||||
*/
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#import "RtcImplDelegate.h"
|
||||
#import "AgoraRtcImpl.h"
|
||||
#import "ZegoRtcImpl.h"
|
||||
#import "TRTCRtcImpl.h"
|
||||
|
||||
@interface RtcManager()<RtcImplDelegate>
|
||||
|
||||
@@ -38,7 +39,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)initEngineWithType:(RtcEngineType)type delegate:(id<RtcDelegate> _Nullable)delegate{
|
||||
+ (instancetype)initEngineWithType:(RtcEngineType)type delegate:(id<RtcDelegate> _Nullable)delegate {
|
||||
RtcManager* rtcManager = [self instance];
|
||||
[rtcManager setEngineType:type];
|
||||
[rtcManager setEngineDelegate:delegate];
|
||||
@@ -52,6 +53,13 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)enterRoom:(NSString *)roomUid trtcSign:(nonnull NSString *)sign {
|
||||
return [self.engine joinChannel:roomUid sign:sign completion:^{
|
||||
[self muteRemote:NO];
|
||||
[self muteLocal:NO];
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)muteRemote:(BOOL)mute {
|
||||
return [self.engine muteRemote:mute];
|
||||
}
|
||||
@@ -119,6 +127,11 @@
|
||||
_engine = [[ZegoRtcImpl alloc] initWithDelegate:self];
|
||||
}
|
||||
break;
|
||||
case RtcEngineType_TRTC:
|
||||
{
|
||||
_engine = [[TRTCRtcImpl alloc] initWithDelegate:self];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
_engine = [[AgoraRtcImpl alloc] initWithDelegate:self];
|
||||
|
@@ -37,6 +37,7 @@ typedef NS_ENUM(NSInteger, RoomModeType){
|
||||
@property (nonatomic , assign) NSInteger calcSumDataIndex;
|
||||
@property (nonatomic , copy) NSString * roomTag;
|
||||
@property (nonatomic , copy) NSString * audioSdkType;
|
||||
@property (nonatomic , copy) NSString * trtcSig;
|
||||
@property (nonatomic , assign) NSInteger hideFlag;
|
||||
@property (nonatomic , assign) NSInteger blindDateState;
|
||||
@property (nonatomic , assign) RoomType type;
|
||||
@@ -72,12 +73,9 @@ typedef NS_ENUM(NSInteger, RoomModeType){
|
||||
@property (nonatomic , assign) NSInteger closeScreenFlag;
|
||||
@property (nonatomic , assign) NSInteger operatorStatus;
|
||||
@property (nonatomic , assign) BOOL isRoomFans;
|
||||
///房间密码
|
||||
@property(nonatomic, copy) NSString *roomPwd;
|
||||
///是否打开离开模式
|
||||
@property (nonatomic, assign) BOOL leaveMode;
|
||||
///房间的模式
|
||||
@property (nonatomic, assign) RoomModeType roomModeType;//房间模式
|
||||
@property (nonatomic , copy) NSString * roomPwd;
|
||||
@property (nonatomic , assign) BOOL leaveMode;
|
||||
@property (nonatomic , assign) RoomModeType roomModeType;//房间模式
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -83,9 +83,32 @@
|
||||
RoomInfoModel* roomInfo = self.hostDelegate.getRoomInfo;
|
||||
self.leaveMode = roomInfo.leaveMode;
|
||||
// 1. 加入语音房间。
|
||||
RtcEngineType type = [roomInfo.audioSdkType isEqualToString:@"wujie"] ? RtcEngineType_WJ : ([roomInfo.audioSdkType isEqualToString:@"zego"] ? RtcEngineType_Zego : RtcEngineType_Agora);
|
||||
RtcEngineType type;
|
||||
if ([roomInfo.audioSdkType isEqualToString:@"wujie"]) {
|
||||
type = RtcEngineType_WJ;
|
||||
} else if ([roomInfo.audioSdkType isEqualToString:@"zego"]) {
|
||||
type = RtcEngineType_Zego;
|
||||
} else if ([roomInfo.audioSdkType isEqualToString:@"trtc"]) {
|
||||
type = RtcEngineType_TRTC;
|
||||
} else {
|
||||
type = RtcEngineType_Agora;
|
||||
}
|
||||
[RtcManager initEngineWithType:type delegate:self];
|
||||
[[RtcManager instance] enterRoom:[NSString stringWithFormat:@"%ld", (long)roomInfo.roomId]];
|
||||
switch (type) {
|
||||
/**
|
||||
* 🐴 Agora
|
||||
* 🐴 Zego
|
||||
* 🦄️ TRTC
|
||||
* TRTC,就你 🦄️ 不一样?
|
||||
*/
|
||||
case RtcEngineType_TRTC:
|
||||
[[RtcManager instance] enterRoom:[NSString stringWithFormat:@"%ld", (long)roomInfo.roomId] trtcSign:roomInfo.trtcSig];
|
||||
break;
|
||||
|
||||
default:
|
||||
[[RtcManager instance] enterRoom:[NSString stringWithFormat:@"%ld", (long)roomInfo.roomId]];
|
||||
break;
|
||||
}
|
||||
|
||||
// 2. 获取麦位的状态,并初始化 self.micQueue 。
|
||||
[[NIMSDK sharedSDK].chatManager addDelegate:self];
|
||||
|
Reference in New Issue
Block a user