chat_ui/components/chat/components/ChatList.vue
2025-03-15 13:45:02 +03:30

1782 lines
58 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="comments-and-replays" class="flex-grow-1 position-relative">
<!-- header : group info -->
<div class="chat-list-group-info">
<!-- start: show right list -->
<button
v-if="!sidebarListStatusGetter"
type="button"
class="btn d-flex ps-0 ps-lg-2 align-items-center"
@click.prevent="closeList()"
>
<span
:style="{
transform: `rotateY(${
!sidebarListStatusGetter ? '0deg' : '180deg'
} )`,
}"
class="tavasi tavasi-Component-71--1"
></span>
</button>
<!-- end: show right list -->
<!-- <button type="button" class="btn ps-0 ps-lg-2 d-lg-none" @click="$root.$emit('open-list', this.menubarStatus)">
<span class="tavasi tavasi-Component-71--1"></span>
</button> -->
<!-- start: selected list info -->
<button
type="button"
class="btn group-row"
:title="titleGetter + '\r\n' + listGetter?.desc"
>
<div class="group-picture-container">
<img
class="group-picture"
:src="sourceGetter"
:alt="listGetter?.title"
/>
</div>
<div class="group-content">
<div class="group-title-container">
<h4 class="group-title">
<!-- <span class="tavasi tavasi-personal"></span> -->
<svg :class="'icon icon-' + iconGetter">
<use :xlink:href="'#icon-' + iconGetter"></use>
</svg>
{{ listGetter?.title }}
</h4>
</div>
<div class="group-description-container">
<p class="group-description" v-html="listGetter?.desc ?? null"></p>
</div>
</div>
</button>
<!-- end: selected list info -->
<div v-if="!isGuest">
<div
class="group-info-actions d-flex px-3 align-items-center"
style="white-space: nowrap"
>
<div v-if="$route.name !== 'privates'">
<a
v-if="listGetter?.reference_id && isIssueGroup()"
:href="'/jahat/' + buttonName + '/' + listGetter?.reference_id"
:title="buttonTitle"
@click.prevent="goToJahat()"
class="btn btn-default issue-redirect-btn"
>
{{ buttonTitle }}
</a>
<a
v-if="listGetter?.reference_id && isAnswerGroup()"
:href="
'/jahat/answers/' +
listGetter.parent_reference +
'/' +
listGetter?.reference_id
"
:title="buttonTitle"
@click.prevent="goToJahat()"
class="btn btn-default issue-redirect-btn"
>
{{ buttonTitle }}
</a>
<multiselect
v-model="selectedLabel"
class="ms-2 hiden3"
id="labels"
track-by="entity_field_id"
placeholder="فیلتر برچسب ها"
label="lable"
:multiple="false"
:options="listGetter?.entity_labels ?? []"
:allow-empty="true"
:searchable="true"
:options-limit="300"
:limit="10"
:limit-text="limitText"
:max-height="350"
@select="filterMessagesBySection"
@remove="filterMessagesBySection(undefined)"
selectLabel=""
selectedLabel=""
deselectLabel=""
:close-on-select="true"
:clear-on-select="false"
:preserve-search="true"
>
<div slot="selection" slot-scope="{ values, search, isOpen }">
<span
class="multiselect__single"
v-if="values.length"
v-show="!isOpen"
>{{ values.length }} فیلتر</span
>
</div>
<div slot="noResult" slot-scope="{ values, search, isOpen }">
چیزی یافت نشد.
</div>
<div slot="noOptions" slot-scope="{ values, search, isOpen }">
فهرست خالی است.
</div>
<!-- <div slot="beforeList" slot-scope="{ values, search, isOpen }">
beforeList
</div>
<div slot="afterList" slot-scope="{ values, search, isOpen }">
afterList
</div> -->
<!-- <div slot="singleLabel" slot-scope="{ values, search, isOpen }">
singleLabel
</div>
-->
<!-- <div slot="singleLabel" slot-scope="props">
<span class="option__desc"
><span class="option__title">{{ props.option.title }}</span></span
></div
>
<div slot="option" slot-scope="props">
<div class="option__desc">
<span class="option__title">{{ props.option.lable }}</span>
</div>
</div> -->
</multiselect>
<div class="multiselect-container ms-2">
<span class="badge badge-primary" title="تعداد اعضاء">
{{ groupMember?.length ?? 0 }}
</span>
<multiselect
v-model="selectedMember"
id="members"
class="hiden3"
track-by="user_id"
placeholder="فیلتر کاربران"
:show-labels="false"
:options="groupMember"
:searchable="true"
:allow-empty="true"
:close-on-select="true"
:options-limit="300"
:limit="3"
:limit-text="limitText"
:max-height="350"
:customLabel="
(item) => {
return `${item.full_name}`;
}
"
@select="filterMessagesByUser"
@remove="filterMessagesByUser(undefined)"
>
</multiselect>
</div>
<!-- <button class="btn" type="button">
<span class="tavasi tavasi-Component-285--2"></span>
</button> -->
</div>
<button
v-if="$route.name != 'lobbies'"
@click.prevent="showMessageSearch()"
class="btn"
type="button"
>
<span class="tavasi tavasi-Component-198--1"></span>
</button>
<context-menu
maxHeight="15rem"
ref="context"
class="position-static group-info-context-menu"
:list="commonContextMenu"
:contextMenu="commonContextMenu"
@remove-item="fireRemoveItemEvent"
@edit-item="fireEditItemEvent"
@join-to-group="fireAddToLobbyEvent"
@copy-item="fireCopyLinkEvent"
@other="fireCopyLinkEvent"
@filter="showFilter = true"
height="auto"
></context-menu>
</div>
</div>
</div>
<div
class="menu-multiselect hiden1 hiden2 hiden"
v-if="$route.name !== 'privates' && showFilter"
>
<div class="open m-2" @click="closefilter()">
<span class="tavasi tavasi-Component-294--1"></span>
</div>
<multiselect
v-model="selectedLabel"
class="ms-2 hiden3"
id="labels"
track-by="entity_field_id"
placeholder="فیلتر برچسب ها"
label="lable"
:multiple="false"
:options="listGetter?.entity_labels ?? []"
:allow-empty="true"
:searchable="true"
:options-limit="300"
:limit="10"
:limit-text="limitText"
:max-height="350"
@select="filterMessagesBySection"
@remove="filterMessagesBySection(undefined)"
selectLabel=""
selectedLabel=""
deselectLabel=""
:close-on-select="true"
:clear-on-select="false"
:preserve-search="true"
>
<div slot="selection" slot-scope="{ values, search, isOpen }">
<span
class="multiselect__single"
v-if="values.length"
v-show="!isOpen"
>{{ values.length }} فیلتر</span
>
</div>
<div slot="noResult" slot-scope="{ values, search, isOpen }">
چیزی یافت نشد.
</div>
<div slot="noOptions" slot-scope="{ values, search, isOpen }">
فهرست خالی است.
</div>
<!-- <div slot="beforeList" slot-scope="{ values, search, isOpen }">
beforeList
</div>
<div slot="afterList" slot-scope="{ values, search, isOpen }">
afterList
</div> -->
<!-- <div slot="singleLabel" slot-scope="{ values, search, isOpen }">
singleLabel
</div>
-->
<!-- <div slot="singleLabel" slot-scope="props">
<span class="option__desc"
><span class="option__title">{{ props.option.title }}</span></span
></div
>
<div slot="option" slot-scope="props">
<div class="option__desc">
<span class="option__title">{{ props.option.lable }}</span>
</div>
</div> -->
</multiselect>
<div class="multiselect-container">
<span class="badge badge-primary" title="تعداد اعضاء">
{{ groupMember?.length ?? 0 }}
</span>
<multiselect
v-model="selectedMember"
class="m-2"
id="members"
track-by="user_id"
placeholder="فیلتر کاربران"
:show-labels="false"
:options="groupMember"
:searchable="true"
:allow-empty="true"
:close-on-select="true"
:options-limit="300"
:limit="3"
:limit-text="limitText"
:max-height="350"
:customLabel="
(item) => {
return `${item.full_name}`;
}
"
@select="filterMessagesByUser"
@remove="filterMessagesByUser(undefined)"
>
<div slot="singleLabel" slot-scope="props">
<span class="option__desc"
><span class="option__title">{{
props.option.full_name
}}</span></span
>
</div>
</multiselect>
</div>
</div>
<div
class="mt-3 d-flex align-items-center px-3"
v-if="selectedLabel || selectedMember"
>
<div class="flex-grow-1 d-flex align-items-center">
<button
@click.prevent="showMobileActions = true"
class="btn selected-filter-list d-xl-none"
type="button"
>
<svg class="icon icon-filter-list">
<use xlink:href="#icon-filter-list"></use>
</svg>
<span class="mx-1">
1
<!-- {{ selectedLabel ? [selectedLabel].length : [selectedMember].length }} -->
</span>
فیلتر
</button>
<filter-list
v-if="selectedLabel"
styleClass="style-2"
keyName="lable"
:filters="[selectedLabel]"
@remove-filter="removeFromLabelFilter"
></filter-list>
<filter-list
v-if="selectedMember"
styleClass="style-2"
keyName="full_name"
:filters="[selectedMember]"
@remove-filter="removeFromUserFilter"
></filter-list>
</div>
<button
@click.prevent="showIssueProperties = true"
class="btn selected-filter-list"
type="button"
>
<svg class="icon icon-eye"><use xlink:href="#icon-eye"></use></svg>
</button>
</div>
<div class="d-flex">
<div
id="paste-section"
class="comment-list position-relative"
:class="{ 'replays-is-open': isMainEditMode }"
>
<div
:class="{ show: showSelectionActionBar }"
class="select-options d-flex align-items-center justify-content-between mx-2"
>
<div class="d-flex align-items-center py-1">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
id="select-all-checkbox"
v-model="checked"
@change="selectAllCheckbox"
/>
<label
class="custom-control-label"
for="select-all-checkbox"
></label>
</div>
<span class="mx-4 text-start" dir="ltr"
>{{ selectedCommentsId.length }}<small>x</small></span
>
<div class="selection-actions">
<!-- <button
disabled="true"
type="button"
class="btn btn-outline-secondary ms-2"
>
انتقال
</button>
<button
disabled="true"
type="button"
class="btn btn-outline-secondary ms-2"
>
خروجی
</button> -->
<!-- <button
disabled="true"
type="button"
class="btn btn-outline-secondary ms-2"
>
آرشیو
</button> -->
<button-component
@click="deleteMany()"
classes="btn-outline-danger"
buttonText="حذف"
:buttonLoading="buttonLoading"
></button-component>
</div>
</div>
<button-component
@click="unSelectAllMessages()"
buttonText="انصراف"
title="انصراف"
classes="d-flex align-items-center text-danger"
>
<svg class="icon icon-error">
<use xlink:href="#icon-error"></use>
</svg>
&nbsp;
<!-- <i style="font-size:0.9rem;color:inherit;" class="tavasi tavasi-Component-294--1"></i> -->
<!-- <span style="font-size:1rem" >بستن</span> -->
</button-component>
</div>
<div
id="comments"
class="comments"
ref="messages"
:class="{ 'show-drad-effects': dragStarted }"
>
<drop
class="drop"
@dragleave="handleDragLeave"
@drop="handleDrop"
@dragover="handleDragEnter($event, 'messages')"
>
<div v-if="canView">
<the-content-loading
class="absolute-positioning"
v-if="fetchingData"
></the-content-loading>
<div v-else>
<!-- <div v-if="localComments && localComments.length"> -->
<virtual-list
:estimate-size="200"
class="stream scroll-touch"
:class="[
{ overflow: overflow },
{ 'filter-is-active': selectedLabel || selectedMember },
]"
ref="vsl"
:data-key="'id'"
:data-sources="localComments"
:data-component="itemComponent"
:start="150"
@totop="toTop()"
@tobottom="toBottom()"
:extra-props="{
selectedCommentsId: selectedCommentsId,
showSelectionActionBar: showSelectionActionBar,
replayUsers: replayUsers,
showCardReplays: true,
replayFrom: 'messages',
editFrom: 'isMainEditMode',
}"
>
<div slot="header" v-show="overflow" class="header">
<div class="spinner" v-show="!finished_top"></div>
<div class="finished" v-show="finished_top">
No more data
</div>
</div>
</virtual-list>
<div class="empty" v-show="!localComments.length">
<div class="wrapper">
<div class="icon"></div>
<div class="tips">No chats</div>
</div>
</div>
<!-- </div> -->
<!-- <no-data v-else></no-data> -->
</div>
</div>
<no-data v-else>
<the-content-loading v-if="fetchingData"></the-content-loading>
<div
v-else
class="d-flex justify-content-center align-items-center"
>
<div
class="alert alert-warning d-flex justify-content-center align-items-center"
>
<span
class="tavasi tavasi-warning-circle color-inherit ms-1 text__32"
></span>
عدم دسترسی
</div>
</div>
</no-data>
</drop>
<div
v-if="showDroppedList && dropBoxClass == 'messages'"
class="dropped-list"
:class="[dropBoxClass, { uploading: uploading }]"
>
<div class="heading-part mb-2">
<button
type="button"
class="btn"
@click.prevent="closeDroppedList()"
:disabled="uploading"
>
<svg class="icon icon-Component-294--1">
<use xlink:href="#icon-Component-294--1"></use>
</svg>
</button>
<span class="flex-grow-1"> ارسال فایل </span>
<button
type="button"
class="btn btn-primary"
@click.prevent="mainSaveFiles()"
>
ارسال
</button>
</div>
<div class="form-group">
<textarea
v-model="uploadDescription"
name="upload-description"
id="upload-description"
cols="30"
rows="3"
class="form-control"
placeholder="توضیحاتی برای فایل وارد نمایید."
></textarea>
<!-- <input
type="text"
class="form-control"
placeholder="عنوانی برای فایل وارد نمایید."
v-model="uploadDescription"
/> -->
</div>
<div v-if="showMobileUploadButton" class="form-group">
<div class="image-uploader-container">
<image-uploader
:accept="acceptTypes"
ref="file-uploader"
:preview="false"
:debug="1"
:maxWidth="300"
:maxHeight="300"
:maxSize="2"
:quality="1"
:autoRotate="true"
outputFormat="file"
:className="['fileinput', { 'fileinput--loaded': hasImage }]"
:capture="false"
@input="setImage"
@onUpload="startUploading"
@onComplete="endOfUploading"
>
<label
class="upload-label"
for="fileInput"
slot="upload-label"
>
<!-- <img
v-if="
files?.length &&
(getFileExtension(files[0].name) == 'png' ||
getFileExtension(files[0].name) == 'jpg' ||
getFileExtension(files[0].name) == 'jpeg')
"
:src="files"
class="img-fluid"
:alt="files.name"
/>
<span
v-else-if="files?.length"
class="tavasi"
:class="`tavasi-${getFileExtension(files[0].name)}-file`"
></span> -->
<!-- replace image src with incoming src -->
<!-- <span v-else class="tavasi tavasi-cloud-upload"></span> -->
<span class="tavasi tavasi-cloud-upload"></span>
</label>
</image-uploader>
</div>
</div>
<ol type="1" class="file-list">
<li
class="file-list-item"
v-for="(file, index) in files"
:key="index"
>
<div class="d-flex align-items-center py-3 px-1">
<span class="file-icon-container">
<svg :class="'icon icon-' + fileIcon(file.type)">
<use :xlink:href="'#icon-' + fileIcon(file.type)"></use>
</svg>
</span>
<div class="me-3 flex-grow-1">
<h6>
{{ file.name }}
</h6>
<p class="m-0">
{{ formatBytes(file.size) }}
</p>
</div>
<button
type="button"
class="btn c-btn-danger"
@click.prevent="removeFile(file, index)"
>
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</button>
</div>
</li>
</ol>
<div class="form-group" v-if="pasting && showReplays">
<div class="custom-checkbox-container position-static">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
v-model="saveAs"
true-value="replay"
false-value="message"
id="save-as"
/>
<label class="custom-control-label pe-1" for="save-as"
>ذخیره به عنوان پاسخ</label
>
</div>
</div>
</div>
<div v-if="uploading" class="uploading-animation">
<the-content-loading class="position-static">
در حال بارگزاری فایل ...
</the-content-loading>
</div>
</div>
<div
v-if="dragStarted && dropBoxClass == 'messages'"
class="drop-container"
:class="dropBoxClass"
>
<span> فایل ها را جهت ارسال اینجا رها کنید. </span>
</div>
<div class="first-last-button">
<button-component
title="برو به اولین پیام"
@click="tofirstItem()"
classes="go-first"
buttonText=""
>
<svg class="icon icon-Component-25--1">
<use xlink:href="#icon-Component-25--1"></use>
</svg>
</button-component>
<button-component
@click="toLastItem()"
title="برو به آخرین پیام"
classes="go-last"
buttonText=""
>
<svg class="icon icon-Component-25--1">
<use xlink:href="#icon-Component-25--1"></use>
</svg>
</button-component>
</div>
</div>
<!-- comment form -->
<div class="comment-form-container">
<div v-if="listGetter?.show_joined" class="join-group">
<button-component
classes="btn-outline-primary join-button"
title="پیوستن به گروه"
buttonText="پبوستن به گروه"
@click="openAuthModal()"
>
</button-component>
<!-- <button-component
classes="me-2"
title="انصراف"
buttonText="انصراف"
@click="cancelJoin"
>
</button-component> -->
</div>
<div v-else>
<div
ref="replyComment"
class="footer-reply_comment"
v-if="isMainEditMode"
>
<div class="d-flex align-items-center">
<span class="tavasi" :class="iconClass"></span>
<div class="p-1">
<p
v-if="isForward"
class="mb-1 text__13"
style="color: #00b6e3"
>
هدایت پیام
</p>
<div v-if="getForwardItem?.type == 'Private'">
<h6 class="commentor-name">
<span v-if="isForward">@</span
>{{ getForwardItem?.message.user?.username }}
<!-- @{{ userFullname(mainCommentor.user) }} -->
</h6>
<p class="commentor-text">
{{
JSON.parse(
JSON.stringify(getForwardItem?.message?.text)
)
}}
</p>
</div>
<div v-else>
<!-- <h6 class="commentor-name">
<span v-if="isForward">@</span
>{{ mainCommentor?.title }}
</h6> -->
<p class="commentor-text">
{{ JSON.parse(JSON.stringify(mainCommentor?.text)) }}
</p>
</div>
</div>
</div>
<button
type="button"
class="btn p-0"
@click.prevent="closeComments('isMainEditMode')"
>
<span class="tavasi tavasi-Component-294--1"></span>
</button>
</div>
<form
@submit.prevent="save($event, 'mainTextAreaInput', 'messages')"
class="comment-form"
>
<my-audio-recorder @send="onResult"></my-audio-recorder>
<!-- <my-plugin-audio-recorder @send="onResult"></my-plugin-audio-recorder> -->
<div class="input-group" dir="ltr">
<div dir="rtl" class="input-group-prepend">
<button
@click.prevent="openUploadForm('messages')"
class="btn ps-3"
type="button"
id="button-addon2"
>
<span class="tavasi tavasi-Component-69--1"></span>
</button>
</div>
<!-- <div dir="rtl" class="input-group-prepend">
<button class="btn p-1" type="button" id="button-addon2">
<span class="tavasi tavasi-Component-305--1"></span>
</button>
</div>
<div dir="rtl" class="input-group-prepend">
<button class="btn" type="button" id="button-addon2">
<span class="tavasi tavasi-at-sign"></span>
</button>
</div> -->
<textarea
ref="mainTextAreaInput"
@keyup.exact.escape="closeComments('isMainEditMode')"
@keyup.exact.enter="
save($event, 'mainTextAreaInput', 'messages')
"
dir="rtl"
placeholder="پیامتو بنویس"
lass="form-control"
v-model="userMessage"
class="form-control textform"
name="user-comment"
id="user-comment"
cols="30"
rows="1"
></textarea>
<div dir="rtl" class="input-group-append">
<button-component
type="submit"
title="ارسال"
classes="btn"
buttonText=""
:disabled="savingComment"
:buttonLoading="savingComment"
>
<span class="tavasi tavasi-Component-236--1"></span>
<!-- <span v-else class="tavasi tavasi-Component-85--1" -->
<!-- ><span class="path1"></span><span class="path2"></span -->
<!-- ></span> -->
</button-component>
<!-- <button type="button" class="btn" v-else>
<vue-record-audio
:key="refreshRecorder"
mode="press"
@stream="onStream"
@result="onResult"
></vue-record-audio>
</button> -->
<!-- <button-component
v-if="recording"
title="حذف"
classes="remove-record"
buttonText=""
:class="{ recording: recording }"
@click="removeRecordFile()"
>
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</button-component> -->
</div>
</div>
</form>
</div>
</div>
</div>
<!-- replay list box -->
<div
id="replays"
v-if="showReplays"
class="position-relative"
:class="{ show: showReplays }"
>
<div class="replay-head">
<div>
<p class="group-title">
<!-- پاسخ به: -->
<!-- <strong style> -->
{{ listGetter?.title }}
<!-- </strong> -->
</p>
<p
class="group-description"
v-html="groupDescription(replayTo.text)"
></p>
</div>
<div class="d-flex">
<button @click.prevent="closeReplays()" type="button" class="btn">
<span class="tavasi tavasi-Component-294--1"></span>
</button>
</div>
</div>
<div
class="replay-list position-relative"
ref="replays"
:class="{ 'replay-comment-is-open': isReplayEditMode }"
>
<drop
class="drop"
@dragleave="handleDragLeave"
@drop="handleDrop"
@dragover="handleDragEnter($event, 'replays')"
>
<the-content-loading
class="absolute-positioning"
v-if="fetchinReplaygData"
></the-content-loading>
<div v-else>
<!-- <div v-if="replays && replays.length"> -->
<virtual-list
:estimate-size="200"
class="stream scroll-touch replays-scroll"
:class="[
{ overflow: overflow },
{ 'filter-is-active': selectedLabel || selectedMember },
]"
ref="replayVsl"
:data-key="'id'"
:data-sources="replays"
:data-component="itemComponent"
:start="150"
@totop="replayToTop('replays', 'isReplayEditMode')"
@tobottom="replayToBottom('replays', 'isReplayEditMode')"
:extra-props="{
replayFrom: 'replays',
editFrom: 'isReplayEditMode',
}"
>
<div slot="header" v-show="overflow" class="header">
<div class="spinner" v-show="!finished_top"></div>
<div class="finished" v-show="finished_top">No more data</div>
</div>
</virtual-list>
<div class="empty" v-show="!replays.length">
<div class="wrapper">
<div class="icon"></div>
<div class="tips">No chats</div>
</div>
</div>
<!-- </div> -->
<!-- <no-data v-else></no-data> -->
<!-- <div v-if="replays.length">
<chat-list-item
v-for="(comment, key) in replays"
:key="key"
:comment="comment"
@replay-comment="replyComments"
@delete-replay="deleteItem(replays, comment, key)"
@edit-replay="
editComment(comment, 'replays', 'isReplayEditMode')
"
></chat-list-item>
</div>
<no-data v-else></no-data> -->
</div>
</drop>
</div>
<div class="comment-form-container">
<div
ref="replyComment"
class="footer-reply_comment"
v-if="replyComment || isReplayEditMode"
>
<div class="d-flex align-items-center">
<span class="tavasi" :class="iconClass"></span>
<div class="p-1">
<h6 class="commentor-name">
{{ userFullname(replayCommentor.user) }}
</h6>
<p class="commentor-text">{{ replayCommentor.text }}</p>
</div>
</div>
<button
type="button"
class="btn p-0"
@click.prevent="closeReplyComment('isReplayEditMode')"
>
<span class="tavasi tavasi-Component-294--1"></span>
</button>
</div>
<form
@submit.prevent="saveReplay($event, 'replayTextRef', 'replays')"
class="comment-form"
>
<my-audio-recorder @send="replayOnResult"></my-audio-recorder>
<div class="input-group" dir="ltr">
<div dir="rtl" class="input-group-prepend">
<button
@click.prevent="openUploadForm('replays')"
class="btn ps-3"
type="button"
id="button-addon2"
>
<span class="tavasi tavasi-Component-69--1"></span>
</button>
</div>
<!-- <div dir="rtl" class="input-group-prepend">
<button class="btn p-1" type="button" id="button-addon2">
<span class="tavasi tavasi-Component-305--1"></span>
</button>
</div> -->
<!-- <div dir="rtl" class="input-group-prepend">
<button class="btn" type="button" id="button-addon2">
<span class="tavasi tavasi-at-sign"></span>
</button>
</div> -->
<textarea
ref="replayTextRef"
@keyup.exact.enter="
saveReplay($event, 'replayTextRef', 'replays')
"
@keyup.exact.escape="closeReplyComment('isReplayEditMode')"
dir="rtl"
placeholder="پیامتو بنویس"
lass="form-control"
v-model="replayText"
class="form-control textform"
name="user-comment"
id="user-comment2"
cols="30"
rows="1"
></textarea>
<div dir="rtl" class="input-group-append">
<button-component
type="submit"
title="ارسال"
classes="btn"
buttonText=""
:disabled="savingReplay"
:buttonLoading="savingReplay"
>
<span class="tavasi tavasi-Component-236--1"></span>
<!-- <span v-else class="tavasi tavasi-Component-85--1" -->
<!-- ><span class="path1"></span><span class="path2"></span -->
<!-- ></span> -->
</button-component>
<!-- <button type="button" class="btn" v-else>
<vue-record-audio
:key="replayRefreshRecorder"
mode="press"
@stream="replayOnStream"
@result="replayOnResult"
></vue-record-audio>
</button> -->
<!-- <button-component
v-if="replayRecording"
title="حذف"
classes="remove-record"
buttonText=""
:class="{ recording: replayRecording }"
@click="replayRemoveRecordFile()"
>
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</button-component> -->
</div>
</div>
</form>
</div>
<div
v-if="showDroppedList && dropBoxClass == 'replays'"
class="dropped-list"
:class="dropBoxClass"
>
<div class="heading-part mb-2">
<button
type="button"
class="btn"
@click.prevent="closeDroppedList()"
>
<svg class="icon icon-Component-294--1">
<use xlink:href="#icon-Component-294--1"></use>
</svg>
</button>
<span class="flex-grow-1"> ارسال فایل </span>
<button
type="button"
class="btn btn-primary"
@click.prevent="replaySaveFiles()"
>
ارسال
</button>
</div>
<div class="form-group">
<textarea
v-model="uploadDescription"
name="upload-description"
id="upload-description"
cols="30"
rows="3"
class="form-control"
placeholder="توضیحاتی برای فایل وارد نمایید."
></textarea>
<!-- <input
type="text"
class="form-control"
placeholder="عنوانی برای فایل وارد نمایید."
v-model="uploadDescription"
/> -->
</div>
<ol type="1" class="file-list">
<li
class="file-list-item"
v-for="(file, index) in files"
:key="index"
>
<div class="d-flex align-items-center py-3 px-1">
<span class="file-icon-container">
<svg :class="'icon icon-' + fileIcon(file.type)">
<use :xlink:href="'#icon-' + fileIcon(file.type)"></use>
</svg>
</span>
<div class="me-3 flex-grow-1">
<h6>
{{ file.name }}
</h6>
<p class="m-0">
{{ formatBytes(file.size) }}
</p>
</div>
<button
type="button"
class="btn c-btn-danger"
@click.prevent="removeFile(file, index)"
>
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</button>
</div>
</li>
</ol>
<!-- upload button -->
<!-- <form class="d-xl-none">
<div class="form-group">
<h5>بارگزاری فایل</h5>
</div> -->
<div v-if="showMobileUploadButton" class="form-group">
<div class="image-uploader-container">
<image-uploader
:accept="acceptTypes"
ref="file-uploader"
:preview="false"
:debug="1"
:maxWidth="300"
:maxHeight="300"
:maxSize="2"
:quality="1"
:autoRotate="true"
outputFormat="file"
:className="['fileinput', { 'fileinput--loaded': hasImage }]"
:capture="false"
@input="setImage"
@onUpload="startUploading"
@onComplete="endOfUploading"
>
<label class="upload-label" for="fileInput" slot="upload-label">
<!-- <img
v-if="
files?.length &&
(getFileExtension(files[0].name) == 'png' ||
getFileExtension(files[0].name) == 'jpg' ||
getFileExtension(files[0].name) == 'jpeg')
"
:src="files"
class="img-fluid"
:alt="files.name"
/> -->
<!-- <span
v-else-if="files?.length"
class="tavasi"
:class="`tavasi-${getFileExtension(files[0].name)}-file`"
></span> -->
<!-- replace image src with incoming src -->
<!-- <span v-else class="tavasi tavasi-cloud-upload"></span> -->
<span class="tavasi tavasi-cloud-upload"></span>
</label>
</image-uploader>
</div>
</div>
<!-- </form> -->
</div>
<div
v-if="dragStarted && dropBoxClass == 'replays'"
class="drop-container d-none d-xl-flex"
:class="dropBoxClass"
>
<span> فایل ها را جهت ارسال اینجا رها کنید. </span>
</div>
<!-- upload group/lobby avatar -->
</div>
<!-- #region issue -->
<div
id="replays"
v-if="showIssueProperties"
class="position-relative"
:class="{ show: showIssueProperties }"
>
<div class="replay-head">
<div>
<p class="group-title m-0">
فیلتر شده براساس:
<strong style>
{{ selectedLabel?.lable }}
</strong>
</p>
</div>
<div class="d-flex">
<button
@click.prevent="closeIssueProperties()"
type="button"
class="btn d-flex"
>
<span class="tavasi tavasi-Component-294--1"></span>
</button>
</div>
</div>
<div
class="replay-list position-relative"
ref="replays"
:class="{ 'replay-comment-is-open': showIssueProperties }"
>
<div class="drop">
<div
class="stream scroll-touch replays-scroll"
:class="[{ 'filter-is-active': selectedLabel || selectedMember }]"
>
<!-- :is="issueProperty[selectedLabel.entity_field_id]" -->
<component
v-if="selectedLabel?.key && issue[selectedLabel?.key]"
:enableComments="false"
:sections="issueSections"
:entityContent="selectedLabel"
:is="entityFieldsGetter(selectedLabel.entity_field_id)"
:propertyName="selectedLabel.key"
:rootCommentId="listGetter?.reference_id"
:updateKey="false"
:entity="issue"
:comments="[]"
:showComments="false"
class="vertical"
:carouselItems="1"
/>
<!-- {{ issue[selectedLabel.key] }} -->
</div>
</div>
</div>
</div>
</div>
<!-- #endregino -->
<div v-if="showMobileActions" class="mobile-actions">
<div class="d-flex justify-content-between p-3 border-bottom">
<h5 class="m-0">عملیات</h5>
<button
@click.prevent="showMobileActions = false"
class="btn d-flex"
type="button"
>
<span
style="transform: rotate(180deg); display: inline-block"
class="tavasi tavasi-Component-71--1"
></span>
</button>
</div>
<div class="p-3">
<div class="d-flex justify-content-center mb-3">
<a
v-if="listGetter?.reference_id && isIssueGroup()"
:href="'/jahat/' + buttonName + '/' + listGetter?.reference_id"
:title="buttonTitle"
@click.prevent="goToJahat()"
class="btn btn-outline-primary"
>
{{ buttonTitle }}
</a>
<a
v-if="listGetter?.reference_id && isAnswerGroup()"
:href="
'/jahat/answers/' +
listGetter.parent_reference +
'/' +
listGetter?.reference_id
"
:title="buttonTitle"
@click.prevent="goToJahat()"
class="btn btn-outline-primary"
>
{{ buttonTitle }}
</a>
</div>
<multiselect
v-model="selectedLabel"
class="mb-3"
id="labels"
track-by="entity_field_id"
placeholder="فیلتر برچسب ها"
label="lable"
:multiple="false"
:options="listGetter?.entity_labels ?? []"
:allow-empty="true"
:searchable="true"
:options-limit="300"
:limit="10"
:limit-text="limitText"
:max-height="350"
@select="filterMessagesBySection"
@remove="filterMessagesBySection(undefined)"
selectLabel=""
selectedLabel=""
deselectLabel=""
:close-on-select="true"
:clear-on-select="false"
:preserve-search="true"
>
<div slot="selection" slot-scope="{ values, search, isOpen }">
<span
class="multiselect__single"
v-if="values.length"
v-show="!isOpen"
>{{ values.length }} فیلتر</span
>
</div>
<div slot="noResult" slot-scope="{ values, search, isOpen }">
چیزی یافت نشد.
</div>
<div slot="noOptions" slot-scope="{ values, search, isOpen }">
فهرست خالی است.
</div>
<!-- <div slot="beforeList" slot-scope="{ values, search, isOpen }">
beforeList
</div>
<div slot="afterList" slot-scope="{ values, search, isOpen }">
afterList
</div> -->
<!-- <div slot="singleLabel" slot-scope="{ values, search, isOpen }">
singleLabel
</div>
-->
<!-- <div slot="singleLabel" slot-scope="props">
<span class="option__desc"
><span class="option__title">{{ props.option.title }}</span></span
></div
>
<div slot="option" slot-scope="props">
<div class="option__desc">
<span class="option__title">{{ props.option.lable }}</span>
</div>
</div> -->
</multiselect>
<div class="multiselect-container">
<span class="badge badge-primary" title="تعداد اعضاء">
{{ groupMember?.length ?? 0 }}
</span>
<multiselect
v-model="selectedMember"
id="members"
class="mb-3"
track-by="user_id"
placeholder="فیلتر کاربران"
:show-labels="false"
:options="groupMember"
:searchable="true"
:allow-empty="true"
:close-on-select="true"
:options-limit="300"
:limit="3"
:limit-text="limitText"
:max-height="350"
:customLabel="
(item) => {
return `${item.full_name}`;
}
"
@select="filterMessagesByUser"
@remove="filterMessagesByUser(undefined)"
>
</multiselect>
</div>
</div>
</div>
<div v-if="showAuthModal" class="custom-auth-modal auth-page">
<!-- Vertically centered scrollable modal -->
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">
{{ $t("LoginForm") }}
</h5>
<button
type="button"
class="close ms-0 me-auto"
data-dismiss="modal"
aria-label="Close"
@click.prevent="closeAuthModal()"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="authentication-styles">
<auth-component></auth-component>
</div>
</div>
<!-- <div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div> -->
</div>
</div>
</div>
<base-modal
v-if="showForwardModal"
modalSize="modal-md"
modalTitle="هدایت به ..."
:hasFooter="false"
@close="closeAndResetForwardModal"
>
<user-search @forward="redirectToPrivates"></user-search>
</base-modal>
</div>
</template>
<script>
// import Vue from "vue";
// import VueRecord from "@codekraft-studio/vue-record";
import { mapState, mapActions } from "pinia";
import { useChatStore } from "@chat/stores/chatStore";
import { useCommonStore } from "@stores/commonStore";
import chatApi from "@chat/apis/chatApi";
import replayMixin from "@chat/mixins/replayMixin";
import createdMixin from "@chat/mixins/createdMixin";
import mountedMixin from "@chat/mixins/mountedMixin";
import fileMixin from "@chat/mixins/fileMixin";
import virtialListMixin from "@chat/mixins/virtialListMixin";
import headerMixin from "@chat/mixins/headerMixin";
import jahatMixin from "@chat/mixins/jahatMixin";
import forwardMixin from "@chat/mixins/forwardMixin";
import commonMixin from "@chat/mixins/commonMixin";
import messageMixin from "@chat/mixins/messageMixin";
// import lobbiesContextMenu from "~/json/chat/json/lobbiesContextMenu.json";
// import VirtualList from "vue-virtual-scroll-list";
import ChatListItem from "@chat/components/chat/components/ChatListItem";
import entityFields from "@chat/json/entityFields.json";
// Vue.use(VueRecord);
export default {
name: "chat-list",
mixins: [
commonMixin,
createdMixin,
fileMixin,
forwardMixin,
headerMixin,
jahatMixin,
messageMixin,
mountedMixin,
replayMixin,
virtialListMixin,
],
beforeMount() {
this.initServices();
},
watch: {
$route: {
handler: function (to) {
this.isRedirectedFromOtherSystems = false;
// this.getAll(to.name);
},
deep: true,
// immediate: true,
},
},
data() {
return {
uploading: false,
entityFields: entityFields,
acceptTypes: "*",
// acceptTypes:
// ".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/*,audio/*,video/*",
showMobileUploadButton: false,
imageUrl: undefined,
hasImage: false,
labelFilter: [],
userFilter: [],
showMobileActions: false,
// issueProperty: {
// 3: "EntityTextContent", // چکیده
// 5: "EntityTextContent", // بیان
// 6: "EntityTextContent", // عنوان
// 9: "EntityTextContent", // هدف
// 11: "EntityTextContent", // توضیح
// 10: "EntityTextContent", // روش پیشنهادی
// 24: "EntityTextContent", // موضوع
// 15: "EntityQustionsContent", // سوالات
// 14: "EntityListContent", // محصولات
// 51: "EntityListContent", // معیار
// 4: "EntitySliderContent", // ضرورت
// 7: "EntitySliderContentTwo", // فایلهای مرتبط
// 8: "EntitySliderContentTwo", // لینک بیرونی
// 13: "EntityHorizontalListContent", // واژه گان
// 12: "EntityHorizontalListContent", // موضوع
// // 6:'EntityFishContent',
// },
issueSections: [],
showAuthModal: false,
isAlreadyJoined: true, // show join button if user does not joined yet.
// showMessageSearch: true, // toggling message search box.
commonContextMenu: [],
lobbiesChildContextMenu: [],
lobbiesParentContextMenu: [],
replayFirstLoad: true, // prevent requesting twice after replay box loaded.
firstLoad: true, // prevent requesting twice after page loaded.
buttonName: "",
buttonTitle: "",
showList: false,
// current_row: 0,
itemComponent: ChatListItem,
finished_top: false,
finished_bottom: false,
messages: [],
overflow: false,
limit: 10,
replayOffset: 0,
replayLimit: 10,
replayFinishedTop: false,
replayFinishedBottom: false,
replayOverflow: false,
// #region mehdi
nomber: 0,
showFilter: false,
contextMenuHeader: undefined,
statusPage: 0,
// messagesContextMenuHeader: messagesContextMenuHeader,
// #endregion
messageLabel: [],
selectedMessageLabel: [],
selectedLabel: undefined,
groupMember: [],
selectedGroupMember: [],
selectedMember: undefined,
busy: false,
iconTypes: {
1: "personal",
2: "groups",
3: "lobby",
},
checked: false,
showSelectionActionBar: false,
selectedCommentsId: [],
prevSelectedItemIndex: 0,
savingReplay: false,
replayPagination: {
page: 0,
total: 0,
pageTop: 0,
pageBottom: 1,
seenDate: null,
},
fetchinReplaygData: false,
replays: [],
canView: true,
replayTo: {},
pagination: {
page: 0,
total: 0,
pageTop: 0,
pageBottom: 1,
seenDate: null,
},
messages: [],
mainCommentor: {},
replayCommentor: {},
replayText: null,
showReplays: false,
isReplayEditMode: false,
isMainEditMode: false,
fetchingData: false,
buttonLoading: false,
replyComment: false,
replys: {},
fileUploadHttpService: undefined,
httpService: undefined,
showList: false,
contextMenu: undefined,
userMessage: null,
localComments: [],
savingComment: false,
replayUsers: [],
firstTimeBefore: true,
firstTimeAfter: false,
isRedirectedFromOtherSystems: false,
groupId: undefined,
messageId: undefined,
dragStarted: false,
filenames: [],
showDroppedList: false,
files: [],
uploadDescription: null,
recording: false,
replayRecording: false,
abortRecord: false,
replayAbortRecord: false,
refreshRecorder: 1,
replayRefreshRecorder: 1,
dropBoxClass: null,
saveAs: "message", // checkbox value for saving screen shot as: 1-message 2-replay
pasting: false, // when user paste something.used for showing dropped list checkbox.
showForwardModal: false, // used for showing the redirect modal when user redirect a message.
selectedItem: {}, // store selected comment when user redirect a message.
isForward: false, // store selected comment when user redirect a message.
isFirstPageReady: false,
showIssueProperties: false, // show/hide issues properties.
issue: {},
menubarStatus: true,
mediaStream: null,
};
},
computed: {
...mapState(useCommonStore, [
"getPanelStatus",
"isSidebarCollapsed",
"sectionsGetter",
"getForwardItem",
"currentUser",
"isGuest",
"sidebarListStatusGetter",
]),
...mapState(useChatStore, ["listGetter"]),
amIGroupAdmin() {
if (this.listGetter?.admins)
return Boolean(
this.listGetter?.admins.find((id) => id == this.currentUser.user_id)
);
return true;
},
iconClass() {
return this.isMainEditMode ? "tavasi-Component-242--1" : "tavasi-reply";
},
sourceGetter() {
// if (this.$route.name == "privates") {
// return getGroupAvatar(this.listGetter?.user.avatar);
// }
return getGroupAvatar(this.listGetter);
},
titleGetter() {
// if (this.$route.name == "privates") {
// return userFullname(this.listGetter?.user);
// }
return this.listGetter?.title;
},
iconGetter() {
// if (this.$route.name == "privates") {
// return userFullname(this.listGetter?.user);
// }
return this.iconTypes[this.listGetter?.icon_type];
},
},
methods: {
...mapActions(useCommonStore, [
"checkPermissions",
"getUserPermissionTags",
]),
...mapActions(useChatStore, [
"SET_SECTIONS",
"SET_FORWARD_ITEM",
"SET_AUTHORIZED_PAGES",
"SET_USER_PERMISSIONS",
"SET_SIDEBAR_LIST_STATUS",
]),
fireAddToLobbyEvent() {
const { $eventBus } = useNuxtApp();
$eventBus.emit("add-to-lobby");
},
fireCopyLinkEvent() {
const { $eventBus } = useNuxtApp();
$eventBus.emit("copy-link");
},
fireEditItemEvent() {
const { $eventBus } = useNuxtApp();
$eventBus.emit("open-edit-item-form");
},
fireRemoveItemEvent() {
const { $eventBus } = useNuxtApp();
$eventBus.emit("open-remove-item-modal");
},
unSelectAllMessages() {
this.showSelectionActionBar = false;
this.selectedCommentsId = [];
},
alertUser() {
alert("ontouchstart");
},
},
};
</script>
<style lang="scss">
.multiselect-container {
position: relative;
.badge {
position: absolute;
left: -5px;
top: -5px;
z-index: 9;
}
}
</style>