تغییرات گفت گو

This commit is contained in:
Mehdi104797 2025-03-15 13:45:02 +03:30
parent 2a5f878608
commit 815f61b76f
39 changed files with 3181 additions and 80 deletions

View File

@ -49,6 +49,9 @@ export default {
updateAvatar: 'group/ed/avatar/',
},
entity:{
get: "get",
},
// myMessages: {
// list: "message/get/ext/",
// create: 'message/set',

View File

@ -1459,25 +1459,26 @@
// 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 chatApi from "~/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 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/ChatListItem";
// import VirtualList from "vue-virtual-scroll-list";
import ChatListItem from "@chat/components/chat/components/ChatListItem";
import entityFields from "~/json/chat/json/entityFields.json";
import entityFields from "@chat/json/entityFields.json";
// Vue.use(VueRecord);
@ -1684,7 +1685,7 @@ export default {
};
},
computed: {
...mapState([
...mapState(useCommonStore, [
"getPanelStatus",
"isSidebarCollapsed",
"sectionsGetter",
@ -1693,7 +1694,7 @@ export default {
"isGuest",
"sidebarListStatusGetter",
]),
...mapState("list", ["listGetter"]),
...mapState(useChatStore, ["listGetter"]),
amIGroupAdmin() {
if (this.listGetter?.admins)
@ -1725,8 +1726,11 @@ export default {
},
},
methods: {
...mapActions(["checkPermissions", "getUserPermissionTags"]),
...mapActions([
...mapActions(useCommonStore, [
"checkPermissions",
"getUserPermissionTags",
]),
...mapActions(useChatStore, [
"SET_SECTIONS",
"SET_FORWARD_ITEM",
"SET_AUTHORIZED_PAGES",

View File

@ -0,0 +1,804 @@
<template>
<div
:id="'chat-item-' + source?.id"
class="comment-container chat-list-item-component"
:class="[
{ replayer: source?.user?.user_id != currentUser.user_id },
{ subjecting: source?.subjecting ?? false },
{ active: source?.active },
]"
>
<div
class="comment"
:style="{ 'margin-bottom': commentMarginBoxForSubjecting + 'em' }"
>
<div class="comment-header">
<div v-if="showSelectionActionBar" class="custom-checkbox-container">
<div class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
:id="'comment-' + source.id"
:value="source.id"
v-model="updateSelectListAfterCheck"
/>
<label
class="custom-control-label"
:for="'comment-' + source.id"
></label>
</div>
</div>
<div class="commentor-avatar">
<img
:src="userAvatar(source?.user)"
:alt="userFullname(source?.user)"
/>
</div>
<div class="commentor-name">
{{ userFullname(source.user) }}
</div>
</div>
<div class="comment-body">
<!-- <div class="commentor-name">
{{ userFullname(source.user) }}
</div> -->
<!-- v-if="source?.label_key && source.label_key.length" -->
<div
class="comment-labels"
v-if="sectionItem?.lable && sectionItem?.lable?.length"
>
<button
@click.prevent="getSectionFromJahat()"
type="button"
class="btn ms-1"
:class="'label-' + sectionItem?.entity_field_id"
>
{{ sectionItem?.lable }}
</button>
<!-- <button
disabled
type="button"
class="btn ms-1"
v-for="label in source.label_key.split(',')"
:class="sectionTitle"
>
{{ label }}
</button> -->
</div>
<!-- 1 : image -->
<div v-if="source.file_type == 1" class="commentor-text">
<p v-html="parseText(source.text)"></p>
<a
@click.prevent="showLightBox(source.attachment)"
:href="source.attachment.name"
class=""
>
<img
class="img-fluid max-height"
:src="getFilePath(source.attachment.url)"
:alt="source.attachment.name"
/>
</a>
</div>
<!-- 4: file -->
<div v-else-if="source.file_type == 4" class="commentor-text">
<p v-html="parseText(source.text)"></p>
<div class="d-flex align-items-center py-3 px-1">
<span class="file-icon-container">
<svg :class="'icon icon-' + source.attachment.ext + '-file'">
<use
:xlink:href="'#icon-' + source.attachment.ext + '-file'"
></use>
</svg>
</span>
<div class="me-3 flex-grow-1">
<h6>
{{ source.attachment.name }}
</h6>
<p class="m-0">
{{ formatBytes(source.attachment.size) }}
</p>
</div>
<a
:href="getFilePath(source.attachment.url)"
target="_blank"
class="btn btn-outline-dark download-btn"
>
<svg class="icon icon-download">
<use xlink:href="#icon-download"></use>
</svg>
</a>
</div>
</div>
<!-- 2: audio -->
<div v-else-if="source.file_type == 2" class="commentor-text">
<p v-html="parseText(source.text)"></p>
<my-vue-plyr :file="source" :type="'audio'"></my-vue-plyr>
</div>
<!-- 3: video -->
<div v-else-if="source.file_type == 3" class="commentor-text">
<p v-html="parseText(source.text)"></p>
<my-vue-plyr :file="source" :type="'video'"></my-vue-plyr>
</div>
<!-- 0: text -->
<div
v-else-if="source.file_type == 0"
class="commentor-text"
v-html="parseText(source.text)"
></div>
<div class="comment-tags" v-if="source.tags && source.tags.length">
<a
:href="'tags/' + tag"
type="button"
class="btn ms-1"
v-for="tag in source.tags"
>
#{{ tag }}
</a>
</div>
</div>
<div class="comment-footer">
<div
v-if="showCardReplays"
class="replayers"
@click.prevent="openReplays()"
>
<button type="button" class="btn">{{ replaysCount }} پاسخ</button>
<div
class="replayer-avatar"
v-for="(user, index) in source?.replys?.users"
:key="index"
>
<img
:src="userAvatar(user)"
:alt="user.first_name + ' ' + user.last_name"
/>
</div>
<div
class="replayer-avatar all-replays-number"
v-if="userCommentedCount > 3"
>
{{ userCommentedCount }}
</div>
</div>
<div class="comment-date">
{{ convertUnixToPersianDateTime(source.created_at) }}
</div>
</div>
<div class="subject-box" v-if="source.showSubjectBox ?? false">
<div class="subject-list">
<button type="button" class="btn subject-list-item">
{{ subjectInput }}
<!-- edit remove -->
<button type="button" class="btn remove-subject">
<!-- bin icon -->
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</button>
<!-- edit item -->
<button
type="button"
class="btn edit-subject"
@click.prevent="showSubjectForm()"
>
<!-- pen icon -->
<svg class="icon icon-Component-242--1">
<use xlink:href="#icon-Component-242--1"></use>
</svg>
</button>
</button>
<button
type="button"
class="btn close-subject-box"
@click.prevent="closeSubjectBox"
>
<!-- close icon -->
<svg class="icon icon-Component-294--1">
<use xlink:href="#icon-Component-294--1"></use>
</svg>
</button>
</div>
<div class="subject p-3" v-if="showSubjectInput">
<form class="search-form rounded-3 mx-0 w-100" role="search">
<div class="form-group m-0">
<div class="input-group">
<input
v-model="subjectInput"
type="search"
required
class="form-control"
id="subject-form"
placeholder="موضوع را تایپ نمایید."
name="subject-form"
aria-label="ایجاد موضوع جدید"
aria-describedby="basic-addon1"
size="50"
@keyup.exact.enter="saveSubject"
@keyup.exact.escape="hideSubjectForm"
/>
<div class="input-group-append">
<button
@click.prevent="saveSubject"
type="button"
class="input-group-text show-reset-btn"
id="basic-addon1"
>
<!-- tick icon -->
<svg class="icon icon-Component-233--1">
<use xlink:href="#icon-Component-233--1"></use>
</svg>
</button>
</div>
<div class="input-group-append">
<button
@click.prevent="hideSubjectForm"
type="button"
class="input-group-text show-reset-btn"
id="basic-addon1"
>
<!-- check icon -->
<i class="tavasi tavasi-Component-294--1"></i>
</button>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="comment-actions">
<div class="btn-group labeling-dropdown">
<button
type="button"
class="btn"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
title="برچسب زدن"
>
<!-- <svg class="icon icon-title"><use xlink:href="#icon-title"></use></svg> -->
<!-- <svg class="icon icon-label"><use xlink:href="#icon-label"></use></svg> -->
<span class="tavasi tavasi-label"></span>
</button>
<div class="dropdown-menu dropdown-menu-right p-3">
<!-- :class="{ active: source.label_key.includes(label.id) }" -->
<button
v-for="(labelItem, key) in listGetter?.entity_labels"
class="dropdown-item text-end"
type="button"
:key="key"
@click.prevent="filterMessagesByLabel(labelItem)"
>
{{ labelItem.lable }}
</button>
</div>
</div>
<button
title="پاسخ"
@click.prevent="openReplays()"
type="button"
class="btn outer-replay-btn"
>
<span class="tavasi tavasi-reply"></span>
</button>
<context-menu
style="position: static"
:parentComponent="'comments'"
:contextMenu="contextMenu"
@remove-item="deleteItem(source.id)"
@reply-to="openReplays()"
@edit-item="editComment(source)"
@subjecting="openSubjectBox()"
@copy-text="copyText()"
@other="catchAllEvent"
width="12em"
maxHeight="20em"
></context-menu>
</div>
</div>
<vue-easy-lightbox
escDisabled
moveDisabled
:visible="visible"
:imgs="imgs"
:index="index"
@hide="handleHide"
></vue-easy-lightbox>
</div>
</template>
<script>
// import { mapGetters } from "vuex";
import { mapState, mapActions } from "pinia";
import { useChatStore } from "@chat/stores/chatStore";
import { useCommonStore } from "@stores/commonStore";
import privateMessagesContextMenu from "@chat/json/privateMessagesContextMenu.json";
import groupMessagesContextMenu from "@chat/json/groupMessagesContextMenu.json";
import lobbyMessagesContextMenu from "@chat/json/lobbyMessagesContextMenu.json";
// import groupsContextMenu from "@chat/json/groupsContextMenu.json";
export default {
mounted() {
if (this.$route.name == "privates")
this.contextMenu = privateMessagesContextMenu;
if (this.$route.name == "groups")
this.contextMenu = groupMessagesContextMenu;
if (this.$route.name == "lobbies")
this.contextMenu = lobbyMessagesContextMenu;
if (this.$route.name == "unReads") {
if (this.listGetter.icon_type == 1)
this.contextMenu = privateMessagesContextMenu;
else if (this.listGetter.icon_type == 2)
this.contextMenu = groupMessagesContextMenu;
}
// dynamic import : method two
// import(`@chat/json/messagesContextMenu.json`)
// .then((obj) => {
// this.contextMenu = obj.default;
// })
// ;
// this.localComment = structuredClone(this.comment);
},
// watch: {
// comment(newVal) {
// this.localComment = newVal;
// },
// },
props: {
index: {
type: Number,
default: 0,
},
source: {
type: Object,
default() {
return {};
},
},
showSelectionActionBar: {
default: false,
type: Boolean,
},
showCardReplays: {
default: false,
type: Boolean,
},
// comment: {
// default() {
// return {};
// },
// type: Object,
// },
selectedCommentsId: {
default() {
return [];
},
type: Array,
},
replayFrom: {
default: "messages",
},
editFrom: {
default: "isMainEditMode",
},
},
// emits: ["replay-comment", "delete-replay", "edit", "open-replay"],
data() {
return {
contextMenu: undefined,
showSubjectBox: false,
showSubjectInput: false,
subjectInput: "موضوع زنی",
activeLabel: undefined,
commentMarginBoxForSubjecting: 0.5,
imgs: "", // Img Url , string or Array of string
visible: false,
// index: 0, // default: 0,
};
},
computed: {
...mapState(useCommonStore, ["currentUser", "sectionsGetter"]),
...mapState(useChatStore, ["listGetter"]),
updateSelectListAfterCheck: {
get() {
return this.selectedCommentsId;
},
set(val) {
this.dispatch("chat-list", "update-selected-comments-id", val);
// this.$emit("update-selected-comments-id", val);
},
},
sectionItem() {
const section = this.listGetter?.entity_labels?.find(
(item) => item.entity_field_id == this.source.entity_field_id
);
return section;
},
replaysCount() {
if (this.source?.replys) return this.source?.replys?.count;
return 0;
},
userCommentedCount() {
const len = this.source?.replys?.users.length;
return len > 3 ? len - 3 : len;
},
// allReplayers() {
// let newUsers = [];
// if (this.source.replys)
// this.source.replys.users.forEach((user) => {
// this.replayUsers.forEach((item) => {
// if (user == item.user_id) newUsers.push(item);
// });
// });
// return newUsers;
// },
},
methods: {
getFilePath(url) {
return (
process.env.VUE_APP_BASE_URL +
process.env.VUE_APP_API_NAME +
this.fileMicroServiceName +
url
);
},
dispatch(componentName, eventName, ...rest) {
try {
let parent = this.$parent || this.$root;
let name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(rest));
}
} catch (err) {
console.info(err);
}
},
// start: light box methods.
showLightBox(file) {
if (this.isImage(file.ext)) {
// this.imgs = item.url;
// or
this.imgs = [{ title: file.name, src: this.getFilePath(file.url) }];
this.show();
}
},
show() {
this.visible = true;
},
handleHide() {
this.visible = false;
},
// end: light box methods.
getFileExtension(filename) {
return filename?.split(".").pop();
},
isImage(fileName) {
return ["jpg", "jpeg", "png", "webp"].includes(fileName);
},
formatBytes(bytes, decimals = 2) {
if (!+bytes) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
},
getSectionFromJahat() {
// this.$emit("get-section", this.source.entity_field_id);
this.dispatch("chat-list", "get-section", this.source.entity_field_id);
},
showSubjectForm() {
this.commentMarginBoxForSubjecting = 7.5;
this.showSubjectInput = true;
},
hideSubjectForm() {
this.showSubjectInput = false;
this.commentMarginBoxForSubjecting = 3;
},
openSubjectBox() {
this.commentMarginBoxForSubjecting = 3;
this.$set(this.source, "showSubjectBox", true);
},
closeSubjectBox() {
this.commentMarginBoxForSubjecting = 0.5;
this.$set(this.source, "showSubjectBox", false);
},
saveSubject() {},
filterMessagesByLabel(selectedLabel) {
// this.activeLabel = selectedLabel.id;
// this.$emit("save-label", selectedLabel ? selectedLabel.entity_field_id : null);
this.dispatch("chat-list", "save-label", {
selectedLabel: selectedLabel ? selectedLabel.entity_field_id : null,
comment: this.source,
});
},
parseText(text) {
try {
let html = text; //JSON.parse(text);
html = html.replaceAll("\\n", "<br>").replace(/^"*|"*$/g, "");
return html;
} catch (err) {
return text;
}
},
replyComments() {
// this.$emit("replay-comment");
this.dispatch(
"chat-list",
"replay-comment",
this.source,
this.replayFrom,
this.editFrom
);
},
deleteItem() {
const vm = this;
// this.$emit("delete-replay");
this.dispatch(
"chat-list",
"delete-item",
this.source,
this.index,
this.editFrom,
this.replayFrom
);
},
editComment() {
const vm = this;
this.dispatch(
"chat-list",
"edit-item",
this.source,
vm.replayFrom,
vm.editFrom
);
},
openReplays() {
const vm = this;
// if (this.replayFrom == "replays") this.replyComments();
if (this.replayFrom == "replays") return;
else this.dispatch("chat-list", "open-replays", vm.source, vm.index);
},
async copyText() {
let text = null;
try {
text = JSON.parse(this.source.text);
} catch (error) {
text = this.source.text;
}
try {
await navigator.clipboard.writeText(text);
// await navigator.clipboard.writeText(
// location.origin + location.pathname + text
// );
this.mySwalToast({
html: "پیام در کلیپ بورد کپی شد",
});
} catch (err) {
console.info(err);
this.mySwalToast({
html: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
});
}
},
catchAllEvent(action) {
this.dispatch(
"chat-list",
action,
this.source,
this.replayFrom,
this.editFrom
);
// this.$emit(action);
},
},
components: {
ContextMenu: () => import("~/components/global/ContextMenu"),
MyVuePlyr: () => import("@chat/components/chat/components/MyVuePlyr"),
// VueEasyLightbox: () => import("vue-easy-lightbox"),
},
};
</script>
<style scoped lang="scss">
.file-icon-container {
.icon {
font-size: 1.7rem;
}
}
.download-btn {
min-width: 3em;
min-height: 3em;
display: flex;
justify-content: center;
align-items: center;
border-radius: 0.5em;
}
.labeling-dropdown {
.dropdown-item {
&.active {
background-color: #ddd;
}
}
}
.comment-container {
&.subjecting {
margin-bottom: 5em;
transition: all 0.2s;
}
}
.subject-box {
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: #f0f0f0;
background-color: #fff;
border-radius: 0.5em;
margin-top: 0.5em;
.subject-list {
position: relative;
.subject-list-item {
position: relative;
padding-right: 1.5em;
padding-left: 1.5em;
&:hover {
.edit-subject,
.remove-subject {
visibility: visible;
transition: all 0.2s;
}
}
.edit-subject {
position: absolute;
right: 0;
font-size: 0.7rem;
padding: 0.2em;
// opacity: 0.5;
// background-color: #fff;
// border-radius: 50%;
overflow: hidden;
display: flex;
justify-content: center;
top: 0;
bottom: 0;
height: auto;
transition: all 0.2s;
visibility: hidden;
color: #17a2b8;
&:hover {
background-color: #d8e7e9;
color: #17a2b8;
width: 70%;
border-radius: 0 0.5em 0.5em 0;
transition: all 0.2s;
opacity: 1;
z-index: 1;
}
}
.remove-subject {
top: 0;
bottom: 0;
height: auto;
font-size: 0.7rem;
padding: 0.2em;
position: absolute;
left: 0;
// opacity: 0.5;
// background-color: #fff;
// border-radius: 50%;
overflow: hidden;
display: flex;
justify-content: center;
transition: all 0.2s;
visibility: hidden;
color: #dc3545;
&:hover {
background-color: #ecd9db;
color: #dc3545;
width: 70%;
border-radius: 0.5em 0 0 0.5em;
transition: all 0.2s;
opacity: 1;
z-index: 1;
}
}
}
.close-subject-box {
font-size: 0.8rem;
position: absolute;
left: 0;
}
}
.subject {
.search-form {
border-radius: 0.5em;
* {
font-size: 0.8rem !important;
}
.input-group-append {
.input-group-text {
border-radius: 0;
}
}
}
}
}
</style>
<style lang="scss">
.chat-list-item-component {
.dropdown-menu {
max-width: 12m;
.dropdown-item {
font-size: 0.8rem;
padding: 0.2em 0;
}
}
}
#replays {
.comment-actions {
.outer-replay-btn {
display: none !important;
}
}
}
</style>

View File

@ -331,14 +331,14 @@
<img
class="group-picture"
@error="handleImageSrcOnError($event, false)"
:src="getGroupAvatar(groupItem)"
src="~/assets/common/img/icomoon/PNG/eye.png"
:alt="groupItem.title"
/>
<img
class="group-picture mini-mode"
@error="handleImageSrcOnError($event, false)"
@click.prevent="showMessages(groups, groupItem, index)"
:src="getGroupAvatar(groupItem)"
src="~/assets/common/img/icomoon/PNG/eye.png"
:alt="groupItem.title"
/>
</div>
@ -411,14 +411,14 @@
<img
class="group-picture"
@error="handleImageSrcOnError($event, false)"
:src="getGroupAvatar(groupItem)"
src="~/assets/common/img/icomoon/PNG/eye.png"
:alt="groupItem.title"
/>
<img
class="group-picture mini-mode"
@error="handleImageSrcOnError($event, false)"
@click.prevent="showMessages(groups, groupItem, index)"
:src="getGroupAvatar(groupItem)"
src="~/assets/common/img/icomoon/PNG/eye.png"
:alt="groupItem.title"
/>
</div>
@ -492,14 +492,14 @@
<img
class="group-picture"
@error="handleImageSrcOnError($event, false)"
:src="getGroupAvatar(groupItem)"
src="~/assets/common/img/icomoon/PNG/eye.png"
:alt="groupItem.title"
/>
<img
class="group-picture mini-mode"
@error="handleImageSrcOnError($event, false)"
@click.prevent="showMessages(groups, groupItem, index)"
:src="getGroupAvatar(groupItem)"
src="~/assets/common/img/icomoon/PNG/eye.png"
:alt="groupItem.title"
/>
</div>
@ -1279,13 +1279,13 @@
import chatApi from "@chat/apis/chatApi";
import { mapState, mapActions } from "pinia";
import privatesContextMenu from "@chat/json/chat/json/privatesContextMenu.json";
import groupsContextMenu from "@chat/json/chat/json/groupsContextMenu.json";
import privatesContextMenu from "@chat/json/privatesContextMenu.json";
import groupsContextMenu from "@chat/json/groupsContextMenu.json";
import lobbiesChildContextMenu from "@chat/json/chat/json/lobbiesChildContextMenu.json";
import lobbiesParentContextMenu from "@chat/json/chat/json/lobbiesParentContextMenu.json";
import lobbiesChildContextMenu from "@chat/json/lobbiesChildContextMenu.json";
import lobbiesParentContextMenu from "@chat/json/lobbiesParentContextMenu.json";
import menu from "@chat/json/chat/json/menu.json";
import menu from "@chat/json/menu.json";
import { useCommonStore } from "@stores/commonStore";
import { useChatStore } from "@chat/stores/chatStore";
@ -1308,7 +1308,7 @@ export default {
$route: {
handler: function (to) {
if (this.getForwardItem) {
this.groupId = this.getForwardItem?.to.id;
this.groupId = this.getForwardItem?.to?.id;
}
this.isRedirectedFromOtherSystems = false;
this.resetPagination();
@ -1590,6 +1590,33 @@ export default {
},
methods: {
...mapActions(useChatStore, ["SET_LIST", "SET_SIDEBAR_LIST_STATUS"]),
async getGroupAvatar(user = undefined) {
const res = (await import("~/assets/common/img/icomoon/PNG/eye.png"))
.default;
console.info(res);
try {
if (user) {
if (isValidHttpUrl(user.avatar)) return user.avatar;
else {
if ("title" in user) {
const nameArray = user.title?.split(" ");
if (nameArray?.length > 1) {
const initials =
nameArray[0].charAt(0) + nameArray[1].charAt(0);
return generateAvatarFromChars(initials);
} else {
const initials = nameArray[0].charAt(0);
return generateAvatarFromChars(initials);
}
} else
return (await import("~/assets/common/img/default.svg")).default;
}
} else return (await import("~/assets/common/img/default.svg")).default;
} catch (err) {
return (await import("~/assets/common/img/default.svg")).default;
}
},
handleImageSrcOnError({ target }, isUserAvatar = true) {
if (isUserAvatar) target.classList.add("human-avatar");
target.classList.add("error");
@ -2030,11 +2057,11 @@ export default {
markActive(groups, index) {
try {
if (this.prevSelectedItemIndex != index) {
this.$set(groups[this.prevSelectedItemIndex], "active", false);
groups[this.prevSelectedItemIndex], "active", false;
}
this.prevSelectedItemIndex = index;
this.$set(groups[index], "active", true);
groups[index], "active", true;
} catch (err) {
this.prevSelectedItemIndex = undefined;
}
@ -2060,8 +2087,8 @@ export default {
// برای دریافت درست پاسخ ها بایستی invite-id
// در روت رو اصلاح کرد
if (groupItem.redirectViaInviteLink)
this.$set(this.$route.query, "invite-id", this.listGetter?.invite_id);
else this.$set(this.$route.query, "invite-id", undefined);
this.$route.query, "invite-id", this.listGetter?.invite_id;
else this.$route.query, "invite-id", undefined;
if (!this.redirectViaInviteLink)
history.pushState(
@ -2072,8 +2099,8 @@ export default {
this.isRedirectedFromOtherSystems = false;
this.redirectViaInviteLink = false;
this.$set(this.$route.query, "group-id", undefined);
this.$set(this.$route.query, "message-id", undefined);
this.$route.query, "group-id", undefined;
this.$route.query, "message-id", undefined;
// this.$route.query["group-id"] = undefined;
// this.$route.query["message-id"] = undefined;
if (groupItem.type == 0)
@ -2089,28 +2116,24 @@ export default {
lobbyParentShowMessages(lobby, parentIndex, groupItem) {
// اگر تالارثابت باشه و کاربر بر روی گروه های تالار کلیک کند.
if (this.prevParentSelectedItemIndex == parentIndex) {
this.$set(
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
"active",
false
);
false;
}
// کاربر بر روی تالار دیگری کلیک کرده است.
else if (this.prevParentSelectedItemIndex != parentIndex) {
this.$set(
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
"active",
false
);
false;
}
this.prevParentSelectedItemIndex = parentIndex;
this.$set(lobby[parentIndex], "active", true);
lobby[parentIndex], "active", true;
this.SET_LIST(groupItem);
this.$emit("update-messages");
@ -2123,29 +2146,25 @@ export default {
this.prevParentSelectedItemIndex == parentIndex &&
this.prevSelectedItemIndex != index
) {
this.$set(
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
"active",
false
);
false;
}
// کاربر بر روی تالار دیگری کلیک کرده است.
else if (this.prevParentSelectedItemIndex != parentIndex) {
this.$set(
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
lobby[this.prevParentSelectedItemIndex].sub_groups[
this.prevSelectedItemIndex
],
"active",
false
);
false;
}
this.prevSelectedItemIndex = index;
this.prevParentSelectedItemIndex = parentIndex;
this.$set(lobby[parentIndex].sub_groups[index], "active", true);
lobby[parentIndex].sub_groups[index], "active", true;
this.SET_LIST(groupItem);
this.$emit("update-messages");
@ -2437,7 +2456,7 @@ export default {
.postRequest(url, payload)
.then((res) => {
// this.$route.query["invite-id"] = undefined;
this.$set(this.$route.query, "invite-id", undefined);
this.$route.query, "invite-id", undefined;
this.groupId = res.data.id;
this.resetPagination();

View File

@ -0,0 +1,159 @@
<template>
<div class="mt-2" id="myVid">
<!-- video element -->
<!-- <vue-plyr :options="options">
<video controls crossorigin playsinline data-poster="poster.jpg">
<source size="720" src="/path/to/video-720p.mp4" type="video/mp4" />
<source size="1080" src="/path/to/video-1080p.mp4" type="video/mp4" />
<track
default
kind="captions"
label="English captions"
src="/path/to/english.vtt"
srclang="en"
/>
</video>
</vue-plyr> -->
<!-- audio element -->
<vue-plyr ref="plyr" :options="options">
<video v-if="type == 'video'" controls crossorigin playsinline>
<source
size="720"
:src="file.attachment.url"
:type="'video/' + file.attachment.ext"
/>
</video>
<audio v-else controls crossorigin playsinline data-poster="poster.jpg">
<source
:src="file.attachment.url"
:type="'audio/' + file.attachment.ext"
/>
</audio>
</vue-plyr>
<p class="my-2">
{{ file.attachment.name }}
</p>
<p class="my-2">
{{ formatBytes(file.attachment.length) }}
</p>
</div>
</template>
<script>
/**
* @vue-prop {String} [type] - نوع فایل
* @vue-prop {Object} [file] - فایل
*
* @vue-data {undefined} [canvas=undefined] - یک محیط توسعه وب برای رسم شکلها، تصاویر، ویدیوها و سایر عناصر گرافیکی است.
* @vue-data {Object} [options] - گزینههای ویدیو پلیر
* @vue-data {Array} [options.controls] - کنترلهای ویدیو پلیر
* @vue-data {Array} [options.controls=["play", "progress", "current-time", "mute", "volume", "settings", "download"]] - کنترلهای ویدیو پلیر (پخش، پیشرفت، زمان فعلی، بیصدا، میزان صدا، تنظیمات، دانلود)
* @vue-data {Object} [options.speed] - سرعت پخش ویدیو
* @vue-data {Number} [options.speed.selected=1] - سرعت پخش انتخاب شده
* @vue-data {Array} [options.speed.options=[0.75, 1, 1.5, 2]] - گزینههای سرعت پخش ویدیو
*/
export default {
props: ["type", "file"],
data() {
return {
canvas: undefined,
options: {
controls: [
// "play-large", // The large play button in the center
//'restart', // Restart playback
// "rewind", // Rewind by the seek time (default 10 seconds)
"play", // Play/pause playback
// "fast-forward", // Fast forward by the seek time (default 10 seconds)
"progress", // The progress bar and scrubber for playback and buffering
"current-time", // The current time of playback
// "duration", // The full duration of the media
"mute", // Toggle mute
"volume", // Volume control
// "captions", // Toggle captions
"settings", // Settings menu
// "pip", // Picture-in-picture (currently Safari only)
// "airplay", // Airplay (currently Safari only)
"download", // Show a download button with a link to either the current source or a custom URL you specify in your options
// "fullscreen", // Toggle fullscreen
],
speed: { selected: 1, options: [0.75, 1, 1.5, 2] },
},
};
},
methods: {
/**
* گرفتن عکس از ویدیو.
* این متد یک عکس از ویدیویی که در آن زمان مشخص شده است، گرفته و آن را به عنوان پوستر ویدیو تنظیم میکند.
*/
captureSnapshot() {
// var video = this.$refs.mediaPlayer;
// this.canvas = document.getElementById("snapshotCanvas");
// var context = canvas.getContext("2d");
// canvas.width = video.videoWidth;
// canvas.height = video.videoHeight;
// video.currentTime = 10;
// context.drawImage(video, 0, 0, canvas.width, canvas.height);
var container = document.getElementById("myVid"),
video = document.createElement("video"),
canCapture = true;
if (!video.canPlayType("video/wmv")) {
/* If you don't have multiple sources, you can load up a Flash fallback here
(like jPlayer), but you won't be able to capture frames */
canCapture = false;
return;
}
video.src = this.file.attachment.url;
video.currentTime = "10.0";
container.appendChild(video);
video.play(); //or put this in a button click handler if you want your own controls
var canvas = document.createElement("canvas");
canvas.width = 640;
canvas.height = 480;
var ctx = canvas.getContext("2d");
// if you want to preview the captured image,
// attach the canvas to the DOM somewhere you can see it.
//draw image to canvas. scale to target dimensions
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
//convert to desired file format
this.$refs.plyr.player.poster = canvas.toDataURL("image/jpeg"); // can also use 'image/png'
},
/**
* فرمت کردن حجم فایل.
* این متد حجم فایل را به صورت خوانا و فرمتداده شده با واحدهای مناسب برمیگرداند.
*
* @param {number} bytes - حجم فایل به بایت
* @param {number} [decimals=2] - تعداد اعشار برای نمایش
* @returns {string} - حجم فایل به صورت فرمتداده شده
*/
formatBytes(bytes, decimals = 2) {
if (!+bytes) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
},
},
mounted() {
// this.captureSnapshot()
},
};
</script>
<style scoped></style>

View File

@ -125,8 +125,8 @@
<script>
import { mapState } from "pinia";
import { entityMixin } from "~/chat/mixins/entityMixin";
import { commonMixin } from "~/chat/mixins/commonMixin";
import { entityMixin } from "@chat/mixins/entityMixin";
import commonMixin from "@chat/mixins/commonMixin";
import repoApi from "~/apis/repoApi";
export default {

View File

@ -149,8 +149,8 @@
<script>
import { mapState } from "pinia";
import { propertyMixin } from "~/chat/mixins/propertyMixin";
import { commonMixin } from "~/chat/mixins/commonMixin";
import { entityMixin } from "~/chat/mixins/entityMixin";
import commonMixin from "@chat/mixins/commonMixin";
import { entityMixin } from "@chat/mixins/entityMixin";
export default {
mixins: [propertyMixin, commonMixin, entityMixin],

View File

@ -76,8 +76,8 @@
</template>
<script>
import { propertyMixin } from "~/chat/mixins/propertyMixin";
import { commonMixin } from "~/chat/mixins/commonMixin";
import { propertyMixin } from "@chat/mixins/propertyMixin";
import commonMixin from "@chat/mixins/commonMixin";
export default {
mixins: [propertyMixin, commonMixin],

View File

@ -117,8 +117,8 @@
</template>
<script>
import { propertyMixin } from "~/chat/mixins/propertyMixin";
import { commonMixin } from "~/chat/mixins/commonMixin";
import { propertyMixin } from "@chat/chat/mixins/propertyMixin";
import commonMixin from "@chat/mixins/commonMixin";
import { mapState } from "pinia";

View File

@ -115,8 +115,8 @@
</div>
</template>
<script>
import { entityMixin } from "~/chat/mixins/entityMixin";
import { commonMixin } from "~/chat/mixins/commonMixin";
import { entityMixin } from "@chat/mixins/entityMixin";
import commonMixin from "@chat/mixins/commonMixin";
export default {
props: {

View File

@ -63,8 +63,8 @@
</template>
<script>
import { propertyMixin } from "~/chat/mixins/propertyMixin";
import { commonMixin } from "~/chat/mixins/commonMixin";
import { propertyMixin } from "@chat/mixins/propertyMixin";
import commonMixin from "@chat/mixins/commonMixin";
export default {
mixins: [propertyMixin, commonMixin],

377
mixins/commonMixin.js Normal file
View File

@ -0,0 +1,377 @@
// import HttpService from "~/services/httpService";
import chatApi from "@chat/apis/chatApi";
// import repoApi from "~/apis/repoApi";
export default {
beforeMount() {
this.httpService = useNuxtApp()["$http"];
},
methods: {
closeList() {
this.SET_SIDEBAR_LIST_STATUS();
// this.footerAction = actionName;
this.menubarStatus = !this.menubarStatus;
// this.$root.$emit("show-search", this.menubarStatus);
},
initServices() {
// const headers = {
// "app-id": import.meta.env.VITE_APP_ID,
// lang: import.meta.env.VITE_LANG,
// "app-version-code": import.meta.env.VITE_APP_VERSION,
// };
this.fileUploadHttpService = new HttpService(
import.meta.env.VITE_MESSAGE_BASE_URL,
{
"Content-Type": "multipart/form-data",
}
);
// this.issueHttpService = new HttpService(
// import.meta.env.VITE_REPO_BASE_URL + "jahat/"
// );
this.httpService = new HttpService(
import.meta.env.VITE_BASE_URL
// headers
);
// this.keyValueHttpService = new HttpService(
// import.meta.env.VITE_KEY_VALUE_BASE_URL
// );
// this.messageHttpService = new HttpService(
// import.meta.env.VITE_MESSAGE_BASE_URL
// );
},
openAuthModal() {
if (!this.isGuest && this.listGetter?.show_joined) {
this.$root.$emit(
"authenticated-by-modal",
this.$route.query["invite-id"]
);
}
// fired event cached in the Group.vue and ChatList.vue
else this.showAuthModal = true;
},
closeAuthModal() {
this.showAuthModal = false;
},
getFileExtension(filename) {
return filename?.split("/").pop();
},
formatBytes(bytes, decimals = 2) {
if (!+bytes) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
},
async handlePasteEvent(e) {
this.uploadDescription = null;
// navigator is not supported in the firefox.
if (!navigator.userAgent.includes("Firefox/")) {
e.preventDefault();
// catching the pasted text and place it into the comment form.
navigator.clipboard
.readText()
.then((text) => {
if (this.showReplays) {
this.mySwalConfirm({
icon: "info",
title: "جایگزاری در فرم پیام ها؟",
html: "",
text: "متن کپی شده در فرم اصلی جایگزاری شود یا فرم پاسخ ها؟",
confirmButtonText: "بله",
cancelButtonText: "خیر",
didOpen: (toast) => {},
showCloseButton: true,
willClose: () => {
},
didClose() {
},
}).then((result) => {
if (result.isConfirmed) {
if (this.userMessage?.length) {
this.userMessage += " " + text;
} else {
this.userMessage = text;
}
// this.userMessage = this.userMessage
// ? this.userMessage + text
// : text;
} else if (result.isDismissed && result.dismiss == "cancel") {
if (this.replayText?.length) {
this.replayText += " " + text;
} else {
this.replayText = text;
}
}
// this.replayText = this.replayText
// ? this.replayText + text
// : text;
});
// const res = confirm("جایگزاری در فرم پیام ها؟");
// if (res) this.userMessage = text;
// else this.replayText = text;
} else {
if (this.userMessage?.length) {
this.userMessage += " " + text;
} else {
this.userMessage = text;
}
}
// this.userMessage = this.userMessage
// ? this.userMessage + text
// : text;
})
.catch((err) => {
console.error("Failed to read clipboard contents: ", err);
});
}
const clipboardItems =
typeof navigator?.clipboard?.read === "function"
? await navigator.clipboard.read()
: e.clipboardData.files;
for (const clipboardItem of clipboardItems) {
let blob;
if (clipboardItem.type?.startsWith("image/")) {
let fileType = clipboardItem?.type?.split("/")[1];
// For files from `e.clipboardData.files`.
blob = clipboardItem;
this.files = [];
this.files.push(new File([blob], fileType));
// this.files.push(blob);
this.showDroppedList = true;
this.dropBoxClass = "messages";
} else {
// For files from `navigator.clipboard.read()`.
const imageTypes = clipboardItem.types?.filter((type) =>
type.startsWith("image/")
);
for (const imageType of imageTypes) {
let fileType = imageType.split("/")[1];
blob = await clipboardItem.getType(imageType);
this.files = [];
this.files.push(new File([blob], fileType));
this.showDroppedList = true;
this.dropBoxClass = "messages";
}
}
}
// 0: "image/png"
// 0: "text/plain"
// 1: "text/html"
},
checkPermisionBeforGetList() {
this.checkPermissions({
permission: `${this.$route.name}_list`,
_this: this,
})
.then(() => {
this.toLastItem();
this.canView = true;
})
.catch(() => {
this.canView = false;
});
},
replaceTextWithLink(text) {
const clonedText = text;
try {
const Rexp =
/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g;
// Replace the RegExp content by HTML element
return text.replace(
Rexp,
`<a href="${clonedText}" title="${clonedText}" target='_blank'>${clonedText}</a>`
);
} catch (err) {
return text;
}
},
saveLabel(selectedLabel, comment) {
if (this.savingComment) return;
this.savingComment = true;
let payload = {
group_id: this.listGetter.id,
id: comment.id,
entity_field_id: selectedLabel,
};
let url = "message/" + chatApi.messages.create;
this.httpService
.formDataRequest(url, payload)
.then((res) => {
comment.entity_field_id = selectedLabel;
// this.getMessages();
})
.catch((err) => {})
.finally(() => {
this.savingComment = false;
});
},
getMids(messages) {
return messages.map((message) => message.id);
},
showfilters() {
this.$emit("my-event", (this.statusPage = 1));
},
closefilter() {
this.showFilter = false;
},
groupDescription(text) {
let res = "";
try {
res = JSON.parse(text);
} catch (err) {
res = text;
}
return res;
},
showMessageSearch() {
// this.SET_SIDEBAR_LIST_STATUS(false);
this.$root.$emit("show-search", true);
// this.$emit("show-search");
},
async onResult(data) {
// در صورت استفاده از پلاگین ،برای متوقف کردن
// علامت ضبط صدا از روی تب، بایستی از این کدها استفاده شود.
// const audioTracks = this.mediaStream.getAudioTracks();
// audioTracks.forEach((element) => {
// element.stop();
// });
this.files = [];
this.files.push(new File([data], "mp3"));
// if (this.abortRecord) return;
// this.abortRecord = true;
this.mainSaveFiles().then(() => {
// this.recording = false;
// this.abortRecord = false;
});
},
async makeLink(comment) {
const params = new URLSearchParams({
["group-id"]: this.listGetter.id,
["message-id"]: comment.id,
});
let link = location.origin + location.pathname + "?" + params.toString();
link += "#chat-item-" + comment.id;
try {
await navigator.clipboard.writeText(link);
this.mySwalToast({
html: "لینک کپی شد.",
});
} catch (err) {
this.mySwalToast({
html: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
});
}
},
updateMessageCount() {
const currentUserModel = {
avatar: this.currentUser.user_data.avatar,
color: 40080,
first_name: this.currentUser.user_data.avatar,
full_name: this.currentUser.user_data.avatar,
id: this.currentUser.user_id,
last_name: this.currentUser.user_data.last_name,
user_id: this.currentUser.user_id,
username: this.currentUser.user_data.username,
};
// finding the target comment by id.
this.localComments.forEach((com) => {
// if comment id is equal to the comment user want to replay.
if (com.id == this.replayTo.id) {
// increment the comment count.
this.$set(com.replys, "count", ++com.replys.count);
// if comment has users, then
if (com.replys.users.length) {
// finding the index of the user by current user id.
const index = com.replys.users.find(
(user) => user.user_id == this.currentUser.user_id
);
// if current user didn't exist, then push it to comments users array.
if (index == -1) com.replys.users.push(currentUserModel);
} else com.replys.users.push(currentUserModel);
}
// com.replys.count++;
});
// let payload = {
// group_id: this.listGetter.id,
// contact_id:
// this.$route.name == "privates" ? this.listGetter.user : undefined,
// ...this.pagination,
// };
// this.httpService
// .postRequest(chatApi.messages.virtualList, payload)
// .then((response) => {
// if (response.data && response.data.length) {
// // this.replayUsers = structuredClone(response.users);
// this.localComments = response.data;
// this.setUsers(this.localComments, response.users);
// }
// });
},
setUsers(list, responseUsers) {
list.forEach((listItem) => {
listItem.user = responseUsers.find(
(user) => user.user_id == listItem.user
);
if (listItem.replys)
listItem.replys.users = this.allReplayers(listItem, responseUsers);
});
},
limitText(count) {
return `و ${count} کاربر دیگر`;
},
parseText(text) {
try {
return JSON.parse(text);
} catch (err) {
return text;
}
},
resetPagination() {
this.pagination = {
page: 0,
total: 0,
pageTop: 0,
pageBottom: 1,
seenDate: null,
};
},
},
};

54
mixins/createdMixin.js Normal file
View File

@ -0,0 +1,54 @@
export default {
created() {
const { $eventBus } = useNuxtApp();
$eventBus.on("replay-comment", (comment, editFrom, replayFrom) => {
this.replyComments(comment, editFrom, replayFrom);
});
// detecting change checked value from item component event.
$eventBus.on("open-replays", (comment, index) => {
this.openReplays(comment, index);
});
// detecting change checked value from item component event.
$eventBus.on("edit-item", (comment, editFrom, replayFrom) => {
this.editComment(comment, editFrom, replayFrom);
});
// detecting change checked value from item component event.
$eventBus.on("delete-item", (comment, index, editFrom, replayFrom) => {
this.deleteItem(comment, index, editFrom, replayFrom);
});
// detecting change checked value from item component event.
$eventBus.on("update-selected-comments-id", ($event) => {
this.selectedCommentsId = $event;
});
// detecting change checked value from item component event.
$eventBus.on("select-many", () => {
this.showSelectionActionBar = true;
});
// detecting change checked value from item component event.
$eventBus.on("save-label", (data) => {
this.saveLabel(data.selectedLabel, data.comment);
});
// detecting change checked value from item component event.
$eventBus.on("get-section", ($event, comment) => {
this.getSectionDetails($event, comment);
});
// detecting change checked value from item component event.
$eventBus.on("make-message-link", (comment) => {
this.makeLink(comment);
});
// detecting change checked value from item component event.
$eventBus.on("forward-to", (comment) => {
this.openForwardModal(comment);
});
// fired from group.vue ->addItem()
$eventBus.on("show-auth-modal", () => {
this.openAuthModal();
});
// // fired from chat MobileFooter.vue.
$eventBus.on("open-message-actions-list", (value) => {
this.showMobileActions = !this.showMobileActions;
});
},
};

146
mixins/fileMixin.js Normal file
View File

@ -0,0 +1,146 @@
import chatApi from "~/apis/chatApi";
import repoApi from "~/apis/repoApi";
export default {
methods: {
removeFile(file, index) {
this.files.splice(index, 1);
this.filenames.splice(index, 1);
if (this.files.length == 0) this.showDroppedList = false;
},
async mainSaveFiles() {
if (this.uploading) return;
this.uploading = true;
if (this.saveAs == "replay") {
this.replaySaveFiles();
} else {
let payload = {
text: this.uploadDescription,
group_id: this.listGetter.id,
id: this.mainCommentor?.id ?? undefined,
contact_id: undefined,
replyto: undefined,
};
if (this.$route.name == "privates") {
payload = {
text: this.uploadDescription,
id: this.mainCommentor?.id ?? undefined,
contact_id: this.listGetter.user,
};
}
let url = chatApi.messages.create;
if (this.files.length > 1) {
this.files.forEach((fileItem) => {
payload.file = fileItem;
payload.length = fileItem.size;
this.fileUploadHttpService
.formDataRequest(url, payload)
.then((res) => {
// this.closeDroppedList();
// this.toLastItem();
})
.catch((err) => {
this.mySwalToast({
html: "حداکثر حجم فایل برای ارسال 50 مگابایت می باشد.",
icon: "error",
});
})
.finally(() => {
this.uploading = false;
});
});
this.userMessage = null;
this.uploadDescription = null;
this.closeDroppedList();
this.toLastItem();
} else {
// 0: files, 1: image, 2:audio 3:video
// payload.file_type = this.getFileType(this.files[0].name);
payload.file = this.files[0];
payload.length = this.files[0].size;
return await this.fileUploadHttpService
.formDataRequest(url, payload)
.then((res) => {
this.closeDroppedList();
// when clicking on the attach file btn.
this.userMessage = null;
this.uploadDescription = null;
this.closeDroppedList();
this.toLastItem();
})
.catch((err) => {
this.mySwalToast({
html: "حداکثر حجم فایل برای ارسال 50 مگابایت می باشد.",
icon: "error",
});
})
.finally(() => {
this.uploading = false;
});
}
}
},
endOfUploading() {
},
startUploading() {
},
openUploadForm(dropBoxClass) {
this.files = [];
this.uploadDescription = null;
this.showDroppedList = true;
this.dropBoxClass = dropBoxClass;
this.showMobileUploadButton = true;
},
setImage(file) {
this.hasImage = true;
this.files.push(file);
},
getFileType(fileName) {
if (
["jpg", "jpeg", "png", "webp"].includes(this.getFileExtension(fileName))
)
return 1;
if (
["mp3", "wav", "png", "webp"].includes(this.getFileExtension(fileName))
)
return 2;
if (["mp4", "mpeg", "mkv"].includes(this.getFileExtension(fileName)))
return 3;
return 0;
},
fileIcon(type) {
if (type == "text/plain") return "txt-file";
else if (type == "pdf-file") return "pdf-file";
else if (type == "doc-file") return "doc-file";
else if (type == "docx-file") return "docx-file";
else if (type == "excel-file") return "excel-file";
else if (type == "csv-file") return "csv-file";
else if (type == "word-file") return "word-file";
else if (type == "ppt-file") return "ppt-file";
else if (type == "mp3-file") return "mp3-file";
else if (type == "jpg-file") return "jpg-file";
else if (type == "png-file") return "png-file";
else if (type == "mov-file") return "mov-file";
else if (type == "rar-file") return "rar-file";
else if (type == "zip-file") return "zip-file";
else return "other-file";
},
closeDroppedList() {
this.files = [];
this.showDroppedList = false;
this.pasting = false;
this.showMobileUploadButton = false;
this.uploadDescription = null;
},
},
};

98
mixins/forwardMixin.js Normal file
View File

@ -0,0 +1,98 @@
import chatApi from "~/apis/chatApi";
import repoApi from "~/apis/repoApi";
export default {
methods: {
async saveForward() {
let payload = {
group_id: this.getForwardItem.to.id,
forward_from: this.getForwardItem?.message.id,
};
// private route condition
if (this.$route.name == "privates") {
payload = {
// contact_id: this.getForwardItem.user.user_id,
contact_id: this.getForwardItem.to.user_id,
forward_from: this.getForwardItem?.message.id,
};
}
let url = "message/" + chatApi.messages.create;
return await this.httpService.formDataRequest(url, payload);
},
forwardTo(user) {
this.save(user);
},
openForwardModal(comment) {
const forwardItem = {
from: this.listGetter,
to: {},
message: comment,
};
// comment.group_forward_from_id = this.listGetter.id;
// comment.group_forward_from_title = this.listGetter.title;
this.SET_FORWARD_ITEM(forwardItem);
this.showForwardModal = true;
this.modalComponentName = "user-search";
setTimeout(() => {
$("#base-modal").modal({
backdrop: "static",
keyboard: false,
show: true,
});
}, 200);
},
redirectToPrivates(routeName) {
this.closeForwardModal();
setTimeout(() => {
this.$router
.push({
name: routeName,
})
.catch((err) => {
});
}, 500);
// if (this.getForwardItem.type == 'Private') {
// this.$router
// .push({
// name: routeName,
// meta: {
// "user-id": this.getForwardItem?.message.user.id,
// "message-id": this.getForwardItem?.message.id,
// },
// })
// .then(() => {}).catch(()=>{})
// } else if (this.getForwardItem.type == "Group") {
// this.$router
// .push({
// name: routeName,
// })
// .then(() => {}).catch(()=>{})
// }
},
closeForwardModal() {
$("#base-modal").modal("hide");
setTimeout(() => {
this.showForwardModal = false;
}, 500);
},
closeAndResetForwardModal() {
this.SET_FORWARD_ITEM();
$("#base-modal").modal("hide");
setTimeout(() => {
this.showForwardModal = false;
}, 500);
},
},
};

119
mixins/headerMixin.js Normal file
View File

@ -0,0 +1,119 @@
import chatApi from "@chat/apis/chatApi";
// import repoApi from "~/apis/repoApi";
// import HttpService from "~/services/httpService";
export default {
beforeMount() {
this.httpService = useNuxtApp()["$http"];
},
methods: {
selectAllCheckbox(val) {
if (this.checked)
this.selectedCommentsId = this.localComments.map((item) => item.id);
else this.selectedCommentsId = [];
},
removeFromLabelFilter() {
// array(multiple) mode
// const index = this.labelFilter.findIndex(item=>item.id = selectedItem.id)
// this.labelFilter.splice(index,1)
// single mode
this.selectedLabel = null;
this.selectedMember = null;
this.selectedMessageLabel = [];
this.localComments = [];
this.showMobileActions = false;
this.selectedGroupMember = [];
this.selectedMessageLabel = [];
this.toLastItem();
},
removeFromUserFilter() {
// array(multiple) mode
// const index = this.userFilter.findIndex(item=>item.id = selectedItem.id)
// this.userFilter.splice(index,1)
// single mode
this.selectedMember = null;
this.selectedLabel = null;
this.selectedMessageLabel = [];
this.localComments = [];
this.showMobileActions = false;
this.selectedGroupMember = [];
this.selectedMessageLabel = [];
this.toLastItem();
},
getGroupMembers() {
if (
this.$route.name !== "privates" &&
(this.listGetter?.group_id || this.listGetter?.id)
)
this.httpService
.getRequest(
"message/" +
chatApi.groups.groupMembers +
(this.listGetter?.group_id || this.listGetter?.id)
)
.then((response) => (this.groupMember = response.data));
},
filterMessagesBySection(selectedLabel) {
// this.resetPagination();
// this.userFilter = [];
this.selectedMember = null;
this.showMobileActions = false;
this.selectedGroupMember = [];
this.selectedMessageLabel = [];
if (selectedLabel) {
this.selectedMessageLabel = [selectedLabel.entity_field_id];
selectedLabel["title"] = selectedLabel?.lable;
selectedLabel["keyName"] = "label";
}
// this.selectedMessageLabel = this.selectedLabel.map(
// (item) => item.entity_field_id
// );
let entityId = this.listGetter?.reference_id;
if (entityId) {
this.getIssue(entityId).then(() => {
this.getSections().then(() => {
if (selectedLabel) this.showIssueProperties = true;
else this.closeIssueProperties();
this.localComments = [];
this.toLastItem();
});
});
} else {
this.localComments = [];
this.closeIssueProperties();
this.toLastItem();
}
},
filterMessagesByUser(selectedUser) {
// this.resetPagination();
// labelFilter.length || userFilter.length"
// this.labelFilter = [];
this.selectedLabel = null;
this.selectedMessageLabel = [];
selectedUser["title"] = selectedUser?.full_name ?? '';
selectedUser["keyName"] = "user";
this.selectedGroupMember = selectedUser ? [selectedUser?.user_id] : [];
this.localComments = [];
this.showMobileActions = false;
this.toLastItem();
},
getSectionDetails() {
let url =
"message/" + chatApi.entity.get + "/" + this.listGetter.reference_id;
this.httpService.getRequest(url).then((res) => {
this.issue = res._source;
});
},
},
};

77
mixins/jahatMixin.js Normal file
View File

@ -0,0 +1,77 @@
// import { getLsItem, setLsItem } from "~/services/localStorage.service.js";
// import chatApi from "~/apis/chatApi";
import { useStorage } from "@vueuse/core";
import repoApi from "~/apis/repoApi";
export default {
methods: {
entityFieldsGetter(entity_field_id) {
return this.entityFields[entity_field_id][2];
},
async getSections() {
// let localStoageSections = getLsItem("sections_" + 2);
let localStorageSections = useStorage("sections_" + 2, []);
if (localStoageSections) {
this.issueSections = JSON.parse(localStoageSections);
return;
}
let payload = {
// entity_id: 2, // 2 : means issues
type_id: 2,
};
// let url = repoApi.entityFields.list;
let url = "keyvalue/" + repoApi.entityFields.list;
return await this.httpService
.postRequest(url, payload)
.then((res) => {
this.issueSections = res.data;
// save res to local storage.
const sections = useStorage("sections_" + 2, []);
sections.value = JSON.stringify(res.data);
// setLsItem("sections_" + 2, JSON.stringify(res.data));
})
.catch((err) => {})
.finally(() => {});
},
async getIssue(entityId) {
let url = "repo/jahat/get/" + entityId;
return await this.httpService.getRequest(url).then((res) => {
this.issue = res._source;
});
},
closeIssueProperties() {
this.showIssueProperties = false;
},
goToJahat() {
this.SET_USER_PERMISSIONS();
this.SET_AUTHORIZED_PAGES();
if (this.listGetter.type == 2) {
this.$router
.push({
name: "issuesShow",
params: {
id: this.listGetter?.reference_id ?? null,
},
})
.then(() => {});
} else if (this.listGetter.type == 3) {
this.$router
.push({
name: "answersShow",
params: {
answerId: this.listGetter?.reference_id,
entityId: this.listGetter?.parent_reference,
},
})
.then(() => {});
}
// :href="'/jahat/' + buttonName + '/' + listGetter?.reference_id"
},
},
};

463
mixins/messageMixin.js Normal file
View File

@ -0,0 +1,463 @@
import chatApi from "~/apis/chatApi";
// import repoApi from "~/apis/repoApi";
export default {
beforeMount() {
this.httpService = useNuxtApp()["$http"];
},
methods: {
async getMessages(
fill_limit = false,
by_first_message = false,
seen_date = undefined,
page = undefined,
message_id = undefined
) {
// if (this.fetchingData) return;
// this.fetchingData = true;
let group_id = undefined;
let contact_id = undefined;
let filter_users = this.selectedGroupMember ?? [];
let filter_labels = this.selectedMessageLabel ?? [];
group_id = this.listGetter.group_id ?? this.listGetter.id;
if (this.isRedirectedFromOtherSystems && this.groupId)
group_id = this.listGetter.group_id ?? this.groupId;
else if (this.$route.name == "privates") {
group_id = undefined;
contact_id = this.listGetter?.user;
} else if (this.$route.name == "unReads") {
if (this.listGetter.icon_type == 1)
contact_id = this.listGetter.user_seen;
else group_id = this.listGetter.id;
}
if (seen_date == undefined) seen_date = this.pagination.seen_date;
//پیام منطبق با تاریخ درخواستی نیاید
if (!by_first_message && seen_date > 0) seen_date++;
if (page == undefined) page = this.pagination.page;
let payload = {
group_id: group_id,
contact_id: contact_id,
offset: 0,
limit: this.limit,
filter_users: filter_users,
filter_labels: filter_labels,
seen_date: seen_date,
page: page,
message_id: message_id,
};
if (this.$route.query["invite-id"])
payload["invite_id"] = this.$route.query["invite-id"];
if (fill_limit) payload.fill_limit = 1;
if (message_id != undefined) payload.seen_date = 0;
return await this.httpService
.postRequest("message/" + chatApi.messages.virtualList, payload)
.then((response) => {
if (response.data.length) {
response.data.forEach((item, index) => {
if (this.isRedirectedFromOtherSystems && this.messageId) {
item.active = item.id == this.messageId;
this.prevSelectedItemIndex = index;
}
if (this.isRedirectedFromOtherSystems && this.userId) {
item.active = item.id == this.userId;
this.prevSelectedItemIndex = index;
}
item.user = response.users.find(
(user) => user.user_id == item.user
);
if (item.replys)
item.replys.users = this.allReplayers(item, response.users);
});
}
this.pagination.total = response.pagination.total;
// this.showList = true;
// this.busy = false;
return response;
})
.finally(() => {
// this.fetchingData = false;
});
},
save(ev, textAreaName, container) {
// disable scroll when saving data.
this.busy = true;
if (this.savingComment) return;
this.savingComment = true;
// اگر پیام هدایت شده باشد و کاربر پیامی نوشته نباشد.
if (
this.isForward &&
(this.userMessage == null || this.userMessage == "")
) {
this.saveForward()
.then((res) => {
this.pagination.offset = 0;
this.seenDate = -1;
this.onSendMessage(res.data);
// this.localComments.push(res.data);
// this.setVirtualListToBottom();
// this.toLastItem();
})
.catch((err) => {
if (
response.status == 403 &&
response.data?.meta &&
response.data?.meta[0]?.is_guest
) {
this.openAuthModal();
}
})
.finally(() => {
this.mainCommentor = undefined;
this.userMessage = null;
this.savingComment = false;
this.replyComment = false;
this.isMainEditMode = false;
this.isForward = false;
this.SET_FORWARD_ITEM();
});
// اگر پیام هدایت شده باشد و کاربر پیامی نوشته باشد.
} else if (
this.isForward &&
(this.userMessage != null || this.userMessage != "")
) {
this.saveForward().then(() => {
let payload = {
text: this.userMessage,
group_id: this.listGetter.id,
id: this.mainCommentor?.id ?? undefined,
contact_id: undefined,
replyto: this.replayTo?.id ?? undefined,
};
// private route condition
if (this.$route.name == "privates") {
payload = {
text: this.userMessage,
contact_id: this.listGetter.user,
};
}
// if user pressed save from right form(main), empty the replayto field.
if (container === "messages") {
payload.replyto = undefined;
}
let url = "message/" + chatApi.messages.create;
payload.text = this.replaceTextWithLink(payload.text);
this.httpService
.formDataRequest(url, payload)
.then((res) => {
// this.mySwalToast({
// html: res?.message ?? "",
// });
// if (this.isMainEditMode) {
// this.mainCommentor = undefined;
// }
// this.userMessage = null;
this.pagination.offset = 0;
this.seenDate = -1;
this.onSendMessage(res.data);
})
.catch((err) => {
if (
response.status == 403 &&
response.data?.meta &&
response.data?.meta[0]?.is_guest
) {
this.openAuthModal();
}
})
.finally(() => {
this.mainCommentor = undefined;
this.userMessage = null;
this.savingComment = false;
this.replyComment = false;
this.isMainEditMode = false;
this.isForward = false;
this.SET_FORWARD_ITEM();
});
});
// حالت معمولی(هدایت نشده باشد.)
} else {
let payload = {
text: this.userMessage,
group_id: this.listGetter.id,
id: this.mainCommentor?.id ?? undefined,
contact_id: undefined,
replyto: this.replayTo?.id ?? undefined,
};
// private route condition
if (this.$route.name == "privates") {
payload = {
text: this.userMessage,
id: this.mainCommentor?.id ?? undefined,
contact_id: this.listGetter.user,
};
}
// if user pressed save from right form(main), empty the replayto field.
if (container === "messages") {
payload.replyto = {};
}
let url = "message/" + chatApi.messages.create;
payload.text = this.replaceTextWithLink(payload.text);
this.httpService
.formDataRequest(url, payload)
.then((res) => {
// this.mySwalToast({
// html: res?.message ?? "",
// });
if (this.isMainEditMode) {
this.mainCommentor = undefined;
}
// this.$root.emit('update-group-list')
this.userMessage = null;
this.toLastItem();
})
.catch(({ response }) => {
if (
response.status == 403 &&
response.data?.meta &&
response.data?.meta[0]?.is_guest
) {
this.openAuthModal();
}
})
.finally(() => {
this.savingComment = false;
this.replyComment = false;
this.isMainEditMode = false;
this.SET_FORWARD_ITEM();
});
}
this.closeReplays();
},
deleteItem(comment, index, editFrom, replayFrom) {
if (this.buttonLoading) return;
this.buttonLoading = true;
let html = `آیا مطمئن هستید که می‌خواهید این پیام را حذف کنید؟`;
if (comment?.replys?.count > 0)
html = `آیا مطمئن هستید که می‌خواهید این پیام را حذف کنید؟<br/>
با حذف این پیام، رشتهٔ پاسخ آن (شامل ${comment.replys.count} پاسخ) نیز حذف خواهد شد.
`;
this.mySwalConfirm({
title: "هشدار!!!",
html,
}).then((result) => {
if (result.isConfirmed) {
let payload = {
group_id: this.listGetter.id,
contact_id: undefined,
messages_id: [comment.id],
};
if (this.$route.name == "privates") {
payload = {
group_id: undefined,
contact_id: this.listGetter.user,
messages_id: [comment.id],
};
}
let url = "message/" + chatApi.privates.delete;
this.httpService
.postRequest(url, payload)
.then((res) => {
// this.mySwalToast({
// title: "",
// html: res.message,
// });
// this.updateMessageCount();
if (replayFrom == "replays") this.replays.splice(index, 1);
else {
this.localComments.splice(index, 1);
this.closeReplays();
}
// this.getMessages();
})
.catch((err) => {})
.finally(() => {
this.buttonLoading = false;
// this.showList = !this.showList;
});
} else this.buttonLoading = false;
});
},
deleteMany() {
if (this.buttonLoading) return;
this.buttonLoading = true;
this.mySwalConfirm({
title: "هشدار!!!",
html: "آیا مطمئن هستید که می‌خواهید این پیام را حذف کنید؟",
}).then((result) => {
if (result.isConfirmed) {
let payload = {
group_id: this.listGetter.id,
contact_id: undefined,
messages_id: this.selectedCommentsId,
};
if (this.$route.name == "privates") {
payload = {
group_id: undefined,
contact_id: this.listGetter.user,
messages_id: this.selectedCommentsId,
};
}
let url = "message/" + chatApi.privates.delete;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.mySwalToast({
title: "",
html: res.message,
});
this.selectedCommentsId.forEach((id) => {
this.localComments = this.localComments.filter(
(fi) => fi.id != id
);
});
this.selectedCommentsId = [];
this.showSelectionActionBar = false;
// this.pagination.offset = 0;
// this.pagination.seenDate = -1;
// this.pagination.page = -1;
// this.pagination.pageBottom = 0;
// this.pagination.pageTop = -1;
// this.toLastItem();
})
.catch((err) => {})
.finally(() => {
this.buttonLoading = false;
// this.showList = !this.showList;
});
}
});
},
onSendMessage(message) {
this.localComments.push(message);
this.$nextTick(() => {
this.setVirtualListToBottom();
});
},
editComment(comment, listRefName, editModeName) {
try {
this.$refs[listRefName].style.removeProperty("height");
} catch (err) {
this.$refs[listRefName].style.height = null;
}
if (editModeName == "isMainEditMode") {
this.mainCommentor = comment;
this.isMainEditMode = true;
this.userMessage = this.parseText(comment.text);
} else {
this.replayCommentor = comment;
this.isReplayEditMode = true;
this.replayText = this.parseText(comment.text);
}
},
replyComments(comment, listRefName) {
try {
this.$refs[listRefName].style.removeProperty("height");
} catch (err) {
this.$refs[listRefName].style.height = null;
}
this.replayText = null;
this.replayCommentor = comment;
this.isReplayEditMode = true;
// this.commentor = comment;
},
openReplays(comment, index) {
this.replayFirstLoad = true;
// this.replayPagination.seen_date =-1;
// this.SET_SELECTED_ITEM(comment);
this.replayTo = comment;
this.markActive(this.localComments, index);
// fill_limit = false,
// by_first_message = false,
// seen_date = undefined,
// page = undefined,
// message_id = undefined
this.getReplays(false, false, comment.created_at, 0).then((response) => {
this.replays = response?.data ?? [];
this.replayPagination.seen_date = response?.pagination.new_seen_date;
this.showReplays = true;
});
},
closeComments(isEditModeName) {
this.isForward = false;
this.replyComment = false;
this[isEditModeName] = false;
this.userMessage = null;
// try {
// this.$refs.messages.style.removeProperty("height");
// this.$refs.mainTextAreaInput.style.removeProperty("height");
// } catch (err) {
// this.$refs.messages.style.height = null;
// this.$refs.mainTextAreaInput.style.height = null;
// }
},
markActive(comments, index) {
if (this.prevSelectedItemIndex != index) {
this.$set(comments[this.prevSelectedItemIndex], "active", false);
}
this.prevSelectedItemIndex = index;
this.$set(comments[index], "active", true);
},
},
};

196
mixins/mountedMixin.js Normal file
View File

@ -0,0 +1,196 @@
import privatesContextMenu from "@chat/json/privatesContextMenu.json";
import groupsContextMenu from "@chat/json/groupsContextMenu.json";
import lobbiesChildContextMenu from "@chat/json/lobbiesChildContextMenu.json";
import lobbiesParentContextMenu from "@chat/json/lobbiesParentContextMenu.json";
export default {
mounted() {
const vm = this;
this.closeForwardModal();
// dynamic import : method two
// import(`~/chat/json/messagesContextMenu.json`)
// .then((obj) => {
// this.contextMenu = obj.default;
// })
// .catch((err) => {});
if (this.$route.name == "privates")
this.commonContextMenu = privatesContextMenu;
if (this.$route.name == "groups")
this.commonContextMenu = groupsContextMenu;
if (this.$route.name == "lobbies") {
if (this.listGetter?.parent_id)
this.commonContextMenu = lobbiesChildContextMenu;
else this.commonContextMenu = lobbiesParentContextMenu;
}
if (this.$route.name == "unReads")
this.commonContextMenu = groupsContextMenu;
this.pagination.offset = 0;
this.pagination.pageBottom = 1;
this.pagination.pageTop = 0;
this.pagination.page = 1;
this.pagination.seen_date = 0;
// check if redirected from other systems.
// or clicked on a copy link.
if (this.$route.query["group-id"] && this.$route.query["message-id"]) {
this.groupId = this.$route.query["group-id"];
this.messageId = this.$route.query["message-id"];
this.isRedirectedFromOtherSystems = true;
}
// when user forward a message from the search-modal component.
if (this.getForwardItem) {
this.userId = this.getForwardItem?.message.user.id;
// this.messageId = this.getForwardItem.forwardFrom.id;
this.isRedirectedFromOtherSystems = false;
this.isForward = true;
this.mainCommentor = this.getForwardItem?.message;
this.isMainEditMode = true;
this.pagination.offset = 0;
this.direction = -1;
this.seenDate = -1;
}
if (this.listGetter) {
// this.pagination.offset = 0;
this.pagination.seen_date = this.listGetter?.seen_messages?.seen_date;
this.pagination.page = 1;
this.pagination.pageBottom = 1;
this.pagination.pageTop = 0;
if (
this.pagination.seen_date == undefined ||
this.pagination.seen_date == ""
) {
this.pagination.seen_date = -1;
this.pagination.page = -1;
this.pagination.pageBottom = 0;
this.pagination.pageTop = -1;
}
if (this.listGetter?.is_group == 0) {
this.limit = 100;
if (this.listGetter.replyto) {
this.pagination.seen_date = this.listGetter.p_date_c;
}
}
// first page request
this.getMessages(true, true).then((response) => {
this.localComments = response.data; //this.localComments.concat(response.data);
this.pagination.seen_date = response.pagination.new_seen_date;
// when user searching.
setTimeout(() => {
this.jumpSpecialMessage();
}, 1000);
});
// if user is not joined, dont get members.
// if (!this.listGetter?.show_joined) {
this.getGroupMembers();
// }
}
document
.getElementById("paste-section")
.addEventListener("paste", async (e) => {
vm.pasting = true;
vm.handlePasteEvent(e);
});
if (this.isIssueGroup() || this.isAnswerGroup()) {
if (this.isIssueGroup()) {
this.buttonTitle = "مشاهده مساله";
this.buttonName = "issues";
} else if (this.isAnswerGroup()) {
this.buttonTitle = "مشاهده پاسخ";
this.buttonName = "answers";
}
}
// events fired from Auth.vue and authMixin.js
// event fired from authMixin.js
this.$root.$on("authenticated-by-modal", () => {
// this.$route.query["invite-id"] = undefined;
this.$set(this.$route.query, "invite-id", undefined);
this.getUserPermissionTags();
this.initServices();
this.closeAuthModal();
this.getGroupMembers();
this.toLastItem();
});
},
methods: {
jumpSpecialMessage(){
let jumpMessageId = 0;
let jumpMessageIndex = 0;
// if listgetter contains a message or replay item.
if (this.listGetter?.is_group == 0) {
if (this.listGetter.replyto) {
const index = this.localComments.findIndex(
(item) => item.id == this.listGetter.replyto
);
const res = this.localComments.find(
(item) => item.id == this.listGetter.replyto
);
this.openReplays(res, index);
jumpMessageId = res.id;
} else {
const index = this.localComments.findIndex(
(item) => item.id == this.listGetter.id
);
jumpMessageIndex = index;
jumpMessageId = this.listGetter?.id;
}
}
// if listgetter contains group item
else {
if (this.$route.query["message-id"]) {
const index = this.localComments.findIndex(
(item) => item.id == this.$route.query["message-id"]
);
const res = this.localComments.find(
(item) => item.id == this.$route.query["message-id"]
);
jumpMessageIndex = index;
jumpMessageId = res.id;
}
}
if(jumpMessageId)
document.getElementById("chat-item-" + jumpMessageId).scrollIntoView({
top: 100,
behavior: "smooth",
block: "center",
});
if(jumpMessageIndex)
this.markActive(this.localComments, jumpMessageIndex);
},
isIssueGroup() {
return this.listGetter?.type == 2;
},
isAnswerGroup() {
return this.listGetter?.type == 3;
},
isPrivate() {
return this.listGetter?.type == 0;
},
},
};

362
mixins/replayMixin.js Normal file
View File

@ -0,0 +1,362 @@
import chatApi from "~/apis/chatApi";
import repoApi from "~/apis/repoApi";
export default {
methods: {
// #region replays methods
closeReplays() {
this.showReplays = false;
this.resetReplayPagination();
this.replays = [];
this.replayFinishedTop = false;
this.replayFinishedBottom = false;
this.replayOverflow = false;
},
allReplayers(comment, replayUsers) {
let newUsers = [];
if (comment.replys)
comment.replys.users.forEach((user) => {
replayUsers.forEach((item) => {
if (user == item.user_id) newUsers.push(item);
});
});
return newUsers;
},
async getReplays(
fill_limit = false,
by_first_message = false,
seen_date = undefined,
page = undefined,
message_id = undefined
) {
// if (this.fetchinReplaygData) return;
// this.fetchinReplaygData = true;
let group_id = this.listGetter.id;
// groups,lobbies
let payload = {
group_id: group_id,
contact_id: undefined,
replyto: this.replayTo.id,
};
if (this.listGetter && this.listGetter?.is_group == 0){
payload.group_id = undefined
payload.contact_id = this.listGetter?.user;
}
if (seen_date == undefined) seen_date = this.replayPagination.seen_date;
//پیام منطبق با تاریخ درخواستی نیاید
if (!by_first_message && seen_date > 0) seen_date++;
if (page == undefined) page = this.replayPagination.page;
payload = {
...payload,
offset: 0,
limit: this.replayLimit,
seen_date: seen_date,
page: page,
message_id: message_id,
};
return await this.httpService
.postRequest("message/" + chatApi.messages.virtualList, payload)
.then((response) => {
// set replays for render on the left side.
let replays = undefined;
if (response.data && response.data.length) {
replays = structuredClone(response);
this.setUsers(replays.data, response.users);
}
// this.localComments = response.data;
// this.replays.forEach((item) => {
// item.user = response.users.find(
// (user) => user.user_id == item.user
// );
// if (item.replys)
// item.replys.users = this.allReplayers(item, response.users);
// });
this.replayPagination.total = response.pagination.total;
// this.replayPagination = {
// ...this.replayPagination,
// ...response.pagination,
// };
return replays;
})
.finally(() => {
// this.fetchinReplaygData = false;
});
},
saveReplay() {
if (this.savingReplay) return;
this.savingReplay = true;
let payload = {
text: this.replayText,
group_id: this.listGetter.id,
id: this.replayCommentor?.id ?? undefined,
contact_id: undefined,
replyto: this.replayTo?.id ?? undefined,
};
if (this.$route.name == "privates") {
payload = {
text: this.replayText,
id: this.replayCommentor?.id ?? undefined,
group_id: undefined,
contact_id: this.listGetter.user,
replyto: this.replayTo?.id ?? undefined,
};
}
let url = "message/" + chatApi.messages.create;
payload.text = this.replaceTextWithLink(payload.text);
this.httpService
.formDataRequest(url, payload)
.then((res) => {
// this.mySwalToast({
// html: res?.message ?? "",
// });
if (this.isReplayEditMode) {
this.replayCommentor = {};
}
this.replayText = null;
this.toReplayLastItem();
this.updateMessageCount(res);
// this.getReplays(
// ).then(() => {
// this.updateMessageCount();
// });
})
.finally(() => {
this.savingReplay = false;
this.isReplayEditMode = false;
this.replyComment = false;
});
},
// #endregion
async replaySaveFiles() {
let payload = {
text: this.uploadDescription,
group_id: this.listGetter.id,
id: this.mainCommentor?.id ?? undefined,
contact_id: undefined,
replyto: this.replayTo?.id ?? undefined,
};
if (this.$route.name == "privates") {
payload = {
text: this.uploadDescription,
id: this.replayTo?.id ?? undefined,
contact_id: this.listGetter.user,
};
}
let url = chatApi.messages.create;
// when screen shot paste, decide where to save it.(replay or message)
// if (
// payload.file.type == "" &&
// payload.file.name == "mp3" &&
// payload.file.size <= 20000
// ) {
// // 20KB
// this.mySwalToast({
// icon: "warn",
// text: "حداقل صوت قابل آپلود 20 کیلو بایت می باشد.",
// });
// } else
// 'Content-Type': 'application/x-www-form-urlencoded'
if (this.files.length > 1) {
this.files.forEach((fileItem) => {
payload.file = fileItem;
payload.length = fileItem.size;
this.fileUploadHttpService
.formDataRequest(url, payload)
.then((res) => {
this.replayText = null;
this.uploadDescription = null;
this.toReplayLastItem();
this.updateMessageCount(res);
})
.catch(() => {
this.mySwalToast({
html: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
});
});
});
} else {
// 0: files, 1: image, 2:audio 3:video
// payload.file_type = this.getFileType(this.files[0].name);
payload.file = this.files[0];
payload.length = this.files[0].size;
return await this.fileUploadHttpService
.formDataRequest(url, payload)
.then((res) => {
this.closeDroppedList();
this.replayText = null;
this.uploadDescription = null;
this.toReplayLastItem();
this.updateMessageCount(res);
})
.catch(() => {
this.mySwalToast({
html: "خطایی رخ داد.لطفا دوباره امتحان کنید.",
});
});
}
},
replayToTop() {
if (this.replayFinishedTop) return;
--this.replayPagination.pageTop;
this.replayPagination.page = this.replayPagination.pageTop;
const vm = this;
this.getReplays().then((response) => {
if (!response?.data?.length) {
vm.replayFinishedTop = true;
vm.replayPagination.pageTop++;
vm.replayPagination.page = vm.replayPagination.pageTop;
return;
}
const vsl = vm.$refs.replayVsl;
let prev_size = vsl.getScrollSize();
vm.replays = response.data.concat(vm.replays);
vm.$nextTick(() => {
const offset = vsl.getScrollSize() - prev_size;
vm.replaySetVirtualListToOffset(offset);
});
});
},
replayToBottom() {
if (this.replayFirstLoad) {
this.replayFirstLoad = false;
return;
}
if (this.replayFinishedBottom) return;
++this.replayPagination.pageBottom;
this.replayPagination.page = this.replayPagination.pageBottom;
this.getReplays().then((response) => {
if (!response?.data?.length) {
this.replayFinishedBottom = true;
this.replayPagination.pageBottom--;
this.replayPagination.page = this.replayPagination.pageBottom;
return;
}
this.replays = [...this.replays, ...response.data];
this.$nextTick(() => {
const vsl = this.$refs.replayVsl;
const offset = vsl.getScrollSize();
this.replaySetVirtualListToOffset(offset);
this.fetchingData = false;
});
});
},
toReplayFirstItem() {
this.replayPagination.pageBottom = 1;
this.replayPagination.pageTop = 0;
this.replayPagination.page = 1;
this.replayPagination.seen_date = 0;
this.getReplays().then((response) => {
if (!response.data.length) {
return;
}
this.replays = response.data;
// const mids = this.getMids(response.data)
this.$nextTick(() => {
this.replayOffset = 0;
this.replaySetVirtualListToOffset(this.replayOffset);
this.fetchingData = false;
});
});
},
toReplayLastItem() {
this.replayPagination.pageBottom = 0;
this.replayPagination.pageTop = -1;
this.replayPagination.page = -1;
this.replayPagination.seen_date = -1;
this.getReplays().then((response) => {
if (!response.data.length) {
return;
}
this.replays = response.data;
// const mids = this.getMids(response.data)
this.$nextTick(() => {
const vsl = this.$refs.replayVsl;
this.offset = vsl.getScrollSize();
this.replaySetVirtualListToOffset(this.replayOffset);
this.fetchingData = false;
});
});
},
async replayOnResult(data) {
this.files = [];
this.files.push(new File([data], "mp3"));
// if (this.replayAbortRecord) return;
// this.replayAbortRecord = true;
this.replaySaveFiles().then(() => {
// this.replayRecording = false;
// this.replayAbortRecord = false;
});
},
showReplyComment() {
const myReply = document.getElementById("show_reply");
myReply.style.display = "none";
},
closeReplyComment(isEditModeName) {
this.replyComment = false;
this[isEditModeName] = false;
this.replayText = null;
// try {
// this.$refs.replays.style.removeProperty("height");
// this.$refs.replayTextRef.style.removeProperty("height");
// } catch (err) {
// this.$refs.replays.style.height = null;
// this.$refs.replayTextRef.style.height = null;
// }
},
resetReplayPagination() {
this.replayPagination = {
page: 0,
total: 0,
pageTop: 0,
pageBottom: 1,
seenDate: null,
};
},
},
};

220
mixins/virtialListMixin.js Normal file
View File

@ -0,0 +1,220 @@
import chatApi from "~/apis/chatApi";
import repoApi from "~/apis/repoApi";
export default {
methods: {
tofirstItem() {
this.pagination.pageBottom = 1;
this.pagination.pageTop = 0;
this.pagination.page = 1;
this.pagination.seen_date = 0;
this.getMessages().then((response) => {
if (!response.data.length) {
return;
}
this.localComments = response.data;
this.finished_top = false;
this.finished_bottom = false;
// const mids = this.getMids(response.data)
this.$nextTick(() => {
const vsl = this.$refs.vsl;
this.offset = 0;
this.setVirtualListToOffset(this.offset);
this.fetchingData = false;
});
});
},
toLastItem() {
this.pagination.pageBottom = 0;
this.pagination.pageTop = -1;
this.pagination.page = -1;
this.pagination.seen_date = -1;
this.getMessages().then((response) => {
if (!response.data.length) {
return;
}
this.localComments = response.data;
this.finished_top = false;
this.finished_bottom = true;
// const mids = this.getMids(response.data)
this.$nextTick(() => {
try {
const vsl = this.$refs.vsl;
this.offset = vsl?.getScrollSize();
this.setVirtualListToOffset(this.offset);
this.fetchingData = false;
} catch (err) {
setTimeout(() => {
const vsl = this.$refs.vsl;
this.offset = vsl?.getScrollSize();
this.setVirtualListToOffset(this.offset);
this.fetchingData = false;
}, 500);
}
});
});
},
toTop() {
if (this.finished_top) return;
// if (this.fetchingData) return;
// this.fetchingData = true;
// if (this.pagination.current_row >= this.pagination.total) {
// return;
// }
--this.pagination.pageTop;
this.pagination.page = this.pagination.pageTop;
const vm = this;
this.getMessages().then((response) => {
if (!response.data.length) {
vm.finished_top = true;
vm.pagination.pageTop++;
vm.pagination.page = vm.pagination.pageTop;
return;
}
const vsl = vm.$refs.vsl;
let prev_size = vsl.getScrollSize();
vm.localComments = response.data.concat(vm.localComments);
// const mids = vm.getMids(response.data)
vm.$nextTick(() => {
// // this.offset = vsl.getOffset();
// const offset = mids.reduce((previousValue, currentSid) => {
// const previousSize = typeof previousValue === 'string' && previousValue !== 0 ? vsl.getSize(previousValue) : previousValue
// return previousSize + vsl.getSize(currentSid)
// }, 0)
const offset = vsl.getScrollSize() - prev_size;
vm.setVirtualListToOffset(offset);
// this.pagination.current_row = response.pagination.current_row;
// vm.fetchingData = false;
});
});
},
toBottom() {
if (this.finished_bottom) return;
++this.pagination.pageBottom;
this.pagination.page = this.pagination.pageBottom;
this.getMessages().then((response) => {
if (!response.data.length) {
this.finished_bottom = true;
this.finished_top = false;
this.pagination.pageBottom--;
this.pagination.page = this.pagination.pageBottom;
return;
}
this.localComments = [...this.localComments, ...response.data];
const vsl = this.$refs.vsl;
this.offset = vsl.getScrollSize();
this.$nextTick(() => {
this.setVirtualListToOffset(this.offset);
this.fetchingData = false;
});
});
},
checkOverFlow() {
const vsl = this.$refs.vsl;
if (vsl) {
this.overflow = vsl.getScrollSize() > vsl.getClientSize();
}
},
setVirtualListToIndex(index) {
if (this.$refs.vsl) {
this.$refs.vsl.scrollToIndex(index);
}
},
setVirtualListToOffset(offset) {
if (this.$refs.vsl) {
this.$refs.vsl.scrollToOffset(offset);
// this.$refs.vsl.scrollToIndex(offset);
}
},
replaySetVirtualListToOffset(offset) {
if (this.$refs.replayVsl) {
this.$refs.replayVsl.scrollToOffset(offset);
}
},
setVirtualListToBottom() {
if (this.$refs.vsl) {
this.$refs.vsl.scrollToBottom();
}
},
handleDrop(data, event) {
event.preventDefault();
this.files = Array.from(event.dataTransfer.files);
this.dragStarted = false;
this.showDroppedList = true;
this.uploadDescription = null;
},
handleDragEnter(event, dropContainer) {
this.dragStarted = true;
this.dropBoxClass = dropContainer;
},
handleDragLeave(event) {
this.dragStarted = false;
},
// // $event, 'replayTextRef', 'replays'
// resizeIt(ev, textAreaName, container) {
// return;
// // do nothing.
// ev.target.style.height = "auto";
// var newHeight = ev.target.scrollHeight > 37 ? ev.target.scrollHeight : 37;
// if (ev.which == 13) {
// return;
// } else {
// // escape key pressed
// if (ev.which == 27) {
// newHeight = 37;
// }
// // const fz= parseFloat(getComputedStyle(ev.target).getPropertyValue("font-size"));
// // increase/decrease height of the form(textarea), if textarea height changed(2 < lines < 5).
// ev.target.style.height = newHeight.toString() + "px";
// if (newHeight < 115) {
// this.$refs[container].style.height = `calc(100vh - ${
// newHeight + "px"
// } - 6em)`;
// let replyCommentBoxHeight = "0px";
// if (this.isReplayEditMode || this.isMainEditMode) {
// replyCommentBoxHeight = "4em";
// }
// // just for the left sidebar(replays) form
// if (textAreaName == "replayTextRef") {
// // decrease height of the comment list, if textarea height changed.
// this.$refs[container].style.height = `calc(100vh - ${
// newHeight + "px"
// } - 10.7em - ${replyCommentBoxHeight} )`;
// } else if (textAreaName == "mainTextAreaInput") {
// this.$refs[container].style.height = `calc(100vh - ${
// newHeight + "px"
// } - 6.2em - ${replyCommentBoxHeight})`;
// }
// }
// }
// },
},
};

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="d-flex w-100">
<group
ref="chat-list-panel"
@update-messages="rerenderChatList++"
@ -10,12 +10,12 @@
<div class="flex-grow-1">
<div class="position-relative d-flex justify-content-center"></div>
<div v-if="listGetter">
<!-- <chat-list
<chat-list
ref="chat-content"
:key="rerenderChatList"
@show="statusPage"
@my-event="openPagGrup"
></chat-list> -->
></chat-list>
</div>
<div v-else class="no-group-selected" :class="$route.name">

View File

@ -6,7 +6,7 @@
<script>
import { clearBodyClass } from "@manuals/utilities";
import menu from "@chat/json/chat/json/menu.json";
import menu from "@chat/json/menu.json";
export default {
name: "chatRouterView",