mirror of
https://code.equilibrium.co.ao/ITO/doneit-web.git
synced 2026-04-20 13:26:08 +00:00
receive user typing
This commit is contained in:
@@ -96,6 +96,7 @@ import { DeplomaOptionsPageModule } from './shared/popover/deploma-options/deplo
|
|||||||
import { DiplomaOptionsPage } from './shared/popover/deploma-options/deploma-options.page';
|
import { DiplomaOptionsPage } from './shared/popover/deploma-options/deploma-options.page';
|
||||||
import { ImageCropperModule } from 'ngx-image-cropper';
|
import { ImageCropperModule } from 'ngx-image-cropper';
|
||||||
import { createAction, createReducer, on, StoreModule } from '@ngrx/store';
|
import { createAction, createReducer, on, StoreModule } from '@ngrx/store';
|
||||||
|
import { typingReducer } from './module/chat/data/data-source/userTyping/user-typing-memory-data-source.service';
|
||||||
// import { ServiceWorkerModule } from '@angular/service-worker';
|
// import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
// import { AngularFireModule } from '@angular/fire';
|
// import { AngularFireModule } from '@angular/fire';
|
||||||
// import { AngularFireMessagingModule } from '@angular/fire/messaging';
|
// import { AngularFireMessagingModule } from '@angular/fire/messaging';
|
||||||
@@ -143,7 +144,9 @@ export function counterReducer(state, action) {
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent, PopupQuestionPipe, InputFilterDirective],
|
declarations: [AppComponent, PopupQuestionPipe, InputFilterDirective],
|
||||||
imports: [BrowserModule,
|
imports: [
|
||||||
|
StoreModule.forRoot({ userTyping: typingReducer }),
|
||||||
|
BrowserModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
CalendarModule.forRoot({
|
CalendarModule.forRoot({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MessageAsyncService } from 'src/app/module/chat/data/async/socket/message-async.service'
|
import { MessageAsyncService } from 'src/app/module/chat/data/async/socket/message-async.service'
|
||||||
|
import { UserTypingAsyncService } from 'src/app/module/chat/data/async/socket/user-typing-async.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -7,6 +8,7 @@ import { MessageAsyncService } from 'src/app/module/chat/data/async/socket/messa
|
|||||||
export class ChatServiceService {
|
export class ChatServiceService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private MessageAsyncService: MessageAsyncService
|
private MessageAsyncService: MessageAsyncService,
|
||||||
|
private UserTypingAsyncService: UserTypingAsyncService
|
||||||
) { }
|
) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { MessageRemoteDataSourceService } from '../../data-source/message/messag
|
|||||||
import { SignalRService } from '../../../infra/socket/signal-r.service';
|
import { SignalRService } from '../../../infra/socket/signal-r.service';
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
import { InstanceId } from '../../repository/message-respository.service';
|
import { InstanceId } from '../../repository/message-respository.service';
|
||||||
import { SocketStreamReturn } from 'src/app/services/decorators/socket-validate-schema.decorator';
|
|
||||||
import { SafeValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
import { SafeValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MessageLiveDataSourceService } from '../../data-source/message/message-live-data-source.service';
|
||||||
|
import { RoomLiveDataSourceService } from '../../data-source/room/room-live-data-source.service';
|
||||||
|
import { RoomRemoteDataSourceService } from '../../data-source/room/room-remote-data-source.service';
|
||||||
|
import { roomDataSource, RoomLocalDataSourceService } from '../../data-source/room/rooom-local-data-source.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class RoomAsyncService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private roomRemoteDataSourceService: RoomRemoteDataSourceService,
|
||||||
|
// private roomMemoryDataSourceService: Store<RoomRemoteDataSourceState>,
|
||||||
|
private roomLocalDataSourceService: RoomLocalDataSourceService,
|
||||||
|
private roomLiveDataSourceService: RoomLiveDataSourceService,
|
||||||
|
private messageLiveDataSourceService: MessageLiveDataSourceService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
roomDataSource.typing.hook('creating', (primKey, obj, trans) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
incomingTyping() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeUserTyping() {
|
||||||
|
const result = await this.roomLocalDataSourceService.removeUserTyping()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { UserTypingLiveDataSourceService } from '../../data-source/userTyping/user-typing-live-data-source.service';
|
||||||
|
import { UserTypingLocalDataSourceService } from '../../data-source/userTyping/user-typing-local-data-source.service';
|
||||||
|
import { SignalRService } from '../../../infra/socket/signal-r.service';
|
||||||
|
import { interval, Subject, timer } from 'rxjs';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import { addUserTyping, removeUserTyping, TypingState } from '../../data-source/userTyping/user-typing-memory-data-source.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserTypingAsyncService {
|
||||||
|
|
||||||
|
typingCallback: {[key: string]: Subject<any> } = {}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private localDataSource: UserTypingLocalDataSourceService,
|
||||||
|
private liveDataSource: UserTypingLiveDataSourceService,
|
||||||
|
private memoryDataSource: Store<TypingState>,
|
||||||
|
private signalR: SignalRService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
this.signalR.getTyping().subscribe(async (e:any) => {
|
||||||
|
if(e?.chatRoomId) {
|
||||||
|
|
||||||
|
console.log('e', e)
|
||||||
|
|
||||||
|
this.memoryDataSource.dispatch(removeUserTyping({data: {...e} as any}))
|
||||||
|
this.memoryDataSource.dispatch(addUserTyping({data: {...e} as any}))
|
||||||
|
//
|
||||||
|
const value = await this.localDataSource.addUserTyping(e);
|
||||||
|
|
||||||
|
const id = e.chatRoomId + '@' + e.userName
|
||||||
|
if(!this.typingCallback[id]) {
|
||||||
|
this.typingCallback[id] = new Subject()
|
||||||
|
this.typingCallback[id].pipe(
|
||||||
|
switchMap(() => timer(2000)),
|
||||||
|
).subscribe(() => {
|
||||||
|
console.log('111111==============')
|
||||||
|
this.memoryDataSource.dispatch(removeUserTyping({data: {...e} as any}))
|
||||||
|
this.localDataSource.removeUserTyping(e)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.typingCallback[id].next()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('e--', e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,18 +40,30 @@ const TableMemberListSchema = z.object({
|
|||||||
joinAt: z.string()
|
joinAt: z.string()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export const TypingSchema = z.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
userId: z.string(),
|
||||||
|
roomId: z.string(),
|
||||||
|
entryDate: z.string()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
export type TableRoom = z.infer<typeof tableSchema>
|
export type TableRoom = z.infer<typeof tableSchema>
|
||||||
export type TableMemberList = z.infer<typeof TableMemberListSchema>
|
export type TableMemberList = z.infer<typeof TableMemberListSchema>
|
||||||
|
export type TypingList = z.infer<typeof TypingSchema>
|
||||||
|
|
||||||
// Database declaration (move this to its own module also)
|
// Database declaration (move this to its own module also)
|
||||||
export const roomDataSource = new Dexie('FriendDatabase') as Dexie & {
|
export const roomDataSource = new Dexie('FriendDatabase') as Dexie & {
|
||||||
room: EntityTable<TableRoom, 'id'>;
|
room: EntityTable<TableRoom, 'id'>;
|
||||||
memberList: EntityTable<TableMemberList, '$roomIdUserId'>;
|
memberList: EntityTable<TableMemberList, '$roomIdUserId'>;
|
||||||
|
typing: EntityTable<TableMemberList, '$roomIdUserId'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
roomDataSource.version(1).stores({
|
roomDataSource.version(1).stores({
|
||||||
room: 'id, createdBy, roomName, roomType, expirationDate, lastMessage',
|
room: 'id, createdBy, roomName, roomType, expirationDate, lastMessage',
|
||||||
memberList: '$roomIdUserId, id, user, joinAt, roomId',
|
memberList: '$roomIdUserId, id, user, joinAt, roomId',
|
||||||
|
TypingList: '++id, userId, roomId, entryDate'
|
||||||
});
|
});
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@@ -63,6 +75,20 @@ export class RoomLocalDataSourceService {
|
|||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
@ValidateSchema(TypingSchema)
|
||||||
|
async addUserTyping(data: any) {
|
||||||
|
try {
|
||||||
|
const result = await roomDataSource.typing.add(data)
|
||||||
|
return ok(result)
|
||||||
|
} catch (e) {
|
||||||
|
return err(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeUserTyping() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ValidateSchema(tableSchema)
|
@ValidateSchema(tableSchema)
|
||||||
async createRoom(data: TableRoom) {
|
async createRoom(data: TableRoom) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { SignalRService } from '../../../infra/socket/signal-r.service';
|
||||||
|
import { SessionStore } from 'src/app/store/session.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserTypingLiveDataSourceService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private SignalRLiveDataSourceService: SignalRService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
sendTyping(ChatRoomId) {
|
||||||
|
return this.SignalRLiveDataSourceService.sendTyping({ChatRoomId, UserName:SessionStore.user.FullName})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { Dexie, EntityTable, liveQuery, Observable } from 'Dexie';
|
||||||
|
import { err, ok } from 'neverthrow';
|
||||||
|
|
||||||
|
export const TypingSchema = z.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
userId: z.string().optional(),
|
||||||
|
userName: z.string(),
|
||||||
|
chatRoomId: z.string(),
|
||||||
|
entryDate: z.string()
|
||||||
|
})
|
||||||
|
|
||||||
|
export type TypingList = z.infer<typeof TypingSchema>
|
||||||
|
|
||||||
|
export type UserTypingList = z.infer<typeof TypingSchema>
|
||||||
|
|
||||||
|
|
||||||
|
// Database declaration (move this to its own module also)
|
||||||
|
export const TypingDataSource = new Dexie('UserTyping') as Dexie & {
|
||||||
|
TypingList: EntityTable<TypingList, 'id'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TypingDataSource.version(1).stores({
|
||||||
|
TypingList: 'id, userId, userName, chatRoomId, entryDate'
|
||||||
|
});
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserTypingLocalDataSourceService {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
|
||||||
|
async addUserTyping(data: TypingList) {
|
||||||
|
data.id = data.chatRoomId + '@' + data.userName
|
||||||
|
try {
|
||||||
|
const result = await TypingDataSource.TypingList.add(data)
|
||||||
|
return ok(result)
|
||||||
|
} catch (e) {
|
||||||
|
return err(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeUserTyping(data: TypingList) {
|
||||||
|
|
||||||
|
const id = data.chatRoomId + '@' + data.userName
|
||||||
|
try {
|
||||||
|
const result = await TypingDataSource.TypingList.delete(id)
|
||||||
|
return ok(result)
|
||||||
|
} catch (e) {
|
||||||
|
return err(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getUserTypingLive() {
|
||||||
|
return liveQuery(() => TypingDataSource.TypingList.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
import { createAction, createFeatureSelector, createReducer, createSelector, on, props } from '@ngrx/store';
|
||||||
|
import { TypingList } from './user-typing-local-data-source.service';
|
||||||
|
|
||||||
|
|
||||||
|
export const addUserTyping = createAction(
|
||||||
|
'[Typing] Add User Typing',
|
||||||
|
props<{ data: TypingList }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const removeUserTyping = createAction(
|
||||||
|
'[Typing] Remove User Typing',
|
||||||
|
props<{ data: TypingList }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const loadUserTyping = createAction('[Typing] Load User Typing');
|
||||||
|
|
||||||
|
export const loadUserTypingSuccess = createAction(
|
||||||
|
'[Typing] Load User Typing Success',
|
||||||
|
props<{ data: TypingList[] }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const loadUserTypingFailure = createAction(
|
||||||
|
'[Typing] Load User Typing Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
export interface TypingState {
|
||||||
|
typingList: TypingList[];
|
||||||
|
error: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState: TypingState = {
|
||||||
|
typingList: [],
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typingReducer = createReducer(
|
||||||
|
initialState,
|
||||||
|
on(loadUserTypingSuccess, (state, { data }) => ({
|
||||||
|
...state,
|
||||||
|
typingList: data
|
||||||
|
})),
|
||||||
|
on(loadUserTypingFailure, (state, { error }) => ({
|
||||||
|
...state,
|
||||||
|
error
|
||||||
|
})),
|
||||||
|
on(addUserTyping, (state, { data }) => ({
|
||||||
|
...state,
|
||||||
|
typingList: [...state.typingList, data]
|
||||||
|
})),
|
||||||
|
on(removeUserTyping, (state, { data }) => ({
|
||||||
|
...state,
|
||||||
|
typingList: state.typingList.filter(
|
||||||
|
typing => typing.chatRoomId !== data.chatRoomId || typing.userName !== data.userName
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectCalendarState = createFeatureSelector<TypingState>('userTyping');
|
||||||
|
|
||||||
|
export const selectAllUserSource = createSelector(
|
||||||
|
selectCalendarState,
|
||||||
|
(state: TypingState) => state.typingList
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectUserTypingList = () => createSelector(
|
||||||
|
selectAllUserSource,
|
||||||
|
(typingList) => typingList
|
||||||
|
);
|
||||||
@@ -50,6 +50,9 @@ export class MessageRepositoryService {
|
|||||||
|
|
||||||
if(localActionResult.isOk()) {
|
if(localActionResult.isOk()) {
|
||||||
|
|
||||||
|
(await this.sendTyping(data.roomId)).map((e) => {
|
||||||
|
console.log('map', e)
|
||||||
|
})
|
||||||
const sendMessageResult = await this.messageLiveSignalRDataSourceService.sendMessage(data)
|
const sendMessageResult = await this.messageLiveSignalRDataSourceService.sendMessage(data)
|
||||||
|
|
||||||
|
|
||||||
@@ -101,4 +104,8 @@ export class MessageRepositoryService {
|
|||||||
subscribeToNewMessages(roomId: any) {
|
subscribeToNewMessages(roomId: any) {
|
||||||
return this.messageLocalDataSourceService.subscribeToNewMessage(roomId)
|
return this.messageLocalDataSourceService.subscribeToNewMessage(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendTyping(ChatRoomId) {
|
||||||
|
return this.messageLiveSignalRDataSourceService.sendTyping({ChatRoomId, UserName:SessionStore.user.FullName})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,26 +42,7 @@ export class RoomRepositoryService {
|
|||||||
private roomLocalDataSourceService: RoomLocalDataSourceService,
|
private roomLocalDataSourceService: RoomLocalDataSourceService,
|
||||||
private roomLiveDataSourceService: RoomLiveDataSourceService,
|
private roomLiveDataSourceService: RoomLiveDataSourceService,
|
||||||
private messageLiveDataSourceService: MessageLiveDataSourceService,
|
private messageLiveDataSourceService: MessageLiveDataSourceService,
|
||||||
) {
|
) {}
|
||||||
|
|
||||||
// this.messageLiveDataSourceService.socket.messages$.subscribe(({payload, requestId, type}) => {
|
|
||||||
// if(payload.sender == null) {
|
|
||||||
// delete payload.sender
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(type == 'sendMessage') {
|
|
||||||
// let clone: TableMessage = {
|
|
||||||
// ...payload,
|
|
||||||
// messageId: payload.id,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.roomLocalDataSourceService.updateRoom({lastMessage: clone})
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// })
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@captureAndReraiseAsync('RoomRepositoryService/list')
|
@captureAndReraiseAsync('RoomRepositoryService/list')
|
||||||
async list() {
|
async list() {
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { TypingList, UserTypingLocalDataSourceService } from '../data-source/userTyping/user-typing-local-data-source.service';
|
||||||
|
import { UserTypingLiveDataSourceService } from '../data-source/userTyping/user-typing-live-data-source.service';
|
||||||
|
import { SessionStore } from 'src/app/store/session.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class UserTypingServiceRepository {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private localDataSource: UserTypingLocalDataSourceService,
|
||||||
|
private liveDataSource: UserTypingLiveDataSourceService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async addUserTyping(ChatRoomId: any) {
|
||||||
|
return await this.liveDataSource.sendTyping(ChatRoomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeUserTyping(data: TypingList) {
|
||||||
|
return await this.localDataSource.removeUserTyping(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getUserTypingLive() {
|
||||||
|
return this.localDataSource.getUserTypingLive()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,10 +13,11 @@ const { App } = Plugins;
|
|||||||
export class SignalRService {
|
export class SignalRService {
|
||||||
private connection: SignalRConnection;
|
private connection: SignalRConnection;
|
||||||
private messageSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
private messageSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
||||||
|
private typingSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
||||||
private connectingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
|
private connectingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private platform: Platform,) {
|
private platform: Platform) {
|
||||||
// this.startConnection();
|
// this.startConnection();
|
||||||
// this.addMessageListener();
|
// this.addMessageListener();
|
||||||
|
|
||||||
@@ -58,6 +59,9 @@ export class SignalRService {
|
|||||||
this.connection.getMessages().subscribe((data) => {
|
this.connection.getMessages().subscribe((data) => {
|
||||||
this.messageSubject.next(data)
|
this.messageSubject.next(data)
|
||||||
})
|
})
|
||||||
|
this.connection.getTyping().subscribe((data) => {
|
||||||
|
this.typingSubject.next(data)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,6 +72,10 @@ export class SignalRService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTyping() {
|
||||||
|
return this.typingSubject.asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
async sendMessage(data: Object) {
|
async sendMessage(data: Object) {
|
||||||
return await this.connection.sendMessage(data as any)
|
return await this.connection.sendMessage(data as any)
|
||||||
}
|
}
|
||||||
@@ -75,4 +83,8 @@ export class SignalRService {
|
|||||||
newConnection() {
|
newConnection() {
|
||||||
this.establishConnection()
|
this.establishConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendTyping({ChatRoomId, UserName}) {
|
||||||
|
return await this.connection.typing({ ChatRoomId, UserName})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
|||||||
import { ok, Result, err } from 'neverthrow';
|
import { ok, Result, err } from 'neverthrow';
|
||||||
import { SessionStore } from 'src/app/store/session.service';
|
import { SessionStore } from 'src/app/store/session.service';
|
||||||
import { filter, first } from 'rxjs/operators';
|
import { filter, first } from 'rxjs/operators';
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
export class SignalRConnection {
|
export class SignalRConnection {
|
||||||
|
|
||||||
private hubConnection: signalR.HubConnection;
|
private hubConnection: signalR.HubConnection;
|
||||||
private messageSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
private messageSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
||||||
|
private typingSubject: BehaviorSubject<string> = new BehaviorSubject<any>(null);
|
||||||
private connectionStateSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private connectionStateSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
private disconnectSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private disconnectSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
private reconnectSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private reconnectSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
@@ -27,7 +28,6 @@ export class SignalRConnection {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.hubConnection = hubConnection
|
this.hubConnection = hubConnection
|
||||||
this.join()
|
|
||||||
|
|
||||||
hubConnection
|
hubConnection
|
||||||
.start()
|
.start()
|
||||||
@@ -70,7 +70,9 @@ export class SignalRConnection {
|
|||||||
public join() {
|
public join() {
|
||||||
if(this.connectionStateSubject.value == true) {
|
if(this.connectionStateSubject.value == true) {
|
||||||
console.log('join=============')
|
console.log('join=============')
|
||||||
this.hubConnection.invoke("Join", 105, "UserFirefox");
|
|
||||||
|
this.hubConnection.invoke("Join", SessionStore.user.UserId, SessionStore.user.FullName);
|
||||||
|
//this.hubConnection.invoke("Join", 105, "UserFirefox");
|
||||||
} else {
|
} else {
|
||||||
this.sendLaterSubject.next({method: 'SendMessage', args:["Join", 312, "Daniel"]})
|
this.sendLaterSubject.next({method: 'SendMessage', args:["Join", 312, "Daniel"]})
|
||||||
}
|
}
|
||||||
@@ -101,17 +103,55 @@ export class SignalRConnection {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async typing(data: Object & { ChatRoomId, UserName}):Promise<Result<any, any>> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
const requestId = uuidv4()
|
||||||
|
if(this.connectionStateSubject.value == true) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.hubConnection.invoke("Typing", {UserName: data.UserName, ChatRoomId: data.ChatRoomId, requestId} as any)
|
||||||
|
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
this.typingSubject.pipe(
|
||||||
|
filter((message: any) => {
|
||||||
|
return requestId == message?.requestId
|
||||||
|
}),
|
||||||
|
first()
|
||||||
|
).subscribe(value => {
|
||||||
|
resolve(ok(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.sendLaterSubject.next({method: 'SendMessage', args: data})
|
||||||
|
return reject(err(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private addMessageListener(): void {
|
private addMessageListener(): void {
|
||||||
this.hubConnection.on('ReceiveMessage', (message) => {
|
this.hubConnection.on('ReceiveMessage', (message) => {
|
||||||
console.log('ReceiveMessage', message)
|
console.log('ReceiveMessage', message)
|
||||||
this.messageSubject.next(message);
|
this.messageSubject.next(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.hubConnection.on('Typing', (_message) => {
|
||||||
|
console.log('_message', _message)
|
||||||
|
this.typingSubject.next(_message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMessages(): Observable<string> {
|
public getMessages(): Observable<string> {
|
||||||
return this.messageSubject.asObservable()
|
return this.messageSubject.asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTyping(): Observable<string> {
|
||||||
|
return this.typingSubject.asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
public getConnectionState(): Observable<boolean> {
|
public getConnectionState(): Observable<boolean> {
|
||||||
return this.connectionStateSubject.asObservable();
|
return this.connectionStateSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ export function ValidateSchema(schema: Schema) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param schema Zod Schema
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
export function SafeValidateSchema(schema: Schema, context: string) {
|
export function SafeValidateSchema(schema: Schema, context: string) {
|
||||||
return (
|
return (
|
||||||
target: unknown,
|
target: unknown,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ion-refresher name="refresher" slot="fixed" (ionRefresh)="doRefresh($event)">
|
<ion-refresher name="refresher" slot="fixed" (ionRefresh)="doRefresh($event)">
|
||||||
@@ -56,6 +57,34 @@
|
|||||||
</ion-fab-button>
|
</ion-fab-button>
|
||||||
</ion-fab>
|
</ion-fab>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ion-fab horizontal="start" vertical="bottom" slot="fixed">
|
||||||
|
|
||||||
|
<div #array>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</ion-fab>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <div *ngIf="userTyping$?.typingList?.typingList">
|
||||||
|
|
||||||
|
<div
|
||||||
|
*ngFor="let message of userTyping$.typingList.typingList " class="messages-list-item-wrapper"
|
||||||
|
>
|
||||||
|
{{ message.userName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<!-- <div *ngIf="userTyping$" class="header-bottom-contacts" >
|
||||||
|
|
||||||
|
<div *ngFor="let typing of userTyping$; let i = index">
|
||||||
|
{{ typing.userName }}<div *ngIf="i < userTyping$.length - 1">, </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||||
import { AnimationController, GestureController, IonRange, ModalController, PopoverController } from '@ionic/angular';
|
import { AnimationController, GestureController, IonRange, ModalController, PopoverController } from '@ionic/angular';
|
||||||
import { ChatService } from 'src/app/services/chat.service';
|
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
import { ChatOptionsPopoverPage } from 'src/app/shared/popover/chat-options-popover/chat-options-popover.page';
|
import { ChatOptionsPopoverPage } from 'src/app/shared/popover/chat-options-popover/chat-options-popover.page';
|
||||||
import { MessagesOptionsPage } from 'src/app/shared/popover/messages-options/messages-options.page';
|
import { MessagesOptionsPage } from 'src/app/shared/popover/messages-options/messages-options.page';
|
||||||
@@ -16,7 +15,6 @@ import { ViewEventPage } from 'src/app/modals/view-event/view-event.page';
|
|||||||
import { Storage } from '@ionic/storage';
|
import { Storage } from '@ionic/storage';
|
||||||
import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-connector.service'
|
import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-connector.service'
|
||||||
import { MessageService } from 'src/app/services/chat/message.service';
|
import { MessageService } from 'src/app/services/chat/message.service';
|
||||||
import { CameraService } from 'src/app/services/camera.service';
|
|
||||||
import { FileType } from 'src/app/models/fileType';
|
import { FileType } from 'src/app/models/fileType';
|
||||||
import { SearchPage } from 'src/app/pages/search/search.page';
|
import { SearchPage } from 'src/app/pages/search/search.page';
|
||||||
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
|
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
|
||||||
@@ -42,6 +40,8 @@ import { TableMessage } from 'src/app/module/chat/data/data-source/message/messa
|
|||||||
import { TableMemberList } from 'src/app/module/chat/data/data-source/room/rooom-local-data-source.service';
|
import { TableMemberList } from 'src/app/module/chat/data/data-source/room/rooom-local-data-source.service';
|
||||||
import { MessageInputDTO } from 'src/app/module/chat/data/dto/message/messageInputDtO';
|
import { MessageInputDTO } from 'src/app/module/chat/data/dto/message/messageInputDtO';
|
||||||
import { RoomListItemOutPutDTO } from 'src/app/module/chat/data/dto/room/roomListOutputDTO';
|
import { RoomListItemOutPutDTO } from 'src/app/module/chat/data/dto/room/roomListOutputDTO';
|
||||||
|
import { UserTypingServiceRepository } from 'src/app/module/chat/data/repository/user-typing-repository.service';
|
||||||
|
import { UserTypingList } from 'src/app/module/chat/data/data-source/userTyping/user-typing-local-data-source.service';
|
||||||
|
|
||||||
|
|
||||||
const IMAGE_DIR = 'stored-images';
|
const IMAGE_DIR = 'stored-images';
|
||||||
@@ -109,6 +109,8 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
|||||||
audioDuration = 0;
|
audioDuration = 0;
|
||||||
audioTimer: any;
|
audioTimer: any;
|
||||||
@ViewChild('range', { static: false }) range: IonRange;
|
@ViewChild('range', { static: false }) range: IonRange;
|
||||||
|
@ViewChild('array') myInputRef!: ElementRef;
|
||||||
|
|
||||||
userName = "";
|
userName = "";
|
||||||
room: any = new Array();
|
room: any = new Array();
|
||||||
roomName: any;
|
roomName: any;
|
||||||
@@ -120,7 +122,8 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
|||||||
roomData$: DexieObservable<RoomListItemOutPutDTO | undefined>
|
roomData$: DexieObservable<RoomListItemOutPutDTO | undefined>
|
||||||
roomMessage$: DexieObservable<TableMessage[]>
|
roomMessage$: DexieObservable<TableMessage[]>
|
||||||
roomMembers$: DexieObservable<TableMemberList[] | undefined>
|
roomMembers$: DexieObservable<TableMemberList[] | undefined>
|
||||||
|
//userTyping$: DexieObservable<UserTypingList[] | undefined>
|
||||||
|
userTyping$: UserTypingList[] | undefined
|
||||||
newMessagesStream!: Subscription
|
newMessagesStream!: Subscription
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -135,7 +138,6 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
|||||||
public ThemeService: ThemeService,
|
public ThemeService: ThemeService,
|
||||||
private storage: Storage,
|
private storage: Storage,
|
||||||
public RochetChatConnectorService: RochetChatConnectorService,
|
public RochetChatConnectorService: RochetChatConnectorService,
|
||||||
private CameraService: CameraService,
|
|
||||||
private sanitiser: DomSanitizer,
|
private sanitiser: DomSanitizer,
|
||||||
private file: File,
|
private file: File,
|
||||||
private platform: Platform,
|
private platform: Platform,
|
||||||
@@ -143,7 +145,8 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
|||||||
public p: PermissionService,
|
public p: PermissionService,
|
||||||
private FileValidatorService: FileValidatorService,
|
private FileValidatorService: FileValidatorService,
|
||||||
private roomRepositoryService: RoomRepositoryService,
|
private roomRepositoryService: RoomRepositoryService,
|
||||||
private messageRepositoryService: MessageRepositoryService
|
private messageRepositoryService: MessageRepositoryService,
|
||||||
|
private userTypingServiceRepository: UserTypingServiceRepository,
|
||||||
) {
|
) {
|
||||||
// update
|
// update
|
||||||
this.checkAudioPermission()
|
this.checkAudioPermission()
|
||||||
@@ -156,6 +159,15 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
|||||||
this.roomRepositoryService.getRoomById(this.roomId)
|
this.roomRepositoryService.getRoomById(this.roomId)
|
||||||
this.messageRepositoryService.listAllMessagesByRoomId(this.roomId)
|
this.messageRepositoryService.listAllMessagesByRoomId(this.roomId)
|
||||||
|
|
||||||
|
this.userTypingServiceRepository.getUserTypingLive().subscribe((e) => {
|
||||||
|
const arrayNames = e.map(e => e.userName)
|
||||||
|
this.userTyping$ = e as any
|
||||||
|
|
||||||
|
const uniqueArray = [...new Set(arrayNames)];
|
||||||
|
|
||||||
|
(this.myInputRef.nativeElement as HTMLDivElement).innerHTML = '::'+ uniqueArray
|
||||||
|
this.scrollToBottomClicked()
|
||||||
|
})
|
||||||
|
|
||||||
this.newMessagesStream?.unsubscribe()
|
this.newMessagesStream?.unsubscribe()
|
||||||
this.newMessagesStream = this.messageRepositoryService.subscribeToNewMessages(this.roomId).subscribe((e) => {
|
this.newMessagesStream = this.messageRepositoryService.subscribeToNewMessages(this.roomId).subscribe((e) => {
|
||||||
@@ -169,9 +181,16 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//this.userTyping$ = this.userTypingMemoryDataSource.select(state => state) as any
|
||||||
|
|
||||||
|
// let a = this.userTypingMemoryDataSource.select(state => state).subscribe((e) => {
|
||||||
|
// this.userTyping$ = e as any
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
sendTyping() {}
|
sendTyping() {
|
||||||
|
this.userTypingServiceRepository.addUserTyping(this.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
async ChatMessageDebuggingPage() {
|
async ChatMessageDebuggingPage() {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user