UNPKG

ngx-tencent-im

Version:
1 lines 317 kB
{"version":3,"file":"ngx-tencent-im.mjs","sources":["../../../projects/ngx-tencent-im/src/store/actions/login.action.ts","../../../projects/ngx-tencent-im/src/store/actions/user.action.ts","../../../projects/ngx-tencent-im/src/store/actions/message.action.ts","../../../projects/ngx-tencent-im/src/store/actions/conversation.action.ts","../../../projects/ngx-tencent-im/src/store/selectors/conversation.selector.ts","../../../projects/ngx-tencent-im/src/store/selectors/login.selector.ts","../../../projects/ngx-tencent-im/src/store/selectors/message.selector.ts","../../../projects/ngx-tencent-im/src/store/selectors/user.selector.ts","../../../projects/ngx-tencent-im/src/store/actions/group.action.ts","../../../projects/ngx-tencent-im/src/store/selectors/group.selector.ts","../../../projects/ngx-tencent-im/src/shared.data.ts","../../../projects/ngx-tencent-im/src/tim-helper.service.ts","../../../projects/ngx-tencent-im/src/util/title-notice.ts","../../../projects/ngx-tencent-im/src/my-profile/edit-profile/edit-profile.component.ts","../../../projects/ngx-tencent-im/src/my-profile/edit-profile/edit-profile.component.html","../../../projects/ngx-tencent-im/src/avatar/avatar.component.ts","../../../projects/ngx-tencent-im/src/avatar/avatar.component.html","../../../projects/ngx-tencent-im/src/my-profile/profile-card/profile-card.component.ts","../../../projects/ngx-tencent-im/src/my-profile/profile-card/profile-card.component.html","../../../projects/ngx-tencent-im/src/my-profile/my-profile.component.ts","../../../projects/ngx-tencent-im/src/my-profile/my-profile.component.html","../../../projects/ngx-tencent-im/src/util/date.ts","../../../projects/ngx-tencent-im/src/conversation/conversation-item/conversation-item.component.ts","../../../projects/ngx-tencent-im/src/conversation/conversation-item/conversation-item.component.html","../../../projects/ngx-tencent-im/src/conversation/conversation-list/conversation-list.component.ts","../../../projects/ngx-tencent-im/src/conversation/conversation-list/conversation-list.component.html","../../../projects/ngx-tencent-im/src/group/create-group/create-group.component.ts","../../../projects/ngx-tencent-im/src/group/create-group/create-group.component.html","../../../projects/ngx-tencent-im/src/group/group-item/group-item.component.ts","../../../projects/ngx-tencent-im/src/group/group-item/group-item.component.html","../../../projects/ngx-tencent-im/src/group/group-list/group-list.component.ts","../../../projects/ngx-tencent-im/src/group/group-list/group-list.component.html","../../../projects/ngx-tencent-im/src/side-bar/side-bar.component.ts","../../../projects/ngx-tencent-im/src/side-bar/side-bar.component.html","../../../projects/ngx-tencent-im/src/title/title.component.ts","../../../projects/ngx-tencent-im/src/title/title.component.html","../../../projects/ngx-tencent-im/src/message/message-status-icon/message-status-icon.component.ts","../../../projects/ngx-tencent-im/src/message/message-status-icon/message-status-icon.component.html","../../../projects/ngx-tencent-im/src/util/emoji-map.ts","../../../projects/ngx-tencent-im/src/util/decode-text.ts","../../../projects/ngx-tencent-im/src/re-edit-message.service.ts","../../../projects/ngx-tencent-im/src/message/message-bubble/message-bubble.component.ts","../../../projects/ngx-tencent-im/src/message/message-bubble/message-bubble.component.html","../../../projects/ngx-tencent-im/src/message/message-element/text-element/text-element.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/text-element/text-element.component.html","../../../projects/ngx-tencent-im/src/message/message-element/image-element/image-element.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/image-element/image-element.component.html","../../../projects/ngx-tencent-im/src/message/message-element/file-element/file-element.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/file-element/file-element.component.html","../../../projects/ngx-tencent-im/src/message/message-footer/message-footer.component.ts","../../../projects/ngx-tencent-im/src/message/message-footer/message-footer.component.html","../../../projects/ngx-tencent-im/src/message/message-header/message-header.component.ts","../../../projects/ngx-tencent-im/src/message/message-header/message-header.component.html","../../../projects/ngx-tencent-im/src/message/message-element/group-tip-element/group-tip-element.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/group-tip-element/group-tip-element.component.html","../../../projects/ngx-tencent-im/src/util/format-duration.ts","../../../projects/ngx-tencent-im/src/util/trtc-custom-message-map.ts","../../../projects/ngx-tencent-im/src/message/message-element/custom-element/custom-element.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/custom-element/custom-element.component.html","../../../projects/ngx-tencent-im/src/util/common.ts","../../../projects/ngx-tencent-im/src/message/message-element/group-system-notice-element/approval-join-group/approval-join-group.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/group-system-notice-element/approval-join-group/approval-join-group.component.html","../../../projects/ngx-tencent-im/src/message/message-element/group-system-notice-element/group-system-notice-element.component.ts","../../../projects/ngx-tencent-im/src/message/message-element/group-system-notice-element/group-system-notice-element.component.html","../../../projects/ngx-tencent-im/src/message/message-item/message-item.component.ts","../../../projects/ngx-tencent-im/src/message/message-item/message-item.component.html","../../../projects/ngx-tencent-im/src/message/message-send-box/message-send-box.component.ts","../../../projects/ngx-tencent-im/src/message/message-send-box/message-send-box.component.html","../../../projects/ngx-tencent-im/src/conversation/current-conversation/current-conversation.component.ts","../../../projects/ngx-tencent-im/src/conversation/current-conversation/current-conversation.component.html","../../../projects/ngx-tencent-im/src/conversation/conversation-profile/conversation-profile/user-profile/user-profile.component.ts","../../../projects/ngx-tencent-im/src/conversation/conversation-profile/conversation-profile/user-profile/user-profile.component.html","../../../projects/ngx-tencent-im/src/group/group-member-info/group-member-info.component.ts","../../../projects/ngx-tencent-im/src/group/group-member-info/group-member-info.component.html","../../../projects/ngx-tencent-im/src/group/group-member-list/group-member-list.component.ts","../../../projects/ngx-tencent-im/src/group/group-member-list/group-member-list.component.html","../../../projects/ngx-tencent-im/src/conversation/conversation-profile/conversation-profile/group-profile/group-profile.component.ts","../../../projects/ngx-tencent-im/src/conversation/conversation-profile/conversation-profile/group-profile/group-profile.component.html","../../../projects/ngx-tencent-im/src/conversation/conversation-profile/conversation-profile.component.ts","../../../projects/ngx-tencent-im/src/conversation/conversation-profile/conversation-profile.component.html","../../../projects/ngx-tencent-im/src/tencent-tim/tencent-tim.component.ts","../../../projects/ngx-tencent-im/src/tencent-tim/tencent-tim.component.html","../../../projects/ngx-tencent-im/src/store/reducer/conversation.reducer.ts","../../../projects/ngx-tencent-im/src/store/reducer/group.reducer.ts","../../../projects/ngx-tencent-im/src/store/reducer/login.reducer.ts","../../../projects/ngx-tencent-im/src/store/reducer/message.reducer.ts","../../../projects/ngx-tencent-im/src/store/reducer/user.reducer.ts","../../../projects/ngx-tencent-im/src/store/reducer/index.ts","../../../projects/ngx-tencent-im/src/tencent-tim.module.ts","../../../projects/ngx-tencent-im/src/public-api.ts","../../../projects/ngx-tencent-im/src/ngx-tencent-im.ts"],"sourcesContent":["import { createAction, props } from '@ngrx/store';\r\n\r\nexport enum LoginActionTypes {\r\n Login = '[toggleIsLogin]',\r\n}\r\n\r\nexport const loginAction = createAction(\r\n LoginActionTypes.Login,\r\n props<{ isLogin: boolean; }>()\r\n);\r\n\r\n","import { createAction, props } from '@ngrx/store';\r\nimport { Profile } from 'tim-js-sdk';\r\n\r\nexport enum UserActionTypes {\r\n SDKReady = '[user] toggleIsSDKReady',\r\n updateCurrentUserProfile = '[user] updateCurrentUserProfile',\r\n resetUser = '[user] resetUser',\r\n}\r\n\r\nexport const SDKReadyAction = createAction(\r\n UserActionTypes.SDKReady,\r\n props<{ SDKReadyState: boolean; }>()\r\n);\r\n\r\nexport const updateCurrentUserProfileAction = createAction(\r\n UserActionTypes.updateCurrentUserProfile,\r\n props<{ profile: Profile; }>()\r\n);\r\n\r\nexport const resetUserAction = createAction(\r\n UserActionTypes.resetUser\r\n);\r\n","import { createAction, props } from '@ngrx/store';\r\nimport { Message } from 'tim-js-sdk';\r\nimport { MESSAGE_STATUS } from '../../shared.data';\r\n\r\nexport const showAction = createAction(\r\n '[showMessage]',\r\n props<{ msgType: MESSAGE_STATUS; message: any; }>()\r\n);\r\nexport const updateMessageAction = createAction(\r\n '[updateMessage]',\r\n props<{ nextReqMessageID: string; isCompleted: boolean; currentMessageList: Array<Message>; }>()\r\n);\r\n","import { createAction, props } from '@ngrx/store';\r\nimport { Conversation, Message} from 'tim-js-sdk';;\r\n\r\nexport const updateCurrentConversationAction = createAction(\r\n '[conversation] updateCurrentConversation',\r\n props<{ conversation: Conversation; }>()\r\n);\r\nexport const updateConversationListAction = createAction(\r\n '[conversation] updateConversationList',\r\n props<{ conversationList: Array<Conversation>; }>()\r\n);\r\nexport const resetCurrentConversationAction = createAction(\r\n '[conversation] resetCurrentConversation'\r\n);\r\nexport const pushCurrentMessageListAction = createAction(\r\n '[conversation] pushCurrentMessageList',\r\n props<{ message: Message; }>()\r\n);\r\nexport const removeMessageAction = createAction(\r\n '[conversation] removeMessage',\r\n props<{ message: any; }>()\r\n);\r\nexport const resetConversationAction = createAction(\r\n '[conversation] reset'\r\n);\r\nexport const checkoutConversationAction = createAction(\r\n '[conversation] checkoutConversation',\r\n props<{ conversationID: string; }>()\r\n);\r\n","import { createSelector } from '@ngrx/store';\r\nimport { Conversation } from 'tim-js-sdk';\r\nimport { ReducerState } from '../reducer';\r\nimport { ConversationState } from '../reducer/conversation.reducer';\r\n\r\nconst selectPlayerStates = (state: ReducerState) => state.currentConversationID;\r\nexport const getCurrentConversationID = createSelector(selectPlayerStates, (state: any) => state.conversationID);\r\n\r\nconst selectConversation = (state: ReducerState) => state.conversation;\r\nexport const conversationSelector = createSelector(selectConversation, (state: ConversationState) => state);\r\n\r\n\r\nconst selectCurrentConversation = (state: ReducerState) => state.conversation.currentConversation;\r\nexport const currentConversationSelector = createSelector(selectCurrentConversation, (state: Conversation) => state);\r\n\r\n\r\nconst selectConversationList = (state: ReducerState) => state.conversation.conversationList;\r\nexport const conversationListSelector =\r\n createSelector(selectConversationList, (state: Array<Conversation>) => state);\r\n","import { createSelector } from '@ngrx/store';\r\nimport { ReducerState } from '../reducer';\r\nimport { LoginState } from '../reducer/login.reducer';\r\n\r\nconst selectPlayerStates = (state: ReducerState) => state.login;\r\n\r\nexport const getLogin = createSelector(selectPlayerStates, (state: LoginState) => state.isLogin);","import { createSelector } from '@ngrx/store';\r\n\r\nconst selectPlayerStates = (state: { message; }) => state.message;\r\n\r\nexport const getMessage = createSelector(selectPlayerStates, (state: any) => state);\r\n","import { createSelector } from '@ngrx/store';\r\nimport { ReducerState } from '../reducer';\r\nimport { UserState } from '../reducer/user.reducer';\r\n\r\nconst selectPlayerStates = (state: ReducerState) => state.user;\r\n\r\nexport const getSDkReady = createSelector(selectPlayerStates, (state: UserState) => state.isSDKReady);\r\nexport const currentUserProfileSelector = createSelector(selectPlayerStates, (state: UserState) => state.currentUserProfile);","import { createAction, props } from '@ngrx/store';\r\nimport { Group, GroupMember } from 'tim-js-sdk';\r\n\r\nexport const updateGroupListAction = createAction(\r\n '[group] updateGroupList',\r\n props<{ groupList: Array<Group>; }>()\r\n);\r\n\r\nexport const updateCurrentMemberListAction = createAction(\r\n '[group] updateCurrentMemberList',\r\n props<{ currentMemberList: Array<GroupMember>; }>()\r\n);\r\nexport const resetCurrentMemberListAction = createAction(\r\n '[group] resetCurrentMemberList'\r\n);\r\n","import { createSelector } from '@ngrx/store';\r\nimport { Group, GroupMember } from 'tim-js-sdk';\r\nimport { ReducerState } from '../reducer';\r\n\r\nconst selectGroupListStates = (state: ReducerState) => state.group.groupList;\r\nexport const groupListSelector = createSelector(selectGroupListStates, (state: Array<Group>) => state);\r\nconst currentMemberListStates = (state: ReducerState) => state.group.currentMemberList;\r\nexport const currentMemberListSelector = createSelector(currentMemberListStates, (state: Array<GroupMember>) => state);\r\n","\r\nimport { InjectionToken } from '@angular/core';\r\nimport { NgTimConfig } from './type';\r\n\r\nexport const NG_Tim_CONFIG = new InjectionToken<NgTimConfig>('config');\r\n\r\nexport enum MESSAGE_STATUS {\r\n success = 'success',\r\n info = 'info',\r\n warning = 'warning',\r\n error = 'error'\r\n}\r\n\r\nexport enum CONVERSATION_TYPE {\r\n client = 'C2C',\r\n group = 'GROUP',\r\n system = '@TIM#SYSTEM',\r\n}\r\nexport enum TIM_TYPES {\r\n GRP_WORK = 'Private',\r\n GRP_PUBLIC = 'Public',\r\n GRP_MEETING = 'ChatRoom',\r\n GRP_AVCHATROOM = 'AVChatRoom',\r\n}\r\n\r\nexport const TIM = {\r\n TYPES: {\r\n \"MSG_TEXT\": \"TIMTextElem\",\r\n \"MSG_IMAGE\": \"TIMImageElem\",\r\n \"MSG_SOUND\": \"TIMSoundElem\",\r\n \"MSG_AUDIO\": \"TIMSoundElem\",\r\n \"MSG_FILE\": \"TIMFileElem\",\r\n \"MSG_FACE\": \"TIMFaceElem\",\r\n \"MSG_VIDEO\": \"TIMVideoFileElem\",\r\n \"MSG_GEO\": \"TIMLocationElem\",\r\n \"MSG_GRP_TIP\": \"TIMGroupTipElem\",\r\n \"MSG_GRP_SYS_NOTICE\": \"TIMGroupSystemNoticeElem\",\r\n \"MSG_CUSTOM\": \"TIMCustomElem\",\r\n \"MSG_MERGER\": \"TIMRelayElem\",\r\n \"MSG_PRIORITY_HIGH\": \"High\",\r\n \"MSG_PRIORITY_NORMAL\": \"Normal\",\r\n \"MSG_PRIORITY_LOW\": \"Low\",\r\n \"MSG_PRIORITY_LOWEST\": \"Lowest\",\r\n \"CONV_C2C\": \"C2C\",\r\n \"CONV_GROUP\": \"GROUP\",\r\n \"CONV_SYSTEM\": \"@TIM%23SYSTEM\",\r\n \"CONV_AT_ME\": 1,\r\n \"CONV_AT_ALL\": 2,\r\n \"CONV_AT_ALL_AT_ME\": 3,\r\n \"GRP_PRIVATE\": \"Private\",\r\n \"GRP_WORK\": \"Private\",\r\n \"GRP_PUBLIC\": \"Public\",\r\n \"GRP_CHATROOM\": \"ChatRoom\",\r\n \"GRP_MEETING\": \"ChatRoom\",\r\n \"GRP_AVCHATROOM\": \"AVChatRoom\",\r\n \"GRP_MBR_ROLE_OWNER\": \"Owner\",\r\n \"GRP_MBR_ROLE_ADMIN\": \"Admin\",\r\n \"GRP_MBR_ROLE_MEMBER\": \"Member\",\r\n \"GRP_TIP_MBR_JOIN\": 1,\r\n \"GRP_TIP_MBR_QUIT\": 2,\r\n \"GRP_TIP_MBR_KICKED_OUT\": 3,\r\n \"GRP_TIP_MBR_SET_ADMIN\": 4,\r\n \"GRP_TIP_MBR_CANCELED_ADMIN\": 5,\r\n \"GRP_TIP_GRP_PROFILE_UPDATED\": 6,\r\n \"GRP_TIP_MBR_PROFILE_UPDATED\": 7,\r\n \"MSG_REMIND_ACPT_AND_NOTE\": \"AcceptAndNotify\",\r\n \"MSG_REMIND_ACPT_NOT_NOTE\": \"AcceptNotNotify\",\r\n \"MSG_REMIND_DISCARD\": \"Discard\",\r\n \"GENDER_UNKNOWN\": \"Gender_Type_Unknown\",\r\n \"GENDER_FEMALE\": \"Gender_Type_Female\",\r\n \"GENDER_MALE\": \"Gender_Type_Male\",\r\n \"KICKED_OUT_MULT_ACCOUNT\": \"multipleAccount\",\r\n \"KICKED_OUT_MULT_DEVICE\": \"multipleDevice\",\r\n \"KICKED_OUT_USERSIG_EXPIRED\": \"userSigExpired\",\r\n \"ALLOW_TYPE_ALLOW_ANY\": \"AllowType_Type_AllowAny\",\r\n \"ALLOW_TYPE_NEED_CONFIRM\": \"AllowType_Type_NeedConfirm\",\r\n \"ALLOW_TYPE_DENY_ANY\": \"AllowType_Type_DenyAny\",\r\n \"FORBID_TYPE_NONE\": \"AdminForbid_Type_None\",\r\n \"FORBID_TYPE_SEND_OUT\": \"AdminForbid_Type_SendOut\",\r\n \"JOIN_OPTIONS_FREE_ACCESS\": \"FreeAccess\",\r\n \"JOIN_OPTIONS_NEED_PERMISSION\": \"NeedPermission\",\r\n \"JOIN_OPTIONS_DISABLE_APPLY\": \"DisableApply\",\r\n \"JOIN_STATUS_SUCCESS\": \"JoinedSuccess\",\r\n \"JOIN_STATUS_ALREADY_IN_GROUP\": \"AlreadyInGroup\",\r\n \"JOIN_STATUS_WAIT_APPROVAL\": \"WaitAdminApproval\",\r\n \"GRP_PROFILE_OWNER_ID\": \"ownerID\",\r\n \"GRP_PROFILE_CREATE_TIME\": \"createTime\",\r\n \"GRP_PROFILE_LAST_INFO_TIME\": \"lastInfoTime\",\r\n \"GRP_PROFILE_MEMBER_NUM\": \"memberNum\",\r\n \"GRP_PROFILE_MAX_MEMBER_NUM\": \"maxMemberNum\",\r\n \"GRP_PROFILE_JOIN_OPTION\": \"joinOption\",\r\n \"GRP_PROFILE_INTRODUCTION\": \"introduction\",\r\n \"GRP_PROFILE_NOTIFICATION\": \"notification\",\r\n \"GRP_PROFILE_MUTE_ALL_MBRS\": \"muteAllMembers\",\r\n \"NET_STATE_CONNECTED\": \"connected\",\r\n \"NET_STATE_CONNECTING\": \"connecting\",\r\n \"NET_STATE_DISCONNECTED\": \"disconnected\",\r\n \"MSG_AT_ALL\": \"__kImSDK_MesssageAtALL__\"\r\n },\r\n EVENT: {\r\n \"SDK_READY\": \"sdkStateReady\",\r\n \"SDK_NOT_READY\": \"sdkStateNotReady\",\r\n \"SDK_DESTROY\": \"sdkDestroy\",\r\n \"MESSAGE_RECEIVED\": \"onMessageReceived\",\r\n \"MESSAGE_REVOKED\": \"onMessageRevoked\",\r\n \"MESSAGE_READ_BY_PEER\": \"onMessageReadByPeer\",\r\n \"CONVERSATION_LIST_UPDATED\": \"onConversationListUpdated\",\r\n \"GROUP_LIST_UPDATED\": \"onGroupListUpdated\",\r\n \"GROUP_SYSTEM_NOTICE_RECEIVED\": \"receiveGroupSystemNotice\",\r\n \"PROFILE_UPDATED\": \"onProfileUpdated\",\r\n \"BLACKLIST_UPDATED\": \"blacklistUpdated\",\r\n \"KICKED_OUT\": \"kickedOut\",\r\n \"ERROR\": \"error\",\r\n \"NET_STATE_CHANGE\": \"netStateChange\",\r\n \"SDK_RELOAD\": \"sdkReload\"\r\n },\r\n VERSION: \"2.10.2\"\r\n};\r\n","import { EventEmitter, Inject, Injectable } from '@angular/core';\r\n\r\nimport { Subject } from 'rxjs';\r\nimport { Store } from '@ngrx/store';\r\n\r\nimport {\r\n loginAction,\r\n pushCurrentMessageListAction,\r\n resetConversationAction,\r\n resetUserAction,\r\n SDKReadyAction,\r\n showAction,\r\n updateConversationListAction,\r\n updateCurrentConversationAction,\r\n updateCurrentUserProfileAction,\r\n updateMessageAction\r\n} from './store/actions';\r\n\r\nimport {\r\n conversationSelector,\r\n} from './store/selectors';\r\n\r\nimport Tim, { Conversation, Group, GroupMember, IMResponse, Message } from 'tim-js-sdk';\r\n\r\nimport { ConversationState } from './store/reducer/conversation.reducer';\r\nimport { resetCurrentMemberListAction, updateCurrentMemberListAction, updateGroupListAction } from './store/actions/group.action';\r\nimport { currentMemberListSelector } from './store/selectors/group.selector';\r\n\r\nimport TIM from 'tim-js-sdk';\r\n\r\nimport TIMUploadPlugin from 'tim-upload-plugin';\r\n\r\nimport { MESSAGE_STATUS, NG_Tim_CONFIG } from './shared.data';\r\nimport { NgTimConfig } from './type';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class TimHelperService {\r\n\r\n tim: Tim;\r\n conversation: ConversationState; // 当前会话\r\n currentMemberList: Array<GroupMember>; // 当前会话\r\n eventBus$: Subject<string> = new Subject();\r\n totalUnRead: Subject<number> = new Subject();\r\n\r\n constructor(\r\n private store: Store,\r\n @Inject(NG_Tim_CONFIG) public config: NgTimConfig,\r\n ) {\r\n\r\n this.initTim(config);\r\n // 初始化监听器\r\n this.initListener();\r\n\r\n // 获取当前会话\r\n this.store.select(conversationSelector).subscribe(res => {\r\n this.conversation = res;\r\n });\r\n\r\n // 获取当前成员\r\n this.store.select(currentMemberListSelector).subscribe(res => {\r\n this.currentMemberList = res;\r\n });\r\n }\r\n\r\n\r\n login(userId: string, userSig: string) {\r\n if (!userSig) {\r\n throw new Error('请配置签名!');\r\n }\r\n\r\n this.tim.login({ userID: userId, userSig })\r\n .then((imResponse: IMResponse<any>) => {\r\n this.eventBus$.next('login');\r\n this.store.dispatch(loginAction({ isLogin: true }));\r\n // this.store.dispatch(startComputeCurrentAction());\r\n this.store.dispatch(showAction({ msgType: MESSAGE_STATUS.success, message: '登录成功!' }));\r\n if (imResponse.data.repeatLogin === true) {\r\n // 标识账号已登录,本次登录操作为重复登录。v2.5.1 起支持\r\n console.log(imResponse.data.errorInfo);\r\n }\r\n }).catch((imError) => {\r\n console.warn('login error:', imError); // 登录失败的相关信息\r\n });\r\n }\r\n\r\n logout() {\r\n // 若有当前会话,在退出登录时已读上报\r\n if (this.conversation.currentConversation.conversationID) {\r\n this.tim.setMessageRead({ conversationID: this.conversation.currentConversation.conversationID });\r\n }\r\n this.tim.logout().then((res) => {\r\n this.eventBus$.next('logout');\r\n // this.store.dispatch(stopComputeCurrentAction());\r\n this.store.dispatch(loginAction({ isLogin: false }));\r\n this.store.dispatch(resetUserAction());\r\n this.store.dispatch(resetConversationAction());\r\n\r\n this.store.dispatch(showAction({ msgType: MESSAGE_STATUS.success, message: '已退出!' }));\r\n });\r\n }\r\n\r\n // 初始化tim监听函数\r\n initListener() {\r\n // sdk ready\r\n this.tim.on(TIM.EVENT.SDK_READY, this.onReadyStateUpdate, this);\r\n // SDK NOT READT\r\n this.tim.on(TIM.EVENT.SDK_NOT_READY, this.onReadyStateUpdate, this);\r\n // 被踢出\r\n this.tim.on(TIM.EVENT.KICKED_OUT, this.onKickOut, this);\r\n // SDK内部出错\r\n this.tim.on(TIM.EVENT.ERROR, this.onError, this);\r\n // 收到新消息\r\n this.tim.on(TIM.EVENT.MESSAGE_RECEIVED, this.onReceiveMessage, this);\r\n // 会话列表更新\r\n this.tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, this.onUpdateConversationList, this);\r\n // 群组列表更新\r\n this.tim.on(TIM.EVENT.GROUP_LIST_UPDATED, this.onUpdateGroupList, this);\r\n // 网络监测\r\n this.tim.on(TIM.EVENT.NET_STATE_CHANGE, this.onNetStateChange, this);\r\n // 已读回执\r\n this.tim.on(TIM.EVENT.MESSAGE_READ_BY_PEER, this.onMessageReadByPeer, this);\r\n }\r\n\r\n private onReadyStateUpdate({ name }) {\r\n const isSDKReady = name === TIM.EVENT.SDK_READY ? true : false;\r\n this.store.dispatch(SDKReadyAction({ SDKReadyState: isSDKReady }));\r\n if (isSDKReady) {\r\n this.tim.getMyProfile()\r\n .then(({ data }) => {\r\n this.store.dispatch(updateCurrentUserProfileAction({ profile: data }));\r\n })\r\n .catch(error => {\r\n this.store.dispatch(showAction({\r\n msgType: MESSAGE_STATUS.warning,\r\n message: error.message\r\n }));\r\n });\r\n }\r\n }\r\n\r\n onKickOut(event: { name: string, data: { type: string; }; }) {\r\n this.eventBus$.next('logout');\r\n // this.store.dispatch(stopComputeCurrentAction());\r\n this.store.dispatch(loginAction({ isLogin: false }));\r\n this.store.dispatch(resetUserAction());\r\n this.store.dispatch(resetConversationAction());\r\n this.store.dispatch(showAction({ msgType: MESSAGE_STATUS.warning, message: '由于多实例登录被踢出,请重新登录!' }));\r\n }\r\n\r\n onError({ data }) {\r\n if (data.message !== 'Network Error') {\r\n console.log('%c error', 'color:red;font-size:20px;', data);\r\n }\r\n }\r\n onMessageReadByPeer(event: any) {\r\n console.log('已回执', event);\r\n }\r\n\r\n onReceiveMessage({ data: messageList }) {\r\n\r\n // this.handleVideoMessage(messageList);\r\n // this.handleAt(messageList);\r\n // this.handleQuitGroupTip(messageList);\r\n this.store.dispatch(pushCurrentMessageListAction({ message: messageList }));\r\n }\r\n\r\n // 会话列表更新\r\n onUpdateConversationList(event: { data: Array<Conversation>; }) {\r\n this.store.dispatch(updateConversationListAction({ conversationList: event.data }));\r\n }\r\n\r\n // 群列表更新\r\n onUpdateGroupList(event: { data: Array<Group>; }) {\r\n this.store.dispatch(updateGroupListAction({ groupList: event.data }));\r\n }\r\n\r\n onNetStateChange(event: any) {\r\n console.log('网络监测::', event);\r\n }\r\n /**\r\n * 切换会话\r\n * 调用时机:切换会话时\r\n */\r\n checkoutConversation(conversationID: string) {\r\n\r\n this.store.dispatch(resetCurrentMemberListAction());\r\n\r\n // 1.切换会话前,将切换前的会话进行已读上报\r\n if (this.conversation.currentConversation.conversationID) {\r\n const prevConversationID = this.conversation.currentConversation.conversationID;\r\n this.tim.setMessageRead({ conversationID: prevConversationID });\r\n }\r\n\r\n // 2.待切换的会话也进行已读上报\r\n this.tim.setMessageRead({ conversationID });\r\n // 3. 获取会话信息\r\n return this.tim.getConversationProfile(conversationID).then((res: IMResponse<{ conversation: Conversation; }>) => {\r\n // 3.1 更新当前会话\r\n this.store.dispatch(updateCurrentConversationAction({ conversation: res.data.conversation }));\r\n // 3.2 获取消息列表\r\n this.getMessageList(conversationID);\r\n\r\n if (res.data.conversation.type === TIM.TYPES.CONV_GROUP) {\r\n this.getGroupMemberList(res.data.conversation.groupProfile.groupID);\r\n }\r\n return Promise.resolve();\r\n\r\n });\r\n // .catch(err => {\r\n // this.store.dispatch(showAction({ msgType: MESSAGE_STATUS.error, message: err }));\r\n // });\r\n }\r\n\r\n /**\r\n * @description 获取消息\r\n */\r\n getMessageList(conversationID: string) {\r\n if (this.conversation.isCompleted) {\r\n this.store.dispatch(showAction({\r\n msgType: MESSAGE_STATUS.info,\r\n message: '已经没有更多的历史消息了哦'\r\n }));\r\n return;\r\n }\r\n const { nextReqMessageID, currentMessageList } = this.conversation;\r\n this.tim.getMessageList({ conversationID, nextReqMessageID, count: 15 })\r\n .then((imResponse: IMResponse<{ isCompleted: boolean, nextReqMessageID: string, messageList: Array<Message>; }>) => {\r\n this.store.dispatch(updateMessageAction({\r\n nextReqMessageID: imResponse.data.nextReqMessageID,\r\n isCompleted: imResponse.data.isCompleted,\r\n currentMessageList: [...imResponse.data.messageList, ...currentMessageList]\r\n }));\r\n });\r\n }\r\n /**\r\n * @description 获取群成员\r\n */\r\n getGroupMemberList(groupID: string) {\r\n this.tim.getGroupMemberList({\r\n groupID,\r\n offset: this.currentMemberList.length,\r\n count: 30\r\n }).then((imResponse: IMResponse<{ memberList: Array<GroupMember>; }>) => {\r\n this.store.dispatch(updateCurrentMemberListAction({ currentMemberList: imResponse.data.memberList }));\r\n });\r\n }\r\n\r\n\r\n private initTim(config: NgTimConfig) {\r\n // const cosImport = await import('cos-js-sdk-v5');\r\n // const timImport = await import('tim-js-sdk');\r\n\r\n this.tim = TIM.create({\r\n SDKAppID: config.sdkAppId,\r\n oversea: config.oversea,\r\n });\r\n // 无日志级别\r\n this.tim.setLogLevel(config.level || 1);\r\n // 注册 cos\r\n this.tim.registerPlugin({ 'tim-upload-plugin': TIMUploadPlugin });\r\n }\r\n\r\n}\r\n","export function titleNotify(count: number) {\r\n const hasNewMessage = count > 0;\r\n if (hasNewMessage) {\r\n if (document.title.search(/\\((.*?)\\)/) >= 0) {\r\n document.title = document.title.replace(/\\((.*?)\\)/, `(${count > 99 ? '99+' : count})`);\r\n } else {\r\n document.title = `(${count})${document.title}`;\r\n }\r\n } else {\r\n document.title = document.title.replace(/\\((.*?)\\)/, '');\r\n }\r\n}\r\n","import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';\r\nimport { FormBuilder, FormGroup, Validators } from '@angular/forms';\r\nimport { Store } from '@ngrx/store';\r\nimport { NzModalRef } from 'ng-zorro-antd/modal';\r\nimport { Profile } from 'tim-js-sdk';\r\nimport { MESSAGE_STATUS, TIM } from '../../shared.data';\r\nimport { showAction } from '../../store/actions';\r\nimport { TimHelperService } from '../../tim-helper.service';\r\n\r\n@Component({\r\n selector: 'im-edit-profile',\r\n templateUrl: './edit-profile.component.html',\r\n styleUrls: ['./edit-profile.component.less'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class EditProfileComponent implements OnInit {\r\n @Input() userProfile: Profile;\r\n\r\n form: FormGroup;\r\n\r\n radioOption = [\r\n { label: '男', value: TIM.TYPES.GENDER_MALE },\r\n { label: '女', value: TIM.TYPES.GENDER_FEMALE },\r\n { label: '不显示', value: TIM.TYPES.GENDER_UNKNOWN },\r\n ];\r\n\r\n constructor(\r\n private fb: FormBuilder,\r\n public modalRef: NzModalRef,\r\n private store: Store,\r\n private timHelperService: TimHelperService\r\n\r\n ) { }\r\n\r\n ngOnInit(): void {\r\n this.form = this.fb.group({\r\n avatar: [this.userProfile.avatar],\r\n nick: [this.userProfile.nick],\r\n gender: [this.userProfile.gender]\r\n });\r\n }\r\n\r\n submitForm() {\r\n const formValue = this.form.value;\r\n if (formValue.avatar && formValue.avatar.indexOf('http') === -1) {\r\n this.store.dispatch(\r\n showAction({ msgType: MESSAGE_STATUS.warning, message: '头像应该是 Url 地址' })\r\n );\r\n formValue.avatar = '';\r\n return;\r\n }\r\n const options = {};\r\n // 过滤空串\r\n Object.keys(formValue).forEach(key => {\r\n if (formValue[key]) {\r\n options[key] = formValue[key];\r\n }\r\n });\r\n this.timHelperService.tim\r\n .updateMyProfile(options as Profile)\r\n .then(() => {\r\n this.store.dispatch(\r\n showAction({ msgType: MESSAGE_STATUS.success, message: '修改成功' })\r\n );\r\n this.modalRef.destroy();\r\n })\r\n .catch(imError => {\r\n this.store.dispatch(\r\n showAction({ msgType: MESSAGE_STATUS.error, message: imError.message })\r\n );\r\n });\r\n }\r\n\r\n}\r\n","<form nz-form [formGroup]=\"form\" (ngSubmit)=\"submitForm()\">\r\n <nz-form-item>\r\n <nz-form-label [nzSm]=\"6\" [nzXs]=\"24\">\r\n <span>头像</span>\r\n </nz-form-label>\r\n <nz-form-control [nzSm]=\"14\" [nzXs]=\"24\">\r\n <input nz-input formControlName=\"avatar\" placeholder=\"头像地址\" />\r\n </nz-form-control>\r\n </nz-form-item>\r\n\r\n <nz-form-item>\r\n <nz-form-label [nzSm]=\"6\" [nzXs]=\"24\" nzFor=\"avatar\">昵称</nz-form-label>\r\n <nz-form-control [nzSm]=\"14\" [nzXs]=\"24\">\r\n <input nz-input formControlName=\"nick\" placeholder=\"昵称\" />\r\n </nz-form-control>\r\n </nz-form-item>\r\n\r\n <nz-form-item>\r\n <nz-form-label [nzSm]=\"6\" [nzXs]=\"24\">性别\r\n </nz-form-label>\r\n <nz-form-control [nzSm]=\"14\" [nzXs]=\"24\">\r\n <nz-radio-group formControlName=\"gender\">\r\n <label *ngFor=\"let item of radioOption\" nz-radio\r\n [nzValue]=\"item.value\">{{item.label}}</label>\r\n </nz-radio-group>\r\n </nz-form-control>\r\n </nz-form-item>\r\n\r\n\r\n <nz-form-item nz-row nzJustify=\"center\">\r\n <nz-form-control [nzSpan]=\"12\" nzPush=\"8\">\r\n <button nz-button nzType=\"primary\">确定</button>\r\n </nz-form-control>\r\n <nz-form-control [nzSpan]=\"12\">\r\n <button nz-button nzType=\"default\" type=\"button\" (click)=\"modalRef.destroy()\">取消</button>\r\n </nz-form-control>\r\n </nz-form-item>\r\n</form>\r\n","import { Component, Input, OnInit, } from '@angular/core';\r\nimport { TIM } from '../shared.data';\r\n\r\n@Component({\r\n selector: 'im-avatar',\r\n templateUrl: './avatar.component.html',\r\n styleUrls: ['./avatar.component.less'],\r\n})\r\nexport class AvatarComponent implements OnInit {\r\n @Input() shape: 'circle' | 'square' = 'circle';\r\n @Input() size: 'large' | 'small' | 'default' | number = 'large';\r\n @Input() type: string = 'C2C';\r\n @Input() title: string;\r\n @Input() set src(value: string) {\r\n this._src = value;\r\n if (/^(https:|http:|\\/\\/)/.test(value)) {\r\n this.avatarSrc = value;\r\n } else {\r\n this.avatarSrc = this.getDefaultAvatar();\r\n }\r\n };\r\n get src() {\r\n return this._src;\r\n }\r\n\r\n private _src: string;\r\n\r\n avatarSrc: string;\r\n\r\n constructor() { }\r\n\r\n ngOnInit(): void {\r\n }\r\n\r\n\r\n getDefaultAvatar() {\r\n switch (this.type) {\r\n case 'C2C':\r\n // 个人头像\r\n return 'https://imgcache.qq.com/open/qcloud/video/act/webim-avatar/avatar-2.png';\r\n case 'GROUP':\r\n // 群默认头像\r\n return 'https://imgcache.qq.com/open/qcloud/video/act/webim-avatar/avatar-3.png';\r\n case TIM.TYPES.CONV_SYSTEM:\r\n return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAK4AAACpCAMAAAHy0MbyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJYUExURQrBYAvBYRPDZhzGbCXIcSrJdSHHbxjFaQ7CYwzBYSnJdEzSi3bcpqLnw87z3+X47uj58Ov68uz68+n58ef579/36rzu05Djt2TYmz7OghnFai/KeIHfrcPw2OP47fz+/f////P8+Nb15K7ry1vVlBrFaw7CYlDTjqrqyPP899j15YrhsxDCZFfUksDv1vr+/C7KdyzKdq3qynLbo2HXmf7//rTszhTDZo/jtk7SjCjJc7nt0fX9+THLes3y3/3//oXgsA3CYjTMe5HjuA/CY8/z4IDfrRbEaLft0GbYnO779Pb9+cLv1x3GbNL04ub575zmv5flvEDPgzDLeaXoxIzitPD79TXMfB7GbeL47F7WlhLDZavqyfj9+03SjN326YvitEbQh3jdqO/79CLHcLHrzE/TjSPHcOT47vH79rHrzUvSivf9+vn++8zy3lXUkdf15SDHblnVk+D36ybIclrVlGfZnHjdpznNf1HTjhfEadv26I3itUfRiMfx26Pow0HPhJPkuZjlvMHv1zjNfkTQhWnZnhvFa8ry3TLLepXkurbt0Gvan4LfrknRiV/Wl9X05HHbo7/v1Z3mv8jx24jhskjRiTrNf8nx3Mvy3WLXmUfQh0XQhu3681LTj9n15ljVk2XYm6/ry+H37Or68bXsz2PYmjfMfW3aoLvu0zbMfTPLe77u1S7KeErRirDrzNr253fcpkPPhSvJdafpxr3u1N736n/frPL89o7itW/boWDXl3ndqJvmvvv+/Xveqsbx2tz26fT8+BHDZD3OgYTgr4fhsQAAAKRC35oAAADIdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8A9ppxxwAAAAlwSFlzAAAXEQAAFxEByibzPwAABVpJREFUaEPt2zuP2koUB3BaN1dCLikwols5fVb5Cim3QaxEtNomDdJKaHWbbaBASPcTUKVBShv6NNH6e2Uefxs/5u0H7NX5NXiOzwwjY+yxPR4N422MhYqEw/KFiDbiZ3UY0V7D1ThizDMiAmIcIkIURaycxlGEQCFLRvUvlDJ8/j+MP2OhTGymxoqxCDc2ioz6/mgo5hB1C39BtBZGkEFAQIiLERKiKN2xtc0ffpSkD1MsVhzxSZxgoydLlA2QKfyLmA7S4Buiaitk5RBWQ04BYTXkFBBWQ04BYTXkFA6IqyCl5AvWNGB9DVbWYW3NGWvVZGOZsb8X8i+MAiHe5rOFeYfMbcSuliQzlA3WSGUQ0fqGPAExHWRJvxFUwzk2h6gacnKIqiEnt0VYCTm5FcJKyMlVzrB1yMl9tFzTBn5ASgFxFWRc6I+YSCjDmoZGDzisq8PaKuWQR52qaTi/BKjB2jo28mZjb7k2TkRRLGv9lCcgTUdrkiQbpbZTVk5003VYH2X3lo4SQkiAN3EokhYbBNuqDFWkN6xqoTRWKmt5d2SJZhSQESQfAyohJwRa0ECSvzka0LhHmrcZGtBYI80b6usYR1kG7ARuRO1KH63d0D8GausFdfiAykbI9fAdNS0+Id2RU2cl1HCivHrQ8bgPihpuvqOSi/RxFRd4XSwyU1bEIndKUcVbym8fYplhBeNlswfWVIJuTXh35WIH7lhjvEHhAcEuRO9/ZKOrPSKEEEIIIVey+e+f8Wy8e3V4yuxuWb4AV02QCFO/rH9CvB3FLRPlbB5P6lsmWBnuBQ3VYXUwNNPg8NTPhA+o1Vr9eKb7UUgJUnt8WDFHTgDDXTkGSQHM97ncHt6ooAGN4NtctstZZHnbo74O0rxpHsgVkObtHvV1Qq+4be2G3hCndiVqV6J2pb7afUJ9HbcpYk316Xx15smAeqiuhzxPW9TWq8xQdmW9u+53J7VQ3Dc0CJgCa98KHJLd2c5tOaQ7QzUr46yoJpeNK3ndY85QyYXPcApV3KCOA9cfTZqglp1fu+7/utT9Z9NNoFJLj2s8nuBYZSxx1dKd919OTKxixLOiNC+OnnkJBdtUKzN+ALo8gub/byy2xVq6bMRKoR3WxekjllfsJw1+fl7H+pjEfHd65ztKV90did/q8hynw+lmomEIfi6mdEKr284nx02y7GlPU+4IIYQQQgghhBBisVxuNptOp4j0YpOt60/wF/MfN9nvHzt0UGH2OfTJUT9eTdMipPmt9Hhj2K4Vr6hwTYcFOuOiu3lUYZauWzZ31S3s90hD2F3vQKF4xdBudq3/3G90wFfoc+t2grYtN75Gf1/x5QEWaGJAL7YpBSah0w3C2SZWGI0HPzzoJ0+66GZ2rDuPV+tUWkyvDGKbvGOx7vbxn9UnfG+g1cDnirbdfUE7A6Hu9om62yfqbp+ou336YN1tMTjn4oFPwm5TR/U6m+flJOCKvSr8vZQA5ZlggQa8/jn7TMTU8ZpZ20ary7SLfMpiv9J2V2klsfuE3WBHfFcnMjTal32buwsKcfgbcQ463bRSfxeZ6Rpf0a2A9xtcTLo4fKn0soH3fAp6P/o4xfWw3+amPfzhetpzuR2+olv3fMp89+56HJ1F+4kCdpQTihVnuc/HB5TLDpOB75VJE9GjZKX6cow1Bx0xWkR4A+AB5ZIU58Gejq5hnnGUaxxCMdacnlC+EZP8qHwqvwyB/ZZF0xt7RSJdF4eN6Wl7PP7asoHmFNv2J5JuyVk3Wnu8yt/fKkq32Jwlq/tbflMmet/e/ZEdncZfj0Gvvw4s35ztXpkjhBBCCCGkrdHoLzv9X1u4OdoGAAAAAElFTkSuQmCC';\r\n default:\r\n // 默认头像\r\n return 'https://imgcache.qq.com/open/qcloud/video/act/webim-avatar/avatar-1.png';\r\n }\r\n }\r\n\r\n}\r\n","<nz-avatar [nzSize]=\"size\" [nzShape]=\"shape\" [nzSrc]=\"avatarSrc\" [nzAlt]=\"title\" [title]=\"title\"></nz-avatar>\r\n","import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';\r\nimport { Profile } from 'tim-js-sdk';\r\nimport { TIM } from '../../shared.data';\r\n\r\n@Component({\r\n selector: 'im-profile-card',\r\n templateUrl: './profile-card.component.html',\r\n styleUrls: ['./profile-card.component.less'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class ProfileCardComponent implements OnInit {\r\n\r\n @Input() set userProfile(value: Profile) {\r\n this._userProfile = value;\r\n switch (value.gender) {\r\n case TIM.TYPES.GENDER_MALE:\r\n this.className = 'icon-male';\r\n break;\r\n case TIM.TYPES.GENDER_FEMALE:\r\n this.className = 'icon-female';\r\n break;\r\n default:\r\n this.className = null;\r\n }\r\n };\r\n\r\n get userProfile() {\r\n return this._userProfile;\r\n }\r\n\r\n className: string;\r\n\r\n private _userProfile: Profile;\r\n\r\n constructor() { };\r\n\r\n ngOnInit(): void {\r\n\r\n }\r\n\r\n}\r\n","<div class=\"profile-card-wrapper\">\r\n <div class=\"content\">\r\n <im-avatar [src]=\"userProfile?.avatar\"></im-avatar>\r\n <div class=\"basic\">\r\n <span class=\"nick text-ellipsis\">{{ userProfile?.nick || userProfile?.userID }}</span>\r\n <span class=\"iconfont\" [class]=\"className\"></span>\r\n </div>\r\n </div>\r\n</div>\r\n","import { Component, OnDestroy, OnInit } from '@angular/core';\r\nimport { Store } from '@ngrx/store';\r\nimport { NzModalService } from 'ng-zorro-antd/modal';\r\nimport { Subscription } from 'rxjs';\r\n\r\nimport { Profile } from 'tim-js-sdk';\r\nimport { currentUserProfileSelector } from '../store/selectors';\r\nimport { EditProfileComponent } from './edit-profile/edit-profile.component';\r\n\r\n@Component({\r\n selector: 'app-my-profile',\r\n templateUrl: './my-profile.component.html',\r\n styleUrls: ['./my-profile.component.less'],\r\n})\r\nexport class MyProfileComponent implements OnInit, OnDestroy {\r\n currentUserProfile: Profile;\r\n subscription: Subscription;\r\n\r\n constructor(\r\n private store: Store,\r\n private modal: NzModalService,\r\n ) { }\r\n\r\n ngOnInit(): void {\r\n this.subscription = this.store.select(currentUserProfileSelector)\r\n .subscribe(res => {\r\n if (res) {\r\n this.currentUserProfile = res;\r\n }\r\n });\r\n }\r\n\r\n editProfile() {\r\n this.modal.create({\r\n nzTitle: `编辑资料`,\r\n nzContent: EditProfileComponent,\r\n nzMaskClosable: false,\r\n nzFooter: null,\r\n nzWidth: '40%',\r\n nzStyle: { top: '20px' },\r\n nzComponentParams: {\r\n userProfile: this.currentUserProfile\r\n }\r\n });\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this.subscription) {\r\n this.subscription.unsubscribe();\r\n }\r\n }\r\n\r\n}\r\n","<ng-template #titleTemplate>\r\n <div class=\"title-container\">\r\n <span>当前用户</span>\r\n <i nz-icon nzType=\"setting\" nzTheme=\"outline\" (click)=\"editProfile()\"></i>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #contentTemplate>\r\n <im-profile-card [userProfile]=\"currentUserProfile\"></im-profile-card>\r\n</ng-template>\r\n\r\n<div class=\"my-profile-wrapper\">\r\n <div nz-button nz-popover nzType=\"primary\" nzPopoverTrigger=\"click\"\r\n [nzPopoverTitle]=\"titleTemplate\" nzPopoverPlacement=\"right\"\r\n [nzPopoverContent]=\"contentTemplate\">\r\n <im-avatar [src]=\"currentUserProfile.avatar\"></im-avatar>\r\n </div>\r\n</div>\r\n","/**\r\n * 返回年月日\r\n * @export\r\n * @param {Date} date\r\n * @param {string} [splitor='-']\r\n * @returns\r\n */\r\nexport function getDate(date, splitor = '-') {\r\n const year = date.getFullYear()\r\n const month = date.getMonth() + 1\r\n const day = date.getDate()\r\n return `${year}${splitor}${addZeroPrefix(month)}${splitor}${addZeroPrefix(day)}`\r\n}\r\n\r\n/**\r\n * 返回时分秒/时分\r\n * @export\r\n * @param {*} date\r\n * @param {boolean} [withSecond=false]\r\n * @returns\r\n */\r\nexport function getTime(date, withSecond = false) {\r\n const hour = date.getHours()\r\n const minute = date.getMinutes()\r\n const second = date.getSeconds()\r\n return withSecond ? `${addZeroPrefix(hour)}:${addZeroPrefix(minute)}:${addZeroPrefix(second)}` : `${hour}:${addZeroPrefix(minute)}`\r\n}\r\n\r\nexport function getFullDate(date) {\r\n return `${getDate(date)} ${getTime(date)}`\r\n}\r\n\r\nexport function isToday(date) {\r\n return date.toDateString() === new Date().toDateString()\r\n}\r\n\r\n\r\n/**\r\n * 个位数,加0前缀\r\n * @param {*} number\r\n * @returns\r\n */\r\nfunction addZeroPrefix(number) {\r\n return number < 10 ? `0${number}`:number\r\n}\r\n","import { Component, Input, OnDestroy, OnInit } from '@angular/core';\r\n\r\nimport { getDate, getTime, isToday } from '../../util/date';\r\n\r\nimport { Store } from '@ngrx/store';\r\n\r\nimport { TimHelperService } from '../../tim-helper.service';\r\n\r\nimport { Subscription } from 'rxjs';\r\nimport { currentUserProfileSelector } from '../../store/selectors';\r\nimport { resetCurrentConversationAction, showAction } from '../../store/actions';\r\n\r\nimport TIM, { Conversation, Profile } from 'tim-js-sdk';\r\nimport { MESSAGE_STATUS } from '../../shared.data';\r\n\r\n\r\n@Component({\r\n selector: 'app-conversation-item',\r\n templateUrl: './conversation-item.component.html',\r\n styleUrls: ['./conversation-item.component.less']\r\n})\r\nexport class ConversationItemComponent implements OnInit, OnDestroy {\r\n @Input() currentConversation: Conversation;\r\n @Input() conversation: Conversation;\r\n\r\n TIM = TIM;\r\n currentUserProfile: Profile;\r\n profileSubscription: Subscription;\r\n\r\n constructor(\r\n private store: Store,\r\n private timHelperService: TimHelperService\r\n ) { }\r\n\r\n ngOnInit(): void {\r\n this.profileSubscription = this.store.select(currentUserProfileSelector)\r\n .subscribe(res => {\r\n this.currentUserProfile = res;\r\n });\r\n }\r\n\r\n selectConversation() {\r\n if (this.conversation.conversationID !== this.currentConversation.conversationID) {\r\n this.timHelperService.checkoutConversation(this.conversation.conversationID);\r\n // this.timHelperService.eventBus$.next('select-item');\r\n }\r\n }\r\n\r\n get avatarSrc() {\r\n switch (this.conversation.type) {\r\n case 'GROUP':\r\n return this.conversation.groupProfile?.avatar;\r\n case 'C2C':\r\n return this.conversation.userProfile?.avatar;\r\n default:\r\n return null;\r\n }\r\n\r\n };\r\n\r\n get date() {\r\n if (!this.conversation.lastMessage || !this.conversation.lastMessage.lastTime) {\r\n return '';\r\n }\r\n const date = new Date(this.conversation.lastMessage.lastTime * 1000);\r\n if (isToday(date)) {\r\n return getTime(date);\r\n }\r\n return getDate(date);\r\n }\r\n get messageForShow() {\r\n if (this.conversation.lastMessage.isRevoked) {\r\n if (this.conversation.lastMessage.fromAccount === this.currentUserProfile?.userID) {\r\n return '你撤回了一条消息';\r\n }\r\n if (this.conversation.type === TIM.TYPES.CONV_C2C) {\r\n return '对方撤回了一条消息';\r\n }\r\n return `${this.conversation.lastMessage.fromAccount}撤回了一条消息`;\r\n }\r\n return this.conversation.lastMessage.messageForShow;\r\n }\r\n\r\n get conversationName() {\r\n if (this.conversation.type === TIM.TYPES.CONV_C2C) {\r\n return this.conversation.userProfile.nick || this.conversation.userProfile.userID;\r\n }\r\n if (this.conversation.type === TIM.TYPES.CONV_GROUP) {\r\n return this.conversation.groupProfile.name || this.conversation.groupProfile.groupID;\r\n }\r\n if (this.conversation.type === TIM.TYPES.CONV_SYSTEM) {\r\n return '系统通知';\r\n }\r\n return '';\r\n }\r\n\r\n\r\n deleteConversation(event) {\r\n // 停止冒泡,避免和点击会话的事件冲突\r\n event.stopPropagation();\r\n this.timHelperService.tim\r\n .deleteConversation(this.conversation.conversationID)\r\n .then(() => {\r\n this.store.dispatch(\r\n showAction({\r\n message: `会话【${this.conversationName}】删除成功!`,\r\n msgType: MESSAGE_STATUS.success\r\n }));\r\n this.store.dispatch(resetCurrentConversationAction());\r\n })\r\n .catch(error => {\r\n this.store.dispatch(\r\n showAction({\r\n message: `会话【${this.conversationName}】删除失败!, error=${error.message}`,\r\n msgType: MESSAGE_STATUS.error\r\n }));\r\n\r\n });\r\n }\r\n\r\n\r\n ngOnDestroy(): void {\r\n if (this.profileSubscription) {\r\n this.profileSubscription.unsubscribe();\r\n }\r\n }\r\n\r\n}\r\n","<div class=\"conversation-item-container\"\r\n [ngClass]=\"{'choose': conversation.conversationID === currentConversation.conversationID }\"\r\n (click)=\"selectConversation()\">\r\n <div class=\"close-btn\">\r\n <span class=\"tim-icon-close\" title=\"删除会话\" (click)=\"deleteConversation($event)\"></span>\r\n </div>\r\n <div class=\"warp\">\r\n <im-avatar [src]=\"avatarSrc\" [type]=\"conversation.type\"></im-avatar>\r\n <div class=\"content\">\r\n <div class=\"row-1\">\r\n <div class=\"name\">\r\n <div class=\"text-ellipsis\">\r\n <span [title]=\"conversation.userProfile.nick || conversation.userProfile.userID\"\r\n *ngIf=\"conversation.type === TIM.TYPES.CONV_C2C\">\r\n {{conversation.userProfile.nick || conversation.userProfile.userID}}\r\n </span>\r\n <span [title]=\"conversation.groupProfile.name || conversation.groupProfile.groupID\"\r\n *ngIf=\"conversation.type===TIM.TYPES.CONV_GROUP\">\r\n {{conversation.groupProfile.name || conversation.groupProfile.groupID}}\r\n </span>\r\n <span *ngIf=\"conversation.type === TIM.TYPES.CONV_SYSTEM\">系统通知\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"unread-count\">\r\n <span class=\"badge\" *ngIf=\"this.conversation.unreadCount > 0\">\r\n {{conversation.unreadCount > 99 ? '99+' : conversation.unreadCount}}\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"row-2\">\r\n <div class=\"summary\">\r\n <div v-if=\"conversation.lastMessage\" class=\"text-ellipsis\">\r\n <!-- <span class=\"remind\" style=\"color:red;\">[有人提到我]</span> -->\r\n <span class=\"text\" [title]=\"conversation.lastMessage.messageForShow\">\r\n {{messageForShow}}\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"date\">\r\n {{date}}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n","import { Component, OnDestroy, OnInit } from '@angular/core';\r\nimport { TimHelperService } from '../../tim-helper.service';\r\n\r\nimport { Store } from '@ngrx/store';\r\n\r\nimport { Conversation } from 'tim-js-sdk';\r\nimport { currentConversationSelector, conversationListSelector } from '../../store/selectors';\r\nimport { showAction } from '../../store/actions';\r\nimport { Subscription } from 'rxjs';\r\nimport { MESSAGE_STATUS } from '../../shared.data';\r\n\r\n\r\n@Component({\r\n selector: 'app-conversation-list',\r\n templateUrl: './conversation-list.component.html',\r\n styleUrls: ['./conversation-list.component.less']\r\n})\r\nexport class ConversationListComponent implements OnInit, OnDestroy {\r\n conversationList: Array<Conversation> = [];\r\n timeout = null;\r\n showDialog = false;\r\n userID = '';\r\n subscription: Subscription;\r\n storeSubscription: Subscription;\r\n currentConversation: Conversation;\r\n\r\n constructor(\r\n private store: Store,\r\n private timHelperService: TimHelperService\r\n ) { }\r\n\r\n ngOnInit(): void {\r\n\r\n this.storeSubscription = this.store.select(currentConversationSelector)\r\n .subscribe(res => {\r\n this.currentConversation = res;\r\n });\r\n\r\n // 获取当前list会话\r\n this.subscription = this.store.select(conversationListSelector)\r\n .subscribe(res => {\r\n this.conversationList = res;\r\n });\r\n };\r\n\r\n add() {\r\n this.showDialog = true;\r\n }\r\n\r\n handleOk(): void {\r\n if (this.userID !== '@TIM#SYSTEM') {\r\n this.timHelperService.checkoutConversation(`C2C${this.userID}`);\r\n this.showDialog = false;\r\n }\r\n this.userID = '';\r\n }\r\n\r\n handleCancel(): void {\r\n this.showDialog = false;\r\n }\r\n\r\n refresh() {\r\n // if (!this.timeout) {\r\n // this.timeout = setTimeout(() => {\r\n // this.timeout = null;\r\n\r\n // }, 1000);\r\n // }\r\n\r\n // 拉取会话列表\r\n this.timHelperService.tim.getConversationList().then(({ data }) => {\r\n this.store.dispatch(showAction({ msgType: MESSAGE_STATUS.success, message: '刷新成功!' }));\r\n\r\n }).catch((imError) => {\r\n console.error('getConversationList error:', imError); // 获取会话列表失败的相关信息\r\n });\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this.subscription) {\r\n this.subscription.unsubscribe();\r\n }\r\n if (this.storeSubscription) {\r\n this.storeSubscription.unsubscribe();\r\n }\r\n }\r\n\r\n}\r\n","<div class=\"list-container\">\r\n <div class=\"header-bar\">\r\n <button title=\"刷新列表\" (cl