Compare commits
37 Commits
master
...
android-se
Author | SHA1 | Date |
---|---|---|
Vitalik | 2fb7234bd6 | 4 months ago |
Vitalik | 23b75add79 | 4 months ago |
Vitalik | 05025f0f99 | 5 months ago |
Vitalik | 603b598cbf | 5 months ago |
Vitalik | a8b82da3c0 | 5 months ago |
Vitalik | 9584ece1ea | 5 months ago |
Vitalik | ef189cc013 | 5 months ago |
Vitalik | 5989573193 | 6 months ago |
Vitalik | e2db9778a3 | 6 months ago |
Vitalik | 8e18d18897 | 6 months ago |
Vitalik | e957cad230 | 6 months ago |
Vitalik | a9b445b1a3 | 6 months ago |
Vitalik | 3246c27785 | 6 months ago |
Vitalik | 349dc65b27 | 6 months ago |
Vitalik | 522bf71a2e | 6 months ago |
Vitalik | d454d62523 | 6 months ago |
Vitalik | f405216bd0 | 6 months ago |
Yevhen Romanenko | b27b8297ea | 6 months ago |
Vitalik | e2413b8cae | 6 months ago |
Yevhen Romanenko | 7decce1f3e | 6 months ago |
Vitalik | 82e9bd074d | 6 months ago |
Vitalik | abd739a331 | 6 months ago |
Yevhen Romanenko | 63d119d870 | 6 months ago |
Vitalik | 711ecc5c25 | 6 months ago |
Vitalik | b9265e9995 | 6 months ago |
Yevhen Romanenko | 4a9df62fde | 7 months ago |
Yevhen Romanenko | 0928bb06a3 | 7 months ago |
Vitalik | e82a9b1d14 | 7 months ago |
Vitalik | 23c32ac791 | 7 months ago |
Vitalik | 4858287b6c | 7 months ago |
Vitalik | 471b88e0bf | 7 months ago |
Vitalik | ccefe51229 | 7 months ago |
Vitalik | 8777c371eb | 7 months ago |
Vitalik | b86ec0f991 | 7 months ago |
Vitalik | 4aac8a6006 | 7 months ago |
Vitalik | 7cfdc600f3 | 7 months ago |
Vitalik | 2ce4f2006b | 7 months ago |
147 changed files with 17688 additions and 9418 deletions
@ -1,3 +1,7 @@
@@ -1,3 +1,7 @@
|
||||
API_URL=http://localhost:3000 |
||||
SOCKET_URL=http://localhost:3000 |
||||
ONE_SIGNAL_KEY=8b9066f5-8c3f-49f7-bef4-c5ab621f9d27 |
||||
ONE_SIGNAL_KEY=8b9066f5-8c3f-49f7-bef4-c5ab621f9d27 |
||||
|
||||
SENTRY_ENVIROMENT=develop |
||||
|
||||
SHOW_VERSION_MODAL=true |
@ -1,3 +1,7 @@
@@ -1,3 +1,7 @@
|
||||
API_URL=https://taskme-api.work-jetup.site |
||||
SOCKET_URL=https://taskme-api.work-jetup.site |
||||
ONE_SIGNAL_KEY=8b9066f5-8c3f-49f7-bef4-c5ab621f9d27 |
||||
|
||||
SENTRY_ENVIROMENT=stage |
||||
|
||||
SHOW_VERSION_MODAL=false |
Binary file not shown.
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
|
||||
auth.token=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 |
||||
|
||||
defaults.org=jetup-digital |
||||
defaults.project=application-taskme |
||||
|
||||
defaults.url=https://sentry.io/ |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
|
||||
auth.token=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 |
||||
|
||||
defaults.org=jetup-digital |
||||
defaults.project=application-taskme |
||||
|
||||
defaults.url=https://sentry.io/ |
File diff suppressed because one or more lines are too long
@ -1,11 +1,19 @@
@@ -1,11 +1,19 @@
|
||||
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config') |
||||
|
||||
const { |
||||
createSentryMetroSerializer |
||||
} = require("@sentry/react-native/dist/js/tools/sentryMetroSerializer"); |
||||
|
||||
/** |
||||
* Metro configuration |
||||
* https://facebook.github.io/metro/docs/configuration
|
||||
* |
||||
* @type {import('metro-config').MetroConfig} |
||||
*/ |
||||
const config = {} |
||||
const config = { |
||||
serializer: { |
||||
customSerializer: createSentryMetroSerializer() |
||||
} |
||||
} |
||||
|
||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config) |
||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config) |
Binary file not shown.
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import * as Sentry from '@sentry/react-native' |
||||
import Config from 'react-native-config' |
||||
|
||||
export const sentryOptions: Sentry.ReactNativeOptions = { |
||||
dsn: 'https://681ea9df3c323fbc1d54a9188491a626@o402114.ingest.us.sentry.io/4507231289540608', |
||||
environment: Config.SENTRY_ENVIROMENT, |
||||
tracesSampleRate: 1.0, |
||||
debug: true, |
||||
_experiments: { |
||||
profilesSampleRate: 1.0, |
||||
}, |
||||
} |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
import { |
||||
CallDataStore, |
||||
CallFromStore, |
||||
ICallStreamsStore, |
||||
useCallDataStore, |
||||
useCallFromStore, |
||||
useCallsStream, |
||||
} from '../../hooks' |
||||
|
||||
export abstract class CallEventHandler<T> { |
||||
protected get store(): CallDataStore { |
||||
return useCallDataStore.getState() |
||||
} |
||||
|
||||
protected get storeFrom(): CallFromStore { |
||||
return useCallFromStore.getState() |
||||
} |
||||
|
||||
protected get streamsStore(): ICallStreamsStore { |
||||
return useCallsStream.getState() |
||||
} |
||||
|
||||
public abstract handle(data: T): Promise<void> |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './event-handler' |
||||
export * from './negotiation.event-handler' |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
import { SocketEvents } from '@/shared' |
||||
import { CallEventHandler } from './event-handler' |
||||
|
||||
export class NegotiationEventHandler extends CallEventHandler< |
||||
SocketEvents['call/negotiation'] |
||||
> { |
||||
public async handle(data) { |
||||
console.log('data', data) |
||||
} |
||||
} |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
import { CallMod } from '../../hooks' |
||||
import { IceStatusHandler } from './ice-status-handler-abstract' |
||||
|
||||
export class ClosedIceStatusHandler extends IceStatusHandler { |
||||
public async handle() { |
||||
console.log('HANDLE CLOSED STATUS') |
||||
|
||||
// this.store.changeMod(CallMod.Finished)
|
||||
} |
||||
} |
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
import { callService } from '../call.service' |
||||
import { IceStatusHandler } from './ice-status-handler-abstract' |
||||
|
||||
/** |
||||
* Also use for disconected status, the same actions nedeed |
||||
*/ |
||||
export class FailedIceStatusHandler extends IceStatusHandler { |
||||
static database: { |
||||
reconnectCount: number |
||||
} = { |
||||
reconnectCount: 0, |
||||
} |
||||
|
||||
private database = FailedIceStatusHandler.database |
||||
private maxReconnectCount = 10 |
||||
|
||||
public async handle() { |
||||
console.log( |
||||
'HANDLE FAILED STATUS', |
||||
this.maxReconnectCount, |
||||
this.database, |
||||
) |
||||
// callService.finishCall()
|
||||
if (this.isReachedMaxReconnects()) { |
||||
this.clear() |
||||
this.reconnectAtemp() |
||||
} else { |
||||
this.increaseReconnectAttemps() |
||||
} |
||||
} |
||||
|
||||
private isReachedMaxReconnects() { |
||||
return this.database.reconnectCount >= this.maxReconnectCount |
||||
} |
||||
|
||||
private increaseReconnectAttemps() { |
||||
this.database.reconnectCount++ |
||||
} |
||||
|
||||
private clear() { |
||||
this.database.reconnectCount = 0 |
||||
} |
||||
private reconnectAtemp() { |
||||
this.store.peerConnection.restartIce() |
||||
} |
||||
|
||||
static clear() { |
||||
this.database.reconnectCount = 0 |
||||
} |
||||
} |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
import { |
||||
CallDataStore, |
||||
CallFromStore, |
||||
ICallStreamsStore, |
||||
useCallDataStore, |
||||
useCallFromStore, |
||||
useCallsStream, |
||||
} from '../../hooks' |
||||
|
||||
export abstract class IceStatusHandler { |
||||
protected get store(): CallDataStore { |
||||
return useCallDataStore.getState() |
||||
} |
||||
|
||||
protected get storeFrom(): CallFromStore { |
||||
return useCallFromStore.getState() |
||||
} |
||||
|
||||
protected get streamsStore(): ICallStreamsStore { |
||||
return useCallsStream.getState() |
||||
} |
||||
|
||||
abstract handle(): Promise<void> |
||||
} |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
import { CallDataStore } from '../../hooks' |
||||
import { ClosedIceStatusHandler } from './closed-ice-status.handle' |
||||
import { FailedIceStatusHandler } from './failed-ice-status.handle' |
||||
import { IceStatusHandler } from './ice-status-handler-abstract' |
||||
|
||||
export class IceStatusHandlerProxy { |
||||
protected iceStatus: RTCIceConnectionState |
||||
|
||||
constructor(protected readonly store: CallDataStore) {} |
||||
|
||||
protected handlers: Partial< |
||||
Record<RTCIceConnectionState, { new (): IceStatusHandler }> |
||||
> = { |
||||
failed: FailedIceStatusHandler, |
||||
disconnected: FailedIceStatusHandler, |
||||
closed: ClosedIceStatusHandler, |
||||
} |
||||
|
||||
public setStatus(iceStatus: RTCIceConnectionState) { |
||||
this.iceStatus = iceStatus |
||||
this.store.setConnectedStatus(iceStatus) |
||||
console.log('ICE STATUS', iceStatus) |
||||
return this |
||||
} |
||||
|
||||
public handle() { |
||||
const Handler = this.handlers[this.iceStatus] |
||||
|
||||
if (Handler) { |
||||
new Handler().handle() |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
import { |
||||
isNegotiationPossibleReq, |
||||
sendNegotiationReq, |
||||
} from '@/api/calls/requests' |
||||
import { CallRoot } from './call-root.service' |
||||
import { callService } from './call.service' |
||||
|
||||
class NegotiationService extends CallRoot { |
||||
public async startNegotiation() { |
||||
/** |
||||
* We don't need negotiation before call initialization |
||||
*/ |
||||
if (this.store.callStartAt < 1) return |
||||
|
||||
const peerConnection = this.store.peerConnection |
||||
|
||||
const isPossible = await this.checkIsNegotiationPossible() |
||||
|
||||
if (!isPossible) { |
||||
callService.finishCall() |
||||
|
||||
return |
||||
} |
||||
|
||||
console.log('Signaling state', peerConnection.signalingState) |
||||
|
||||
if (peerConnection.signalingState !== 'stable') return |
||||
|
||||
const offer = await peerConnection.createOffer({}) |
||||
await peerConnection.setLocalDescription(offer) |
||||
|
||||
sendNegotiationReq(this.store.callId, { |
||||
type: 'offer', |
||||
description: offer, |
||||
}) |
||||
// } else {
|
||||
// const answer = await peerConnection.createAnswer()
|
||||
// await peerConnection.setLocalDescription(answer)
|
||||
|
||||
// sendNegotiationReq(this.store.callId, {
|
||||
// type: 'answer',
|
||||
// description: answer,
|
||||
// })
|
||||
// }
|
||||
} |
||||
|
||||
/** |
||||
* Send request to server to check that user is still online |
||||
*/ |
||||
public async checkIsNegotiationPossible(): Promise<boolean> { |
||||
try { |
||||
const { data } = await isNegotiationPossibleReq(this.store.callId) |
||||
return data |
||||
} catch (e) { |
||||
return false |
||||
} |
||||
} |
||||
} |
||||
|
||||
export const negotiationService = new NegotiationService() |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
import { COPY_ENABLED_MESSAGES_TYPES } from '@/modules/chats/consts' |
||||
import { netConnectStateCtr } from '@/modules/root/states' |
||||
import { MessageType } from '@/shared' |
||||
import { IChatMessage } from '@/shared/components/plugins/chat' |
||||
import { _ } from 'jet-tools' |
||||
import { ChatUserRole } from '../chat-user-role' |
||||
|
||||
export class ChatMessageActionChecker { |
||||
constructor(private readonly messages: IChatMessage[]) {} |
||||
|
||||
private get isConnected() { |
||||
return netConnectStateCtr().isConnected |
||||
} |
||||
|
||||
public isAllOneType(type: MessageType) { |
||||
return _.every(this.messages, it => it.type === type) |
||||
} |
||||
|
||||
public isOneMessage() { |
||||
return this.messages.length === 1 |
||||
} |
||||
|
||||
public canShare() { |
||||
return ( |
||||
this.isConnected && |
||||
this.isOneMessage() && |
||||
this.messages[0].type !== MessageType.Sticker |
||||
) |
||||
} |
||||
|
||||
public canCopy() { |
||||
if (!this.isConnected) return this.isAllOneType(MessageType.Text) |
||||
|
||||
if ( |
||||
this.isOneMessage() && |
||||
COPY_ENABLED_MESSAGES_TYPES.includes(this.messages[0].type) |
||||
) |
||||
return true |
||||
|
||||
if (this.messages.length > 1 && this.isAllOneType(MessageType.Text)) |
||||
return true |
||||
|
||||
return false |
||||
} |
||||
|
||||
public canDeleteForAll() { |
||||
if (ChatUserRole.fromAccount().isAdmin()) { |
||||
return true |
||||
} |
||||
|
||||
return _.every(this.messages, it => { |
||||
if (!it.read && it.isMy) return true |
||||
|
||||
return false |
||||
}) |
||||
} |
||||
|
||||
public isSomeOfflineMessages() { |
||||
return _.some(this.messages, it => _.isNaN(Number(it.id))) |
||||
} |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './chat-message-action-checker' |
@ -0,0 +1,81 @@
@@ -0,0 +1,81 @@
|
||||
import { ChatType, IChatDetails, createFullName } from '@/shared' |
||||
import { _ } from 'jet-tools' |
||||
|
||||
export class ChatHeader { |
||||
constructor( |
||||
private readonly chat: IChatDetails, |
||||
private readonly accountId: number, |
||||
) {} |
||||
|
||||
public getPreparedData() { |
||||
return { |
||||
...this.getInfo(), |
||||
label: this.getLabel(), |
||||
} |
||||
} |
||||
|
||||
private getInfo() { |
||||
if (this.chat.type === ChatType.Personal) return this.getPersonalInfo() |
||||
return this.getGroupInfo() |
||||
} |
||||
|
||||
private getPersonalInfo() { |
||||
const member = _.find( |
||||
this.chat.chatMembers, |
||||
member => member.userId !== this.accountId, |
||||
) |
||||
|
||||
return { |
||||
name: createFullName( |
||||
member?.user?.firstName, |
||||
member?.user?.lastName, |
||||
), |
||||
previewUrl: member?.user?.avatarUrl, |
||||
type: this.chat.type, |
||||
} |
||||
} |
||||
|
||||
private getGroupInfo() { |
||||
return { |
||||
name: this.chat.name, |
||||
previewUrl: this.chat.previewUrl, |
||||
type: this.chat.type, |
||||
} |
||||
} |
||||
|
||||
private getLabel() { |
||||
switch (this.chat.type) { |
||||
case ChatType.Group: |
||||
return this.getGroupLabel() |
||||
case ChatType.Personal: |
||||
return this.getPersonalLabel() |
||||
default: |
||||
return '' |
||||
} |
||||
} |
||||
|
||||
private getPersonalLabel() { |
||||
const member = _.find( |
||||
this.chat.chatMembers, |
||||
member => member.userId !== this.accountId, |
||||
) |
||||
if (member?.isOnline) return 'Зараз в мережі' |
||||
|
||||
return 'Не в мережі' |
||||
} |
||||
|
||||
private getGroupLabel() { |
||||
const membersCount = _.filter( |
||||
this.chat.chatMembers, |
||||
member => !member.isDeleted, |
||||
).length |
||||
|
||||
const onlineCount = this.chat.chatMembers?.reduce( |
||||
(count, it) => count + (it.isOnline && !it.isDeleted ? 1 : 0), |
||||
0, |
||||
) |
||||
return `${membersCount} учасників${ |
||||
onlineCount ? `, ${onlineCount} онлайн` : '' |
||||
}` |
||||
} |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
import { chatManager } from '@/managers' |
||||
import { chatStateCtr } from '../states' |
||||
import { RouteKey, appEvents } from '@/shared' |
||||
|
||||
class ChatNavigator { |
||||
constructor(private navigate?: any) {} |
||||
|
||||
public setNavigate(navigate: any) { |
||||
this.navigate = navigate |
||||
} |
||||
|
||||
public navigateToChat(chatId: any, unreadMessagesCount?: number) { |
||||
chatStateCtr().loadChat(chatId) |
||||
|
||||
this.navigate(RouteKey.Conversation) |
||||
|
||||
if (unreadMessagesCount) { |
||||
appEvents.emit('onReadChat', { |
||||
chatId: chatId, |
||||
unreadCount: unreadMessagesCount, |
||||
}) |
||||
} |
||||
|
||||
chatManager.readChat(chatId) |
||||
} |
||||
} |
||||
|
||||
export const chatNavigator = new ChatNavigator() |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
import { ChatMemberRole, ChatType, IChatDetails } from '@/shared' |
||||
import store from '@/store' |
||||
import { selectId } from '@/store/account' |
||||
import { chatStateCtr } from '../states' |
||||
import { _ } from 'jet-tools' |
||||
|
||||
export class ChatUserRole { |
||||
constructor( |
||||
private readonly chat: IChatDetails, |
||||
private readonly userId: number, |
||||
) {} |
||||
|
||||
public get role(): ChatMemberRole { |
||||
const member = _.find( |
||||
this.chat?.chatMembers, |
||||
member => member.userId === this.userId, |
||||
) |
||||
const role = |
||||
member && this.chat?.type === ChatType.Group |
||||
? member.role |
||||
: ChatMemberRole.Member |
||||
|
||||
return role |
||||
} |
||||
|
||||
public isAdmin() { |
||||
return this.role === ChatMemberRole.Admin |
||||
} |
||||
|
||||
public isMembed() { |
||||
return this.role === ChatMemberRole.Member |
||||
} |
||||
|
||||
static fromAccount() { |
||||
const accountId = selectId(store.getState()) |
||||
return new ChatUserRole(chatStateCtr().chat, accountId) |
||||
} |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './chat-header' |
||||
export * from './chat-user-role' |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './use-chat-details.hook' |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
import { useChatState } from '../states' |
||||
|
||||
export const useChatLoader = () => { |
||||
const state = useChatState() |
||||
|
||||
async function loadChat(chatId: number, force = false) { |
||||
if (state.chatId && !force) { |
||||
if (state.chatId === chatId) return |
||||
} |
||||
|
||||
await state.loadChat(chatId) |
||||
} |
||||
|
||||
return { |
||||
chatId: state.chatId, |
||||
load: loadChat, |
||||
} |
||||
} |
||||
|
||||
export const useChatDetailsv2 = () => { |
||||
const isLoading = useChatState(s => s.isLoading) |
||||
const chat = useChatState(s => s.chat) |
||||
|
||||
return { |
||||
isLoading, |
||||
chat, |
||||
chatId: chat?.id, |
||||
} |
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
import { ChatType, IChatDetails } from '@/shared' |
||||
import { IHeaderChatInfo } from '../../interfaces' |
||||
import { create } from 'zustand' |
||||
import { ChatHeader } from '../core' |
||||
import { selectId } from '@/store/account' |
||||
import store from '@/store' |
||||
|
||||
const headerChatInfoInitialState: IHeaderChatInfo = { |
||||
name: ' ', |
||||
previewUrl: null, |
||||
type: ChatType.Group, |
||||
label: ' ', |
||||
} |
||||
|
||||
interface IChatHeaderState { |
||||
data: IHeaderChatInfo |
||||
|
||||
set(chat: IChatDetails): void |
||||
} |
||||
|
||||
export const chatHeaderState = create<IChatHeaderState>()(set => ({ |
||||
data: headerChatInfoInitialState, |
||||
set(chat) { |
||||
const data = new ChatHeader( |
||||
chat, |
||||
selectId(store.getState()), |
||||
).getPreparedData() |
||||
|
||||
set({ data }) |
||||
}, |
||||
})) |
||||
|
||||
export const chatHeaderStateCtr = () => chatHeaderState.getState() |
||||
export const useChatHeaderState = chatHeaderState |
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
import { IChatMessage } from '@/shared/components/plugins/chat' |
||||
import { _ } from 'jet-tools' |
||||
import { create } from 'zustand' |
||||
|
||||
interface IChatSelectedMessagesState { |
||||
messages: IChatMessage[] |
||||
selectMessage: (message: IChatMessage) => void |
||||
unselectAll: () => void |
||||
} |
||||
|
||||
export const chatSelectedMessagesState = create<IChatSelectedMessagesState>()( |
||||
set => ({ |
||||
messages: [], |
||||
selectMessage(message: IChatMessage) { |
||||
set(state => { |
||||
if (_.find(state.messages, it => it.id === message.id)) |
||||
return { |
||||
messages: state.messages.filter( |
||||
it => it.id !== message.id, |
||||
), |
||||
} |
||||
else |
||||
return { |
||||
messages: [ |
||||
...state.messages, |
||||
_.pick(message, [ |
||||
'id', |
||||
'type', |
||||
'content', |
||||
'authorId', |
||||
'chatId', |
||||
'read', |
||||
'isMy', |
||||
'author', |
||||
'isPined', |
||||
'createdAt', |
||||
]), |
||||
], |
||||
} |
||||
}) |
||||
}, |
||||
unselectAll() { |
||||
set({ messages: [] }) |
||||
}, |
||||
}), |
||||
) |
||||
|
||||
export const chatSelectedMessagesStateCtr = () => |
||||
chatSelectedMessagesState.getState() |
||||
export const useChatSelectedMessagesState = chatSelectedMessagesState |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
import { chatManager } from '@/managers' |
||||
import { IChatDetails } from '@/shared' |
||||
import { create } from 'zustand' |
||||
import { chatHeaderStateCtr } from './chat-header.state' |
||||
|
||||
interface IChatState { |
||||
chatId: number |
||||
chat: IChatDetails |
||||
isLoading: boolean |
||||
|
||||
setChat: (chat: IChatDetails) => void |
||||
loadChat: (chatId: number) => Promise<void> |
||||
clear: () => void |
||||
} |
||||
|
||||
export const chatState = create<IChatState>()(set => ({ |
||||
chatId: null, |
||||
isLoading: false, |
||||
chat: null, |
||||
|
||||
setChat(chat) { |
||||
set({ chat, isLoading: false }) |
||||
}, |
||||
|
||||
async loadChat(chatId) { |
||||
set({ isLoading: true, chat: null, chatId: chatId }) |
||||
|
||||
const chatDetails = await chatManager.getChatDetail.bind(chatManager)({ |
||||
id: chatId, |
||||
}) |
||||
|
||||
chatHeaderStateCtr().set(chatDetails) |
||||
|
||||
set({ chat: chatDetails, isLoading: false }) |
||||
}, |
||||
|
||||
clear() { |
||||
set(chatState.getInitialState()) |
||||
}, |
||||
})) |
||||
|
||||
export const chatStateCtr = () => chatState.getState() |
||||
export const useChatState = chatState |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export * from './chat-header.state' |
||||
export * from './chat-selected-messages.state' |
||||
export * from './chat.state' |
||||
export * from './use-chat-view-mode.state' |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
import { create } from 'zustand' |
||||
import { ChatViewModeEnum } from '../../enums' |
||||
|
||||
interface IChatViewModeState { |
||||
mode: ChatViewModeEnum |
||||
setMode: (mode: ChatViewModeEnum) => void |
||||
} |
||||
|
||||
export const chatViewModeState = create<IChatViewModeState>()(set => ({ |
||||
mode: ChatViewModeEnum.DEFAULT, |
||||
setMode: (mode: ChatViewModeEnum) => { |
||||
set({ mode }) |
||||
}, |
||||
})) |
||||
|
||||
export const useChatViewModeState = chatViewModeState |
||||
export const chatViewModeStateCtr = () => chatViewModeState.getState() |
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
import { createFullName, IChatDetails } from '@/shared' |
||||
import _ from 'lodash' |
||||
import { IHeaderChatInfo } from '../interfaces' |
||||
|
||||
export const getGroupChatInfo = ( |
||||
chatDetail: IChatDetails, |
||||
): Omit<IHeaderChatInfo, 'label'> => ({ |
||||
name: chatDetail.name, |
||||
previewUrl: chatDetail.previewUrl, |
||||
type: chatDetail.type, |
||||
}) |
||||
|
||||
export const getPersonalChatInfo = ( |
||||
chatDetail: IChatDetails, |
||||
accountId: number, |
||||
): Omit<IHeaderChatInfo, 'label'> => { |
||||
const member = _.find( |
||||
chatDetail.chatMembers, |
||||
member => member.userId !== accountId, |
||||
) |
||||
|
||||
return { |
||||
name: createFullName(member?.user?.firstName, member?.user?.lastName), |
||||
previewUrl: member?.user?.avatarUrl, |
||||
type: chatDetail.type, |
||||
} |
||||
} |
@ -1,16 +0,0 @@
@@ -1,16 +0,0 @@
|
||||
import { IChatMember } from "@/shared" |
||||
import _ from "lodash" |
||||
|
||||
export const getHeaderGroupChatInfo = (membersCount: number, onlineCount: number) => { |
||||
return `${membersCount} учасників${onlineCount ? `, ${onlineCount} онлайн` : ''}` |
||||
} |
||||
|
||||
export const getHeaderPersonalChatInfo = (members: IChatMember[], accountId: number) => { |
||||
const member = _.find( |
||||
members, |
||||
member => member.userId !== accountId, |
||||
) |
||||
if (member?.isOnline) return 'Зараз в мережі' |
||||
|
||||
return 'Не в мережі' |
||||
} |
@ -1,6 +1,4 @@
@@ -1,6 +1,4 @@
|
||||
export * from './chat-messages.helper' |
||||
export * from './chats-helpers.helper' |
||||
export * from './get-chat-info.helper' |
||||
export * from './get-header-chat-info.helper' |
||||
export * from './get-time-from-message-send.helper' |
||||
export * from './get-copied-messages-content.helper' |
||||
export * from './get-time-from-message-send.helper' |
||||
|
@ -1,14 +0,0 @@
@@ -1,14 +0,0 @@
|
||||
import { create } from 'zustand' |
||||
import { ChatViewModeEnum } from '../enums' |
||||
|
||||
interface IChatViewModeState { |
||||
mode: ChatViewModeEnum |
||||
setMode: (mode: ChatViewModeEnum) => void |
||||
} |
||||
|
||||
export const useChatViewModeState = create<IChatViewModeState>()(set => ({ |
||||
mode: ChatViewModeEnum.DEFAULT, |
||||
setMode: (mode: ChatViewModeEnum) => { |
||||
set({ mode }) |
||||
}, |
||||
})) |
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
import { fsService } from '@/services/system' |
||||
import { |
||||
Button, |
||||
FileType, |
||||
IconComponent, |
||||
ScreenLayout, |
||||
WebviewPlugin, |
||||
appEvents, |
||||
} from '@/shared' |
||||
import { useNavigation, useRoute } from '@react-navigation/native' |
||||
import React, { useMemo } from 'react' |
||||
import { Platform, StyleSheet, TouchableOpacity, View } from 'react-native' |
||||
import PDFView from 'react-native-view-pdf' |
||||
|
||||
export const ChatFilePreviewScreen = () => { |
||||
const navigation = useNavigation() |
||||
const route = useRoute() |
||||
const params: any = route.params |
||||
|
||||
const stopLoading = () => { |
||||
appEvents.emit('closeLoaderModal', {}) |
||||
} |
||||
|
||||
const openByApp = () => { |
||||
try { |
||||
fsService.previewFile(params.fileUrl) |
||||
} catch (e) { |
||||
console.log('e', e) |
||||
} |
||||
} |
||||
|
||||
const renderItem = useMemo(() => { |
||||
switch (params?.mimeType) { |
||||
case FileType.PDF: |
||||
return ( |
||||
<PDFView |
||||
fadeInDuration={250.0} |
||||
style={{ flex: 1 }} |
||||
resource={params?.fileUrl} |
||||
resourceType={'url'} |
||||
onLoad={stopLoading} |
||||
/> |
||||
) |
||||
case FileType.TXT: |
||||
return ( |
||||
<WebviewPlugin |
||||
url={params?.fileUrl} |
||||
renderHtml |
||||
onLoaded={stopLoading} |
||||
/> |
||||
) |
||||
default: |
||||
return ( |
||||
<WebviewPlugin |
||||
url={`https://docs.google.com/gview?embedded=true&url=${encodeURIComponent( |
||||
params?.fileUrl, |
||||
)}`}
|
||||
onLoaded={stopLoading} |
||||
/> |
||||
) |
||||
} |
||||
}, [params?.fileUrl]) |
||||
|
||||
return ( |
||||
<ScreenLayout horizontalPadding={0}> |
||||
<TouchableOpacity |
||||
onPress={navigation.goBack} |
||||
style={styles.closeBtn}> |
||||
<IconComponent name="xcircle-1" size={32} color="#000" /> |
||||
</TouchableOpacity> |
||||
|
||||
{renderItem} |
||||
{Platform.OS === 'android' ? ( |
||||
<View style={{ padding: 14 }}> |
||||
<Button onPress={openByApp} title="Відкрити в додатку" /> |
||||
</View> |
||||
) : null} |
||||
</ScreenLayout> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
closeBtn: { |
||||
position: 'absolute', |
||||
top: 20, |
||||
left: 5, |
||||
zIndex: 99999, |
||||
backgroundColor: 'rgba(0,0,0,.1)', |
||||
}, |
||||
}) |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './net-connect.state' |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue