403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/cvetdv.ru/bitrix/js/im/v2/model/src/recent/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/js/im/v2/model/src/recent/recent.js
import { Type, type JsonObject } from 'main.core';
import { BuilderModel } from 'ui.vue3.vuex';

import { Core } from 'im.v2.application.core';
import { ChatType, FakeDraftMessagePrefix, Settings } from 'im.v2.const';
import { Utils } from 'im.v2.lib.utils';
import { ChannelManager } from 'im.v2.lib.channel';
import { formatFieldsWithConfig, convertObjectKeysToCamelCase } from 'im.v2.model';

import { recentFieldsConfig } from './format/field-config';
import { CallsModel } from './nested-modules/calls';

import type { Store, GetterTree, ActionTree, MutationTree } from 'ui.vue3.vuex';
import type { ImModelMessage, ImModelChat } from 'im.v2.model';

import type { RecentItem as ImModelRecentItem } from '../type/recent-item';

type RecentStore = Store<RecentState>;

type RecentState = {
	collection: {[dialogId: string]: ImModelRecentItem},
	recentCollection: Set<string>,
	unreadCollection: Set<string>,
	copilotCollection: Set<string>,
	channelCollection: Set<string>,
	collabCollection: Set<string>,
};

type SetDraftPayload = {
	id: string | number,
	text: string,
};

export class RecentModel extends BuilderModel
{
	getName(): string
	{
		return 'recent';
	}

	getNestedModules(): { [moduleName: string]: BuilderModel }
	{
		return {
			calls: CallsModel,
		};
	}

	getState(): RecentState
	{
		return {
			collection: {},
			recentCollection: new Set(),
			unreadCollection: new Set(),
			copilotCollection: new Set(),
			channelCollection: new Set(),
			collabCollection: new Set(),
		};
	}

	getElementState(): ImModelRecentItem
	{
		return {
			dialogId: '0',
			messageId: 0,
			draft: {
				text: '',
				date: null,
			},
			unread: false,
			pinned: false,
			liked: false,
			invitation: {
				isActive: false,
				originator: 0,
				canResend: false,
			},
			isFakeElement: false,
			isBirthdayPlaceholder: false,
			lastActivityDate: null,
		};
	}

	// eslint-disable-next-line max-lines-per-function
	getGetters(): GetterTree
	{
		return {
			/** @function recent/getRecentCollection */
			getRecentCollection: (state: RecentState): ImModelRecentItem[] => {
				return [...state.recentCollection].filter((dialogId) => {
					const dialog = this.store.getters['chats/get'](dialogId);

					return Boolean(dialog);
				}).map((id) => {
					return state.collection[id];
				});
			},
			/** @function recent/getUnreadCollection */
			getUnreadCollection: (state: RecentState): ImModelRecentItem[] => {
				return [...state.unreadCollection].map((id) => {
					return state.collection[id];
				});
			},
			/** @function recent/getCopilotCollection */
			getCopilotCollection: (state: RecentState): ImModelRecentItem[] => {
				return [...state.copilotCollection].filter((dialogId) => {
					const dialog = this.store.getters['chats/get'](dialogId);

					return Boolean(dialog);
				}).map((id) => {
					return state.collection[id];
				});
			},
			/** @function recent/getChannelCollection */
			getChannelCollection: (state: RecentState): ImModelRecentItem[] => {
				return [...state.channelCollection].filter((dialogId) => {
					const dialog = this.store.getters['chats/get'](dialogId);

					return Boolean(dialog);
				}).map((id) => {
					return state.collection[id];
				});
			},
			/** @function recent/getCollabCollection */
			getCollabCollection: (state: RecentState): ImModelRecentItem[] => {
				return [...state.collabCollection].filter((dialogId) => {
					const dialog = this.store.getters['chats/get'](dialogId);

					return Boolean(dialog);
				}).map((id) => {
					return state.collection[id];
				});
			},
			/** @function recent/getSortedCollection */
			getSortedCollection: (state: RecentState): ImModelRecentItem[] => {
				const recentCollectionAsArray = [...state.recentCollection].map((dialogId) => {
					return state.collection[dialogId];
				});

				return recentCollectionAsArray.sort((a, b) => {
					const messageA: ImModelMessage = this.#getMessage(a.messageId);
					const messageB: ImModelMessage = this.#getMessage(b.messageId);

					return messageB.date - messageA.date;
				});
			},
			/** @function recent/get */
			get: (state: RecentState) => (dialogId: string): ImModelRecentItem | null => {
				if (!state.collection[dialogId])
				{
					return null;
				}

				return state.collection[dialogId];
			},
			/** @function recent/getMessage */
			getMessage: (state: RecentState) => (dialogId: string): ImModelMessage | null => {
				const element = state.collection[dialogId];
				if (!element)
				{
					return null;
				}

				return this.#getMessage(element.messageId);
			},
			/** @function recent/needsBirthdayPlaceholder */
			needsBirthdayPlaceholder: (state: RecentState) => (dialogId): boolean => {
				const currentItem = state.collection[dialogId];
				if (!currentItem)
				{
					return false;
				}

				const dialog = this.store.getters['chats/get'](dialogId);
				if (!dialog || dialog.type !== ChatType.user)
				{
					return false;
				}
				const hasBirthday = this.store.getters['users/hasBirthday'](dialogId);
				if (!hasBirthday)
				{
					return false;
				}

				const isSelfChat = Number.parseInt(dialogId, 10) === Core.getUserId();
				if (isSelfChat)
				{
					return false;
				}

				const showBirthday = this.store.getters['application/settings/get'](Settings.recent.showBirthday);
				const hasTodayMessage = this.#hasTodayMessage(currentItem.messageId);

				return showBirthday && !hasTodayMessage && dialog.counter === 0;
			},
			/** @function recent/needsVacationPlaceholder */
			needsVacationPlaceholder: (state: RecentState) => (dialogId): boolean => {
				const currentItem = state.collection[dialogId];
				if (!currentItem)
				{
					return false;
				}

				const isNotes = Number.parseInt(dialogId, 10) === Core.getUserId();
				if (isNotes)
				{
					return false;
				}

				const dialog = this.store.getters['chats/get'](dialogId);
				if (!dialog || dialog.type !== ChatType.user)
				{
					return false;
				}

				const hasVacation = this.store.getters['users/hasVacation'](dialogId);
				if (!hasVacation)
				{
					return false;
				}

				const hasTodayMessage = this.#hasTodayMessage(currentItem.messageId);

				return !hasTodayMessage && dialog.counter === 0;
			},
			/** @function recent/getSortDate */
			getSortDate: (state: RecentState) => (dialogId): Date | null => {
				const currentItem = state.collection[dialogId];
				if (!currentItem)
				{
					return null;
				}

				const message: ImModelMessage = this.#getMessage(currentItem.messageId);

				if (Type.isDate(currentItem.draft.date) && currentItem.draft.date > message.date)
				{
					return currentItem.draft.date;
				}

				const needsBirthdayPlaceholder = this.store.getters['recent/needsBirthdayPlaceholder'](currentItem.dialogId);
				if (needsBirthdayPlaceholder)
				{
					return Utils.date.getStartOfTheDay();
				}

				const lastActivity = currentItem.lastActivityDate;
				const needToUseActivityDate = Type.isDate(lastActivity) && lastActivity > message.date;
				if (ChannelManager.isChannel(currentItem.dialogId) && needToUseActivityDate)
				{
					return lastActivity;
				}

				return message.date;
			},
		};
	}

	/* eslint-disable no-param-reassign */
	/* eslint-disable-next-line max-lines-per-function */
	getActions(): ActionTree
	{
		return {
			/** @function recent/setRecent */
			setRecent: async (store: RecentStore, payload: Array | Object) => {
				const itemIds = await Core.getStore().dispatch('recent/store', payload);

				store.commit('setRecentCollection', itemIds);

				this.#updateUnloadedRecentCounters(payload);
			},
			/** @function recent/setUnread */
			setUnread: async (store: RecentStore, payload: Array | Object) => {
				const itemIds = await this.store.dispatch('recent/store', payload);
				store.commit('setUnreadCollection', itemIds);
			},
			/** @function recent/setCopilot */
			setCopilot: async (store: RecentStore, payload: Array | Object) => {
				const itemIds = await this.store.dispatch('recent/store', payload);
				store.commit('setCopilotCollection', itemIds);

				this.#updateUnloadedCopilotCounters(payload);
			},
			/** @function recent/setChannel */
			setChannel: async (store: RecentStore, payload: Array | Object) => {
				const itemIds = await this.store.dispatch('recent/store', payload);
				store.commit('setChannelCollection', itemIds);
			},
			/** @function recent/setCollab */
			setCollab: async (store: RecentStore, payload: Array | Object) => {
				const itemIds = await this.store.dispatch('recent/store', payload);
				store.commit('setCollabCollection', itemIds);

				this.#updateUnloadedCollabCounters(payload);
			},
			/** @function recent/clearChannelCollection */
			clearChannelCollection: (store: RecentStore) => {
				store.commit('clearChannelCollection');
			},
			/** @function recent/store */
			store: (store: RecentStore, payload: Array | Object) => {
				if (!Array.isArray(payload) && Type.isPlainObject(payload))
				{
					payload = [payload];
				}

				const itemsToUpdate = [];
				const itemsToAdd = [];
				payload.map((element) => {
					return this.#formatFields(element);
				}).forEach((element) => {
					const preparedElement = { ...element };
					const existingItem = store.state.collection[element.dialogId];
					if (existingItem)
					{
						itemsToUpdate.push({ dialogId: existingItem.dialogId, fields: preparedElement });
					}
					else
					{
						itemsToAdd.push({ ...this.getElementState(), ...preparedElement });
					}
				});

				if (itemsToAdd.length > 0)
				{
					store.commit('add', itemsToAdd);
				}

				if (itemsToUpdate.length > 0)
				{
					store.commit('update', itemsToUpdate);
				}

				return [...itemsToAdd, ...itemsToUpdate].map((item) => item.dialogId);
			},
			/** @function recent/update */
			update: (store: RecentStore, payload: { id: string | number, fields: Object }) => {
				const { id, fields } = payload;
				const existingItem: ImModelRecentItem = store.state.collection[id];
				if (!existingItem)
				{
					return;
				}

				store.commit('update', {
					dialogId: existingItem.dialogId,
					fields: this.#formatFields(fields),
				});
			},
			/** @function recent/unread */
			unread: (store: RecentStore, payload: { id: string | number, action: boolean }) => {
				const existingItem = store.state.collection[payload.id];
				if (!existingItem)
				{
					return;
				}

				store.commit('update', {
					dialogId: existingItem.dialogId,
					fields: { unread: payload.action },
				});
			},
			/** @function recent/pin */
			pin: (store: RecentStore, payload: { id: string | number, action: boolean }) => {
				const existingItem = store.state.collection[payload.id];
				if (!existingItem)
				{
					return;
				}

				store.commit('update', {
					dialogId: existingItem.dialogId,
					fields: { pinned: payload.action },
				});
			},
			/** @function recent/like */
			like: (store: RecentStore, payload: { id: string | number, messageId: number, liked: boolean }) => {
				const existingItem: ImModelRecentItem = store.state.collection[payload.id];
				if (!existingItem)
				{
					return;
				}

				const isLastMessage = existingItem.messageId === Number.parseInt(payload.messageId, 10);
				const isExactMessageLiked = !Type.isUndefined(payload.messageId) && payload.liked === true;
				if (isExactMessageLiked && !isLastMessage)
				{
					return;
				}

				store.commit('update', {
					dialogId: existingItem.dialogId,
					fields: { liked: payload.liked === true },
				});
			},
			/** @function recent/setDraft */
			setDraft: (store: RecentStore, payload: SetDraftPayload) => {
				const isRemovingDraft = !Type.isStringFilled(payload.text);
				if (isRemovingDraft && this.#shouldDeleteItemWithDraft(payload))
				{
					void Core.getStore().dispatch('recent/delete', { id: payload.id });

					return;
				}

				const existingCollectionItem = store.state.recentCollection.has(payload.id);
				const needsFakeItem = !existingCollectionItem && !isRemovingDraft;
				if (needsFakeItem)
				{
					this.#handleFakeItemWithDraft(payload, store);
				}

				const existingItem = store.state.collection[payload.id];
				if (!existingItem)
				{
					return;
				}

				void Core.getStore().dispatch('recent/update', {
					id: payload.id,
					fields: {
						draft: { text: payload.text.toString() },
					},
				});
			},
			/** @function recent/delete */
			delete: (store: RecentStore, payload: { id: string | number }) => {
				const existingItem = store.state.collection[payload.id];
				if (!existingItem)
				{
					return;
				}

				store.commit('deleteFromRecentCollection', existingItem.dialogId);
				store.commit('deleteFromCopilotCollection', existingItem.dialogId);
				store.commit('deleteFromChannelCollection', existingItem.dialogId);
				store.commit('deleteFromCollabCollection', existingItem.dialogId);
				const canDelete = this.#canDelete(existingItem.dialogId);

				if (!canDelete)
				{
					return;
				}

				store.commit('delete', {
					id: existingItem.dialogId,
				});
			},
			/** @function recent/clearUnread */
			clearUnread: (store: RecentStore) => {
				store.commit('clearUnread');
			},
		};
	}

	getMutations(): MutationTree
	{
		return {
			setRecentCollection: (state: RecentState, payload: string[]) => {
				payload.forEach((dialogId) => {
					state.recentCollection.add(dialogId);
				});
			},
			deleteFromRecentCollection: (state: RecentState, payload: string) => {
				state.recentCollection.delete(payload);
			},
			setUnreadCollection: (state: RecentState, payload: string[]) => {
				payload.forEach((dialogId) => {
					state.unreadCollection.add(dialogId);
				});
			},
			setCopilotCollection: (state: RecentState, payload: string[]) => {
				payload.forEach((dialogId) => {
					state.copilotCollection.add(dialogId);
				});
			},
			deleteFromCopilotCollection: (state: RecentState, payload: string) => {
				state.copilotCollection.delete(payload);
			},
			deleteFromChannelCollection: (state: RecentState, payload: string) => {
				state.channelCollection.delete(payload);
			},
			setChannelCollection: (state: RecentState, payload: string[]) => {
				payload.forEach((dialogId) => {
					state.channelCollection.add(dialogId);
				});
			},
			clearChannelCollection: (state: RecentState) => {
				state.channelCollection = new Set();
			},
			setCollabCollection: (state: RecentState, payload: string[]) => {
				payload.forEach((dialogId) => {
					state.collabCollection.add(dialogId);
				});
			},
			deleteFromCollabCollection: (state: RecentState, payload: string) => {
				state.collabCollection.delete(payload);
			},
			add: (state: RecentState, payload: Object[] | Object) => {
				if (!Array.isArray(payload) && Type.isPlainObject(payload))
				{
					payload = [payload];
				}
				payload.forEach((item) => {
					state.collection[item.dialogId] = item;
				});
			},

			update: (state: RecentState, payload: Object[] | Object) => {
				if (!Array.isArray(payload) && Type.isPlainObject(payload))
				{
					payload = [payload];
				}
				payload.forEach(({ dialogId, fields }) => {
					// if we already got chat - we should not update it with fake user chat
					// (unless it's an accepted invitation or fake user with real message)
					const elementIsInRecent = state.recentCollection.has(dialogId);
					const isFakeElement = fields.isFakeElement && Utils.text.isTempMessage(fields.messageId);
					if (elementIsInRecent && isFakeElement && !fields.invitation)
					{
						return;
					}

					const currentElement = state.collection[dialogId];
					state.collection[dialogId] = { ...currentElement, ...fields };
				});
			},

			delete: (state: RecentState, payload: {id: string}) => {
				delete state.collection[payload.id];
			},

			clearUnread: (state: RecentState) => {
				Object.keys(state.collection).forEach((key) => {
					state.collection[key].unread = false;
				});
			},
		};
	}

	#formatFields(rawFields: JsonObject): Partial<ImModelRecentItem>
	{
		const options = Type.isPlainObject(rawFields.options) ? rawFields.options : {};
		const fields = { ...rawFields, ...options };

		return formatFieldsWithConfig(fields, recentFieldsConfig);
	}

	#updateUnloadedRecentCounters(payload: Array | Object)
	{
		this.#updateUnloadedCounters(payload, 'counters/setUnloadedChatCounters');
	}

	#updateUnloadedCopilotCounters(payload: Array | Object)
	{
		this.#updateUnloadedCounters(payload, 'counters/setUnloadedCopilotCounters');
	}

	#updateUnloadedCollabCounters(payload: Array | Object)
	{
		this.#updateUnloadedCounters(payload, 'counters/setUnloadedCollabCounters');
	}

	#updateUnloadedCounters(payload: Array | Object, updateMethod: string)
	{
		if (!Array.isArray(payload) && Type.isPlainObject(payload))
		{
			payload = [payload];
		}
		const zeroedCountersForNewItems = {};
		const preparedItems = payload.map((item) => convertObjectKeysToCamelCase(item));

		preparedItems.forEach((item) => {
			zeroedCountersForNewItems[item.chatId] = 0;
		});
		void Core.getStore().dispatch(updateMethod, zeroedCountersForNewItems);
	}

	#getMessage(messageId: number | string): ImModelMessage
	{
		return Core.getStore().getters['messages/getById'](messageId);
	}

	#getDialog(dialogId: string): ImModelChat
	{
		return Core.getStore().getters['chats/get'](dialogId);
	}

	#hasTodayMessage(messageId: number | string): boolean
	{
		const message: ImModelMessage = this.#getMessage(messageId);
		const hasMessage = Utils.text.isUuidV4(message.id) || message.id > 0;

		return hasMessage && Utils.date.isToday(message.date);
	}

	#canDelete(dialogId: string): boolean
	{
		const NOT_DELETABLE_TYPES = [ChatType.openChannel];
		const { type } = this.#getDialog(dialogId);

		return !NOT_DELETABLE_TYPES.includes(type);
	}

	#handleFakeItemWithDraft(payload: SetDraftPayload, store: RecentStore): void
	{
		const existingItem = store.state.collection[payload.id];
		if (!existingItem)
		{
			store.commit('add', { ...this.getElementState(), ...this.#prepareFakeItemWithDraft(payload) });
		}
		store.commit('setRecentCollection', [payload.id.toString()]);
	}

	#prepareFakeItemWithDraft(payload: SetDraftPayload): Partial<ImModelRecentItem>
	{
		const messageId = this.#createFakeMessageForDraft(payload.id);

		return this.#formatFields({
			dialogId: payload.id.toString(),
			draft: {
				text: payload.text.toString(),
			},
			messageId,
		});
	}

	#createFakeMessageForDraft(dialogId: string): string
	{
		const messageId = `${FakeDraftMessagePrefix}-${dialogId}`;
		void Core.getStore().dispatch('messages/store', { id: messageId, date: new Date() });

		return messageId;
	}

	#shouldDeleteItemWithDraft(payload: SetDraftPayload): boolean
	{
		const existingItem = Core.getStore().state.recent.collection[payload.id];

		return existingItem
			&& !Type.isStringFilled(payload.text)
			&& existingItem.messageId.toString().startsWith(FakeDraftMessagePrefix)
		;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit