<template> <div class="menu-bar__content menu-bar-content home-list p-3" :class="{ 'hidden-heder': !showMainpag }" > <!-- <div class="menu-bar__content menu-bar-content home-list p-3" v-if="showMainpag" > --> <Breadcrumbs class="m-0" /> <div class="home-list__header pb-0"> <div class="flex-grow-1 search-main hidean-search" ref="search"> <button class="button-search open" @click="showSearchs()"> <span class="tavasi tavasi-Component-21--1"></span> </button> <form class="search-filter" role="search" @submit.prevent="sendQuery"> <div class="input-group mb-3"> <div class="input-group-append"> <button class="input-group-text" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" > <!-- settings icon --> <svg class="icon icon-settings"> <use xlink:href="#icon-settings"></use> </svg> </button> <div class="dropdown-menu"> <button @click.prevent="setSearchType('all')" class="btn dropdown-item new-message" :class="{ active: serachType == 'all' }" type="button" > <svg class="icon icon-all"> <use xlink:href="#icon-all"></use> </svg> همه </button> <button @click.prevent="setSearchType('personal')" class="btn dropdown-item new-group" :class="{ active: serachType == 'personal' }" type="button" > <svg class="icon icon-personal"> <use xlink:href="#icon-personal"></use> </svg> شخصی </button> <button @click.prevent="setSearchType('groups')" class="btn dropdown-item new-group" :class="{ active: serachType == 'groups' }" type="button" > <svg class="icon icon-groups"> <use xlink:href="#icon-groups"></use> </svg> گروه ها </button> <button @click.prevent="setSearchType('my-messages')" class="btn dropdown-item new-group" :class="{ active: serachType == 'my-messages' }" type="button" > <svg class="icon icon-my-messages"> <use xlink:href="#icon-my-messages"></use> </svg> خوانده نشده ها </button> <button @click.prevent="setSearchType('unread')" class="btn dropdown-item new-group" :class="{ active: serachType == 'unread' }" type="button" > <svg class="icon icon-unread"> <use xlink:href="#icon-unread"></use> </svg> پیام های من </button> <button @click.prevent="setSearchType('lobby')" class="btn dropdown-item new-group" :class="{ active: serachType == 'lobby' }" type="button" > <svg class="icon icon-lobby"> <use xlink:href="#icon-lobby"></use> </svg> تالارها </button> </div> </div> <input dir="rtl" v-model.trim="searchText" type="search" required class="form-control" id="search-query" placeholder="جستجو..." name="search-query" aria-label="جستجو در اسناد، عناوین و واژگان" aria-describedby="basic-addon1" size="50" @keyup="sendQuery()" @keydown="onKeyDown()" /> <div class="input-group-prepend"> <button @click="clearSearchAndGetList" type="button" class="input-group-text" id="basic-addon1" > <i v-if="searchText" class="tavasi tavasi-Component-294--1"></i> </button> </div> </div> </form> </div> <div class="navbar open"> <div class="navbar-grow"> <div v-for="(item, index) in menu.chats" :key="index" class="hover-border" > <RouterLink :to="{ name: item.link }" class="has-sub-items gl-link" > <svg class="nav-icon-container" :class="'icon icon-' + item.icon"> <use :xlink:href="'#icon-' + item.icon"></use> </svg> </RouterLink> </div> <div class="hover-border1" @click="showSearchs()"> <span class="tavasi tavasi-Component-198--1"></span> </div> </div> </div> </div> <div class="home-list__content scroll-needed" :class="{ loading: fetchingData }" > <div class="last-search h-100" id="last-search"> <div class="last-search-content h-100" @scroll="loadMore"> <div class="content"> <div v-if="$route.name == 'privates'"> <div class="group-item" v-for="(groupItem, index) in groups" :key="index" > <div class="group-row enable-hover"> <div class="group-picture-container"> <context-menu dataOffset="0,0" style="position:static;" :list="groups" :clickedItem="groupItem" :contextMenu="commonContextMenu" :parentComponent="'groups-panel'" @remove-item="removeItem(groups, groupItem, index)" > <!-- @generate-invite-post="openCreateForm('privates', groupItem)" --> </context-menu> <img class="group-picture" @error="handleImageSrcOnError($event, false)" :src="getGroupAvatar(groupItem)" :alt="groupItem.title" /> </div> <button type="button" @click.prevent="showMessages(groups, groupItem, index)" @click="showfilter()" :title="groupItem.unread ?? 0" class="btn group-content" :class="{ active: groupItem.active ?? false }" > <div class="group-title-container"> <h4 class="group-title"> <!-- <span class="tavasi tavasi-personal"></span> --> <svg class="icon icon-personal"> <use xlink:href="#icon-personal"></use> </svg> {{ groupItem.title }} </h4> <span class="group-time"> {{ convertUnixToPersianDateTime(groupItem.created_at) }} </span> </div> <div class="group-description-container"> <p class="group-description"> {{ groupItem.desc }} </p> <span v-if="groupItem?.seen_messages?.count > 0" class="group-unread-indicator badge badge-info" > {{ groupItem?.seen_messages?.count ?? 0 }} </span> </div> </button> </div> </div> </div> <div v-else-if="$route.name == 'groups'"> <div class="group-item" v-for="(groupItem, index) in groups" :key="index" > <div class="group-row " :class="{ 'enable-hover': isGroupAdmin(groupItem) }" > <div class="group-picture-container"> <context-menu v-if="isGroupAdmin(groupItem)" dataOffset="0,0" style="position:static;" :list="groups" :clickedItem="groupItem" :contextMenu="commonContextMenu" :parentComponent="'groups-panel'" @remove-item="removeItem(groups, groupItem, index)" @edit-item="editItem(groups, groupItem, index)" @join-to-group="openGroupSearchForm('groups', groupItem)" > </context-menu> <img class="group-picture" @error="handleImageSrcOnError($event, false)" :src="getGroupAvatar(groupItem)" :alt="groupItem.title" /> </div> <button type="button" @click.prevent="showMessages(groups, groupItem, index)" @click="showfilter()" :title="groupItem.unread ?? 0" class="btn group-content" :class="{ active: groupItem.active ?? false }" > <div class="group-title-container"> <h4 class="group-title"> <!-- <span class="tavasi tavasi-groups"></span> --> <svg class="icon icon-groups"> <use xlink:href="#icon-groups"></use> </svg> {{ groupItem.title }} </h4> <span class="group-time"> {{ convertUnixToPersianDateTime(groupItem.created_at) }} </span> </div> <div class="group-description-container"> <p class="group-description"> {{ groupItem.desc }} </p> <span v-if="groupItem?.seen_messages?.count > 0" class="group-unread-indicator badge badge-info" > {{ groupItem?.seen_messages?.count ?? 0 }} </span> </div> </button> </div> </div> </div> <div v-else-if="$route.name == 'unReads'"> <div class="group-item unReads" v-for="(groupItem, index) in groups" :key="index" > <div class="group-row "> <div class="group-picture-container"> <context-menu dataOffset="0,0" style="position:static;" :list="groups" :clickedItem="groupItem" :contextMenu="commonContextMenu" :parentComponent="'groups-panel'" @remove-item="removeItem(groups, groupItem, index)" @edit-item="editItem(groups, groupItem, index)" > </context-menu> <img class="group-picture" @error="handleImageSrcOnError($event, false)" :src="getGroupAvatar(groupItem)" :alt="groupItem.title" /> </div> <div class="group-content p-2" :class="{ active: groupItem.active ?? false }" > <div class="group-title-container"> <h4 class="group-title"> <!-- <span class="tavasi tavasi-personal"></span> --> {{ groupItem.title }} </h4> <div class="unreads-box"> <!-- message info --> <button type="button" @click.prevent=" showMessages(groups, groupItem, index) " :title="groupItem.unread ?? 0" class="btn unread-button" > <!-- <span class="unread-message-label"> پیام ها : </span> --> <svg class="icon icon-Component-112--1"> <use xlink:href="#icon-Component-112--1"></use> </svg> {{ groupItem.seen_messages.count }} </button> <!-- replay info --> <button type="button" @click.prevent=" showMessages(groups, groupItem, index) " :title="groupItem.unread ?? 0" class="btn unread-button" @click="showfilter()" > <!-- <span class="unread-message-label"> پاسخ ها : </span> --> <svg class="icon icon-reply"> <use xlink:href="#icon-reply"></use> </svg> {{ replayCount(groupItem.seen_replys) }} </button> </div> <!-- <span class="group-time"> {{ convertUnixToPersianDateTime(groupItem.date_c) }} </span> --> </div> <div class="group-description-container"> <p class="group-description"> {{ groupItem.last_message.text }} </p> <!-- <span class="group-unread-indicator badge badge-info"> {{ groupItem.seen.count }} </span> --> </div> </div> </div> </div> </div> <div v-if="$route.name == 'lobbies'"> <div class="accordion" id="lobby-accordion"> <div v-for="(lobby, key) in groups" :key="key" class="card border-0 group-item" > <div class="card-header border-bottom-0 p-0" :id="'heading' + key" > <button type="button" class="btn has-indicator group-row" :title="lobby.title" data-toggle="collapse" :data-target="'#collapse' + key" :aria-expanded="!collapseAll" :class="[ { collapsed: collapseAll }, { ' enable-hover': isGroupAdmin(lobby) }, ]" :aria-controls="'collapse' + key" > <div class="group-picture-container"> <context-menu v-if="isGroupAdmin(lobby)" dataOffset="0,0" style="position:static;" :list="groups" :clickedItem="lobby" :contextMenu="commonContextMenu" :parentComponent="'groups-panel'" @remove-item="removeItem(groups, lobby, key)" @edit-item="editItem(groups, lobby, key)" > </context-menu> <img class="group-picture" @error="handleImageSrcOnError($event, false)" :src="getGroupAvatar(lobby)" :alt="lobby.title" /> </div> <div class="group-content" @click="showfilter()"> <div class="group-title-container"> <h4 class="group-title"> <svg class="icon icon-lobby"> <use xlink:href="#icon-lobby"></use> </svg> <!-- <img width="1em" height="1em" class="" src="assets/common/img/lobby.svg" alt="لابی" /> --> {{ lobby.title }} </h4> </div> <div class="group-description-container"> <p class="group-description"> {{ lobby.desc }} </p> </div> </div> </button> </div> <div :id="'collapse' + key" class="collapse" :class="{ show: !collapseAll }" :aria-labelledby="'heading' + key" data-parent="#lobby-accordion" > <div class="card-body p-0"> <ul class="list-group list-group-flush"> <li :key="j" v-for="(group, j) in lobby.sub_groups" class="list-group-item p-0" > <button type="button" @click.prevent=" lobbyShowMessages(groups, key, group, j) " class="btn group-row" :title="group.title" :class="{ 'enable-hover': isGroupAdmin(group) }" > <div class="group-picture-container"> <context-menu v-if="isGroupAdmin(group)" dataOffset="0,0" style="position:static;" :list="groups" :clickedItem="group" :contextMenu="commonContextMenu" :parentComponent="'groups-panel'" @edit-item="editItem(groups, group, j)" @remove-from-lobby="removeGroupFromLobby(group)" > </context-menu> <img class="group-picture" @error="handleImageSrcOnError($event, false)" :src="getGroupAvatar(group)" :alt="group.title" /> </div> <div class="group-content" :class="{ active: group.active ?? false }" > <div class="group-title-container"> <h4 class="group-title"> <img width="1em" height="1em" class="" :src="group.avatar" :alt="group.title" /> {{ group.title }} </h4> <span class="group-time"> {{ group.created_at }} </span> </div> <div class="group-description-container"> <p class="group-description"> {{ convertUnixToPersianDateTime( group.created_at ) }} </p> <span v-if="group?.seen_messages?.count > 0" class="group-unread-indicator badge badge-secondary" > {{ group?.seen_messages?.count ?? 0 }} </span> </div> </div> </button> </li> </ul> </div> </div> </div> </div> </div> </div> </div> </div> <!-- sidebar toggle button --> <!-- <div class="d-md-none hide-list-panel"> <button name="button" type="button" class="toggle-mobile-nav" @click="$emit('hide-panel')" > <svg class="s12 icon-chevron-double-lg-right"> <use href="assets/common/img/icons.svg#chevron-double-lg-right" ></use> </svg> <span class="collapse-text mr-2">بستن</span> </button> </div> --> <!-- create new message button --> <div class="btn-group dropup actions-button" data-offset="10,10"> <button type="button" class="btn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" > <!-- plus icon --> <span class="tavasi tavasi-Component-133--1 plus-icon"></span> <!-- muliply icon --> <span class="tavasi tavasi-Component-294--1 multiply-icon"></span> </button> <div class="dropdown-menu dropdown-menu-right"> <!-- Dropdown menu links --> <button @click.prevent="openUserSearchForm()" class="btn dropdown-item new-message" type="button" > <span class="tavasi tavasi-personal"></span> پیام جدید </button> <button @click.prevent="openCreateForm('groups')" class="btn dropdown-item new-group" type="button" > <span class="tavasi tavasi-groups"></span> گروه جدید </button> <!-- <button @click.prevent="openCreateForm('lobbies')" class="btn dropdown-item new-lobby" type="button" > <span class="tavasi tavasi-channel"></span> تالار جدید </button> --> </div> </div> </div> <div class="create-forms" v-if="showForm"> <div class="close-form mb-5"> <button @click.prevent="closeCreateForm()" type="button" class="btn d-flex align-items-center pr-0" > <span class="tavasi tavasi-Component-71--1 ml-2"></span> بازگشت </button> </div> <!-- private chat --> <div v-if="showUserSearchForm"> <div class="form-group"> <label for="users">انتخاب کاربر: </label> <multiselect id="users" track-by="user_id" placeholder="جستجوی..." :show-labels="false" :options="foundUsers" :searchable="true" :loading="isLoading" :internal-search="false" :clear-on-select="false" :close-on-select="true" :options-limit="300" :limit="3" :limit-text="limitText" :max-height="600" :customLabel=" (item) => { return `${item.first_name} ${item.last_name}`; } " @search-change="asyncFind" @select="showUserPrivateMessages" @close="isLoading = false" > <!-- <div slot="tag" slot-scope="{ option, remove }" ><span class="custom__tag" ><span>{{ option.name }}</span ><span class="custom__remove" @click="remove(option)" >❌</span ></span ></div > --> <!-- <div slot="clear" slot-scope="props"> <div class="multiselect__clear" v-if="selectedCountries.length" @mousedown.prevent.stop="clearAll(props.search)" ></div> </div ><span slot="noResult" >Oops! No elements found. Consider changing the search query.</span > --> </multiselect> </div> </div> <!-- finding group --> <div v-if="showGroupSearchForm"> <div class="form-group"> <label for="add-member-group">جستجوی گروه: </label> <multiselect id="add-member-group" track-by="id" label="title" placeholder="جستجوی..." :options="foundGroups" :searchable="true" :loading="isLoading" :internal-search="false" :clear-on-select="false" :close-on-select="true" :options-limit="300" :limit="3" :limit-text="limitText" :max-height="600" @search-change="asyncFindGroup" @select="addGroupToSelectedGroup" @close="isLoading = false" > </multiselect> </div> </div> <!-- group/lobby create/edit form --> <form v-if="showNewGroupForm" @submit.prevent="createItem"> <div class="form-group"> <h6> فرم ایجاد گروه </h6> </div> <div v-if="formType == 'lobbies'" class="form-group"> <label for="exampleFormControlTextarea1">انتخاب گروه: </label> <div class="d-flex align-items-center"> <multiselect :allow-empty="true" :searchable="true" :close-on-select="true" :show-labels="false" label="title" track-by="id" placeholder="انتخاب تالار" v-model="form.parent" :options="groups" :hide-selected="false" :limit-text=" (count) => { return `و ${count} تالار دیگر`; } " :max-height="200" > </multiselect> <div class="dropdown create-group-inner-form"> <button type="button" class="btn" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" > <!-- plus icon --> <span class="tavasi tavasi-Component-133--1 plus-icon"></span> <!-- muliply icon --> <span class="tavasi tavasi-Component-294--1 multiply-icon" ></span> </button> <div class="dropdown-menu "> <div class="form-group"> <label for="form-title">عنوان: </label> <input type="text" class="form-control" id="form-title" aria-describedby="title" v-model.trim="form.title" /> </div> <div class="form-group"> <label for="form-desc">توضیحات: </label> <textarea class="form-control" id="form-desc" rows="3" v-model.trim="form.desc" ></textarea> </div> <div class="dropdown-divider"></div> <div class="d-flex justify-content-between text-center"> <a @click.prevent="createNewGroup" class=" btn btn-primary " href="#" >ذخیره</a > <a @click.prevent="createNewGroup" class=" btn btn-danger " href="#" >انصراف</a > </div> </div> </div> </div> </div> <div class="form-group"> <label for="exampleInputEmail1">عنوان: </label> <input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="title" v-model.trim="form.title" /> <small id="emailHelp" class="form-text text-muted"> این متن فقط جنبه نمایشی دارد. </small> </div> <div class="form-group"> <label for="exampleFormControlTextarea1">توضیحات: </label> <textarea class="form-control" id="exampleFormControlTextarea1" rows="3" v-model.trim="form.desc" ></textarea> </div> <div class="form-group " v-if="form.id"> <div class="custom-checkbox-container position-static"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="enable-add-user-to-group" v-model="showAddUserForm" /> <label class="custom-control-label" for="enable-add-user-to-group" > افزودن کاربر به گروه </label> </div> </div> <!-- private chat --> <div v-if="showAddUserForm"> <div class="form-group"> <label for="users">جستجوی کاربر: </label> <multiselect id="users" track-by="user_id" placeholder="جستجوی..." :show-labels="false" :options="foundUsers" :searchable="true" :internal-search="false" :clear-on-select="false" :close-on-select="true" :options-limit="300" :limit="3" :limit-text="limitText" :max-height="600" :customLabel=" (item) => { return `${item.first_name} ${item.last_name}`; } " @search-change="asyncFind" @select="addUserToGroup" > </multiselect> </div> </div> </div> <button type="submit" class="btn btn-primary"> {{ form.id ? "بروزرسانی" : "ایجاد" }} </button> </form> <!-- upload group/lobby avatar --> <form v-if="showUploadAvatarForm" @submit.prevent="uploadAvatar"> <div class="form-group"> <h5> بارگزاری آواتار </h5> </div> <div class="form-group"> <div class="image-uploader-container" :class="{ 'hide-preview': hasImage }" > <image-uploader ref="file-uploader" :preview="true" :debug="1" :maxWidth="300" :maxHeight="300" :maxSize="2" :quality="1" :autoRotate="true" outputFormat="file" accept="image/*" :className="['fileinput', { 'fileinput--loaded': hasImage }]" :capture="false" @input="setImage" > <label class="upload-label" for="fileInput" slot="upload-label"> <img v-if=" form.avatar?.length && (getFileExtension(form.avatar) == 'png' || getFileExtension(form.avatar) == 'jpg' || getFileExtension(form.avatar) == 'jpeg') " :src="form.avatar" class="img-fluid" :alt="form.avatar" /> <span v-else-if="form.avatar?.length" class="tavasi " :class="`tavasi-${getFileExtension(form.avatar)}-file`" ></span> <!-- replace image src with incoming src --> <span v-else class="tavasi tavasi-cloud-upload"></span> </label> </image-uploader> </div> </div> </form> </div> </div> </template> <script> // import { dragDropMoveMixin } from "~/borhan/mixins/dragDropMoveMixin"; // import { searchMixin } from "~/borhan/mixins/searchMixin"; import ImageUploader from "vue-image-upload-resize"; import chatApi from "~/apis/chatApi"; import { mapActions, mapState } from "pinia"; import privatesContextMenu from "~/json/chat/json/privatesContextMenu.json"; import groupsContextMenu from "~/json/chat/json/groupsContextMenu.json"; import lobbiesContextMenu from "~/json/chat/json/lobbiesContextMenu.json"; import menu from "~/json/chat/json/menu.json"; import { useCommonStore } from "~/stores/commonStore"; import { useAuthStore } from "~/stores/authStore"; export default { // mixins: [dragDropMoveMixin], emits: ["list-changed", "list-item-changed"], props: { statusPagHedear: { default: 1, type: Number, }, }, watch: { $route: { handler: function(to) { this.isRedirectedFromOtherSystems = false; this.getAll(to.name); }, deep: true, // immediate: true, }, }, beforeMount() { 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.httpService = new HttpService( import.meta.env.VITE_MESSAGE_BASE_URL, headers ); this.authHttpService = new HttpService(import.meta.env.VITE_AUTH_BASE_URL); if (this.$route.name == "privates") this.commonContextMenu = privatesContextMenu; if (this.$route.name == "groups") this.commonContextMenu = groupsContextMenu; if (this.$route.name == "lobbies") this.commonContextMenu = lobbiesContextMenu; if (this.$route.name == "unReads") this.commonContextMenu = groupsContextMenu; this.SET_LIST(undefined); }, mounted() { // check if redirected from other systems. // or clicked on a copy link. if (this.$route.query["group-id"]) { this.groupId = this.$route.query["group-id"]; this.isRedirectedFromOtherSystems = true; } this.getAll(this.$route.name); if (this.statusPagHedear == 1) { } }, data() { return { // #region mehdi showMainpag: true, menu: menu, // #endregion typingTimer: 0, doneTypingInterval: 500, searchText: "", serachType: "all", showAddUserForm: false, busy: false, foundGroups: [], showUploadAvatarForm: false, showGroupSearchForm: false, groupsExpanded: false, showNewGroupForm: false, hasImage: false, imageUrl: undefined, uploading: false, selectedUser: {}, foundUsers: [], isLoading: false, showUserSearchForm: false, selectedGroup: undefined, commonContextMenu: [], fetchingData: false, users: [], prevSelectedItemIndex: 0, prevParentSelectedItemIndex: 0, convertGroupIntegerToString: { groups: 1, lobbies: 2, privates: 3, }, convertGroupStringToInteger: { 1: "groups", 2: "lobbies", 3: "privates", }, showForm: false, formType: 1, form: { id: undefined, title: null, desc: null, type: 1, message_state: 1, members: [], parent_id: undefined, }, collapseAll: false, groups: [], showReplaceInput: true, httpService: undefined, authHttpService: undefined, list: [], pagination: { pages: 0, total: 0, page: 1, offset: 0, // page * per_page limit: 50, //per_page }, isRedirectedFromOtherSystems: false, groupId: undefined, messageId: undefined, }; }, computed: { ...mapState(useCommonStore,["isSidebarCollapsed"]), }, methods: { ...mapActions(useCommonStore, ["TOGGLE_PANEL", "checkPermissions"]), ...mapActions("list", ["SET_LIST"]), sendQuery() { // todo: show loading. // todo: send query and show result. // todo: hide loading. clearTimeout(this.typingTimer); this.typingTimer = setTimeout(() => { this.getAll(); }, this.doneTypingInterval); }, onKeyDown() { clearTimeout(this.typingTimer); }, setSearchType(type) { this.serachType = type; }, clearSearchAndGetList() { this.searchText = null; this.getAll(); }, async getAll(listType = "all") { if (this.fetchingData) return; this.fetchingData = true; let url = chatApi[listType].list; // let urlPrefix = chatApi.groups.list; if (!(listType == "lobbies" || listType == "unReads")) url += `${this.pagination.offset}/${this.pagination.limit}`; // else if (listType == "lobbies") urlPrefix = chatApi.lobbies.list // else if (listType == "privates") url += `${this.pagination.offset}/${this.pagination.limit}`; // let url = urlPrefix + `${this.pagination.offset}/${this.pagination.limit}` // const methodType = listType == "unReads" ? 'postRequest' : 'getRequest' return await this.httpService .getRequest(url) .then((response) => { this.groups = response.data; this.users = response.users; this.pagination = { ...this.pagination, ...response.pagination }; // when redirecting from other pages(jahat). if (this.isRedirectedFromOtherSystems) { this.groups.forEach((group) => { if (group.id == this.groupId) { group.active = true; this.SET_LIST(group); } }); } return response; }) .catch((error) => { }) .finally(() => { this.fetchingData = false; this.busy = false; this.showMainpag = true; }); }, setUser(list, responseUsers) { list.forEach((listItem) => { listItem.user = responseUsers.find( (user) => user.user_id == listItem.user ); }); }, createItem(group_id = undefined) { if (this.fetchingData) return; this.fetchingData = true; let url = chatApi[this.$route.name].create; // if (formType == "groups") urlPrefix = // else if (formType == "lobbies") urlPrefix = chatApi.lobbies.create // else if (formType == "privates") urlPrefix = chatApi.privates.create // const payload = structuredClone(this.form); this.form.members = JSON.stringify(this.form.members); this.form.admins = undefined; this.form.group_id = group_id; this.httpService .formDataRequest(url, this.form) .then((res) => { this.fetchingData = false; // this.getAll(this.formType); // this.form = res.data; // this.mySwalToast({ // title: "", // html: res.message, // }); this.showNewGroupForm = false; this.showUploadAvatarForm = true; }) }, // remove group/remove lobby group removeItem(groups, groupItem, index) { this.mySwalConfirm({ title: "هشدار!!!", html: `از حذف <b>${groupItem.title}</b> اطمینان دارید؟ `, icon: "warning", }).then((result) => { if (result.isConfirmed) { const payload = { group_id: groupItem.id, }; this.httpService .postRequest(chatApi.groups.delete, payload) .then((res) => { // this.mySwalToast({ // html: res.message, // icon: "success", // }); this.getAll(); // groups.splice(index, 1); }); } }); }, editItem(groups, groupItem, index) { this.form = structuredClone(groupItem); this.markActive(groups, index); this.openGroupCreateForm(this.convertGroupIntegerToString[this.formType]); }, resetForm() { this.form = { id: undefined, title: null, desc: null, type: 1, message_state: 1, members: [], }; }, loadMore($event) { // const listElm = document.querySelector("#last-search"); const listElm = $event.target; const vm = this; if (vm.busy) return; if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) { this.busy = true; vm.pagination.offset = vm.pagination.offset + vm.pagination.limit; if (vm.pagination.total > vm.pagination.offset) { setTimeout(() => { vm.getListOnScroll(); }, 300); } else { vm.mySwalToast({ title: "کاربر محترم", html: "دیگر رکوردی جهت بارگزاری وجود ندارد.", icon: "info", position: "bottom-start", }); vm.busy = false; } } else vm.busy = false; }, getListOnScroll() { const listType = this.$route.name; let url = chatApi[listType].list; // let urlPrefix = chatApi.groups.list; if (!(listType == "lobbies" || listType == "unReads")) url += `${this.pagination.offset}/${this.pagination.limit}`; // else if (listType == "lobbies") urlPrefix = chatApi.lobbies.list // else if (listType == "privates") url += `${this.pagination.offset}/${this.pagination.limit}`; // let url = urlPrefix + `${this.pagination.offset}/${this.pagination.limit}` // const methodType = listType == "unReads" ? 'postRequest' : 'getRequest' this.httpService .getRequest(url) .then((response) => { this.pagination = { ...this.pagination, ...response.pagination }; this.groups = [...this.groups, ...response.data]; }) .finally(() => { this.busy = false; }); }, openUserSearchForm() { this.showForm = true; this.showUserSearchForm = true; this.formType = "privates"; // this.loading = false; // this.groupCreated = false; }, openGroupSearchForm(formType, groupItem = undefined) { this.showForm = true; this.showGroupSearchForm = true; this.formType = formType; this.selectedGroup = groupItem; }, openGroupCreateForm() { this.showForm = true; this.showNewGroupForm = true; this.formType = "groups"; }, openCreateForm(formType, groupItem = undefined) { // if (formType == 'privates') // this.showUserSearchForm = true; // else if (formType == 'groups') this.showNewGroupForm = true; this.selectedGroup = groupItem; this.showForm = true; this.formType = "groups"; this.loading = false; }, closeCreateForm() { this.showForm = false; this.showNewGroupForm = false; this.showGroupSearchForm = false; this.showUserSearchForm = false; this.showUploadAvatarForm = false; this.loading = false; this.resetForm(); this.$router .push({ name: this.formType, }) .catch(() => {}); }, closeCreateFormWithoutRedirect() { this.showForm = false; this.showNewGroupForm = false; this.showGroupSearchForm = false; this.showUserSearchForm = false; this.showUploadAvatarForm = false; this.loading = false; }, resetPagination() { this.pagination = { pages: 0, total: 0, page: 1, offset: 0, // page * per_page limit: 50, //per_page }; }, toggleSidebarMenu() { this.$store.commit("TOGGLE_SIDEBAR_MENU"); }, markActive(groups, index) { if (this.prevSelectedItemIndex != index) { this.$set(groups[this.prevSelectedItemIndex], "active", false); } this.prevSelectedItemIndex = index; this.$set(groups[index], "active", true); }, showMessages(groups, groupItem, index) { this.markActive(groups, index); this.SET_LIST(groupItem); history.pushState({}, document.title, this.$route.path); this.isRedirectedFromOtherSystems = false; this.$route.query["group-id"] = undefined; this.$route.query["message-id"] = undefined; this.$emit("update-messages"); }, // lobby,lobbyIndex.sub_groups_item, childIndex lobbyShowMessages(lobby, parentIndex, groupItem, index) { // اگر تالارثابت باشه و کاربر بر روی گروه های تالار کلیک کند. if ( this.prevParentSelectedItemIndex == parentIndex && this.prevSelectedItemIndex != index ) { this.$set( lobby[this.prevParentSelectedItemIndex].sub_groups[ this.prevSelectedItemIndex ], "active", false ); } // کاربر بر روی تالار دیگری کلیک کرده است. else if (this.prevParentSelectedItemIndex != parentIndex) { this.$set( lobby[this.prevParentSelectedItemIndex].sub_groups[ this.prevSelectedItemIndex ], "active", false ); } this.prevSelectedItemIndex = index; this.prevParentSelectedItemIndex = parentIndex; this.$set(lobby[parentIndex].sub_groups[index], "active", true); this.SET_LIST(groupItem); this.$emit("update-messages"); }, // after finding the user, on select user event, show the user's messages showUserPrivateMessages(user) { user.user = user.user_id; // user.id = user.user_id; user.title = userFullname(user); user.desc = null; this.SET_LIST(user); this.groups.forEach((groupItem) => { groupItem.active = false; if (groupItem.user == user.id) groupItem.active = true; }); this.closeCreateFormWithoutRedirect(); this.$emit("update-messages"); }, asyncFindGroup(query) { if (this.isLoading) return; this.isLoading = true; if (query.trim().length) { this.httpService .getRequest("group/ve/search/1/" + query) .then((response) => { this.foundGroups = response.data ?? []; }) .catch((err) => { }) .finally(() => { this.isLoading = false; }); } }, // add group to a group(making a lobby) addGroupToSelectedGroup(comboSelectedGroup) { if (this.fetchingData) return; this.fetchingData = true; let url = chatApi[this.$route.name].parent; const payload = { parent_id: comboSelectedGroup.id, group_id: this.selectedGroup.id, }; this.httpService .formDataRequest(url, payload) .then((res) => { // this.mySwalToast({ // title: "", // html: res.message, // }); this.closeCreateForm(); }) .finally(() => { this.fetchingData = false; }); }, // remove from lobby removeGroupFromLobby(group) { this.mySwalConfirm({ title: "هشدار!!!", html: ` حذف <b>${group.title}</b> ?`, icon: "warning", }).then((result) => { if (result.isConfirmed) { if (this.fetchingData) return; this.fetchingData = true; let url = chatApi[this.$route.name].delete; const payload = { group_id: group.id, parent_id: 0, }; this.httpService .formDataRequest(url, payload) .then((res) => { this.fetchingData = false; // this.mySwalToast({ // html: res.message, // }); this.getAll("lobbies"); }) } }); }, replayCount(replays) { let sum = 0; replays.forEach((item) => { sum += item.count; }); return sum; }, limitText(count) { return `و ${count} کاربر دیگر`; }, asyncFind(query) { if (this.isLoading) return; this.isLoading = true; if (query.trim().length) { this.authHttpService .postRequest("user/suggestion", { query }) .then((response) => { this.isLoading = false; this.foundUsers = response.data ?? []; }) .catch((err) => { }) } }, isGroupAdmin(comment) { const isAdmin = comment.admins.findIndex((user) => user == this.currentUser.user_id) > -1; return isAdmin; }, getFileExtension(filename) { const validtypes = [ "xlsx", "xls", "doc", "docx", "ppt", "pptx", "txt", "pdf", "jpg", "png", "jpeg", ]; const fileExtension = filename?.split(".").pop(); return validtypes.includes(fileExtension) ? fileExtension : "other"; }, setImage(file) { this.hasImage = true; this.imageUrl = file; this.form.avatar = file.name; this.saveFile(file); }, saveFile() { if (this.uploading) return; this.uploading = true; let url = chatApi.groups.updateAvatar + this.form.id; const formData = new FormData(); formData.append("file", this.$refs["file-uploader"].currentFile); this.httpService .postRequest(url, formData, { headers: { "Content-Type": "multipart/form-data", }, }) .then((res) => { // this.mySwalToast({ // title: res.message, // html: "", // }); this.closeCreateForm(); // this.getAll(); }) .catch((err) => { }) .finally(() => { this.uploading = false; }); }, addUserToGroup(selectedUser) { if (this.fetchingData) return; this.fetchingData = true; let url = chatApi.groups.addMember; const payload = { group_id: this.form.id, members: [selectedUser.id], }; this.httpService .postRequest(url, payload) .then((res) => { // this.mySwalToast({ // title: "", // html: res.message, // }); // this.closeCreateForm(); // this.getAll(); }) .catch((err) => { }) .finally(() => { this.isLoading = false; this.fetchingData = false; }); }, // #region mehdi showfilter() { this.showMainpag = !this.showMainpag; }, showSearchs() { this.showSearch = false; this.$refs.search.classList.toggle("hidean-search"); }, // #endregion }, }; </script> <style scoped lang="scss"> @import "../../../../assets/common/scss/mixin.scss"; .group-item { .group-row { display: flex; align-items: center; width: 100%; max-width: 100%; margin-bottom: 0.3em; &.enable-hover:hover { .group-picture-container { .context-menu-dropdown { display: flex; } .group-picture { display: none; } } .group-content { background-color: #f0f0f0; } } .group-picture-container { display: flex; align-items: center; justify-content: center; border-radius: 50%; // margin-left: 0.5em; border: 1px solid #ccc; width: 2.3em; height: 2.3em; position: relative; background-color: #fff; .context-menu-dropdown { display: none; } .group-picture { object-fit: cover; object-position: center; overflow: hidden; border-radius: 50%; width: 100%; height: 100%; } } .group-content { flex: 1; border-radius: 0; position: relative; padding: 0.3em; &:active, &.active { background-color: #d8f8fd; } .group-title-container { display: flex; align-items: center; justify-content: space-between; .group-title { // display: flex; flex: 1; text-align: right; // align-items: center; // font-size: 0.7rem; font-size: 0.8rem; margin-bottom: 0.3em; // font-family: "sahel-bold"; font-family: "sahel-semi-bold"; color: #444; @include textOverflow(13em); .tavasi { font-size: 0.8rem; margin-left: 0.3em; } } .group-time { font-size: 0.7rem; font-family: "sahel-light"; } .unreads-box { display: flex; align-items: center; .btn { padding: 0; margin: 0; margin-right: 0.5em; color: #888; svg { font-size: 0.7rem; } } } } .group-description-container { display: flex; align-items: center; justify-content: space-between; .group-unread-indicator { font-size: 0.7rem; background-color: #00b6e3; font-family: "sahel-light"; display: flex; justify-content: center; align-items: center; padding-top: 0.4em; } .group-description { color: #6f6f6f; font-size: 0.8em; margin: 0; flex: 1; text-align: right; @include textOverflow(15em); } } .unread-button { .unread-message-label { font-size: 0.7rem; color: #6f6f6f; margin-left: 0.3em; line-height: 1; } display: flex; align-items: center; justify-content: flex-start; margin-bottom: 0.2em; font-size: 0.9rem; line-height: 1.2; } } } } #lobby-accordion { & > .card { overflow: visible; } .list-group-item { border-radius: 0; &:not(:last-child) { border-bottom: 1px dashed #ddd; } // &:last-child { // .group-content { // &::before{ // content:none; // } // } // } } .card-header { .group-row:hover { .group-content { background-color: transparent; } } } .card-body { .group-row { &:hover { } &:active, &.active { } .group-picture-container { .group-picture { // width: 3em; // height: 3em; } } .group-content { .group-title-container { .group-title { font-size: 0.8rem; .tavasi { font-size: 0.7rem; // margin-left: 0.3em; } } .group-time { font-size: 0.7rem; } } .group-description-container { .group-unread-indicator { font-size: 0.7rem; } .group-description { font-size: 0.7rem; } } } } } } .menu-bar__content { position: static; flex: 1 1 100%; max-width: 25em; max-width: 20em; width: auto; min-width: 20em; &.show-list-panel { right: 0 !important; } } .home-list__content { /*max-height: calc(100vh - 12em);*/ height: calc(100vh - 8.5em); position: relative; overflow-x: hidden; padding-left: 0.3em; &.loading { //background-image: url('./img/item-loading.svg'); background-repeat: repeat-y; background-position: top right; background-size: 12em; &::before { content: ""; clear: both; position: absolute; right: 0; width: 0.5em; height: 100%; background-color: #fff; animation-name: example; animation-duration: 2s; animation-fill-mode: forwards; animation-iteration-count: infinite; animation-timing-function: ease-in-out; } } &:hover .actions-button { opacity: 1; visibility: visible; transition: all 0.2s; } } .actions-button { position: absolute; bottom: 1em; left: 1em; z-index: 1; width: 2.3em; height: 2.3em; background-color: #00b6e3; border-radius: 50%; opacity: 0; visibility: hidden; transition: all 0.2s; & > .btn { display: flex; align-items: center; justify-content: center; padding: 0; } .plus-icon { display: inline; } .multiply-icon { display: none; } &.show { .plus-icon { display: none; } .multiply-icon { display: inline; } } .btn[data-toggle="dropdown"] { .tavasi::before { color: #fff; } } .dropdown-menu.show { // top: -10px !important; // padding-top: 1.5em; // padding-bottom: 1.5em; } .dropdown-item { text-align: right; font-family: "sahel-semi-bold"; display: flex; align-items: center; margin-bottom: 0.7em; font-size: 0.8rem; color: #666; .tavasi { margin-left: 0.5em; } } } .search-filter { .input-group-text { display: flex; align-items: center; justify-content: center; font-size: 0.8rem; // border: 1px solid #ddd; } .input-group-append { .input-group-text { border-top-right-radius: 0.5em; border-bottom-right-radius: 0.5em; background-color: transparent; } } .input-group-prepend { .input-group-text { background-color: transparent; border-top-left-radius: 0.5em; border-bottom-left-radius: 0.5em; } } .form-control { border-left: 0; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; &::-webkit-search-decoration, &::-webkit-search-cancel-button, &::-webkit-search-results-button, &::-webkit-search-results-decoration { -webkit-appearance: none; } } .dropdown-item { text-align: right; font-family: "sahel-semi-bold"; display: flex; align-items: center; margin-bottom: 0.7em; font-size: 0.8rem; .icon { margin-left: 0.2em; } } } .create-forms { position: fixed; right: 3.5em; top: 0; bottom: 0; min-width: 20em; background-color: #fff; width: 0; width: 100%; max-width: 20em; z-index: 2; overflow: hidden; padding: 1em 2em; .form-control, label, .btn { font-size: 0.8rem; } } .create-group-inner-form { & > .btn { margin-right: 0.2em; display: flex; align-items: center; justify-content: center; padding: 0; width: 2em; height: 2em; background-color: #00b6e3; border-radius: 50%; } .plus-icon { display: inline; } .multiply-icon { display: none; } &.show { .plus-icon { display: none; } .multiply-icon { display: inline; } } .btn[data-toggle="dropdown"] { .tavasi::before { color: #fff; } } .dropdown-menu.show { width: 17em; text-align: right; padding: 1.5em 1em; } .dropdown-item { text-align: right; display: flex; align-items: center; margin-bottom: 0.7em; color: #666; .tavasi { margin-left: 0.5em; } } } </style> <style lang="scss"> .picture-input input[type="file"], #fileInput { display: none !important; } .image-uploader-container { position: relative; // border: 2px dashed #eee; padding: 0.2em; &.hide-preview { .upload-label { position: absolute; right: 0; left: 0; top: 0; bottom: 0; width: auto !important; overflow: hidden; .tavasi, img { visibility: hidden; } } } .upload-label { .tavasi { font-size: 4rem; } &:hover { cursor: pointer; } } .img-preview { width: 300px; max-width: 100%; height: auto; max-height: 200px; object-fit: contain; } } </style> <!-- start: mehdi --> <style scoped lang="scss"> .input-group { display: flex; flex-direction: row; justify-content: space-evenly; flex-wrap: nowrap; } .nav-icon-container { font-size: 0.875rem; color: #a0acb8; } //mehdi @media (min-width: 992px) { .open { display: none; } .home-list__header { padding-top: 1em; } } @media only screen and (min-width: 768px) and (max-width: 991.98px) { .hidden-heder { display: none; } .menu-bar__content { position: fixed; top: 0rem !important; right: 3rem !important; right: 2rem; background-color: #fff; z-index: 9; } .open { display: none; } } @media only screen and (min-width: 576px) and (max-width: 766.98px) { .hidden-heder { display: none; } .menu-bar__content { position: fixed; right: 0rem !important; background-color: #fff; z-index: 9; // max-width: 25em; // max-width: 20em; width: auto; min-width: 100% !important; } .navbar { width: 100%; height: 4.1rem; position: relative; left: 0.3rem; display: flex; align-items: center; //background-color: red; .navbar-grow { width: 100%; display: flex; justify-content: space-evenly; div { margin-left: 1.2rem; } } } .hover-border { position: relative; border: 2px solid transparent; border-radius: 5px; transition: transform 0.3s ease; } .hover-border:hover { transform: translateY(-5px); .nav-icon-container { color: black !important; } } .search-main { display: flex; flex-direction: row-reverse; position: absolute; z-index: 999; top: 24px; background-color: #fff; width: 100%; justify-content: center; // background-color: red; .button-search { position: relative; width: 35px; height: 31px; margin-right: 5px; margin-left: 5px; margin-top: 2px; border: none; background-color: #fff; &:hover { border-radius: 5px; border: 1px solid #a0acb8; background-color: #a0acb8; color: #fff; } span { position: absolute; top: 3px; right: 0px; } } } .hidean-search { display: none !important; } .hover-border1 { position: relative; top: 7px; } } @media (max-width: 575.98px) { .hidden-heder { display: none; } .menu-bar__content { position: fixed; right: 0rem !important; background-color: #fff; z-index: 9; // max-width: 25em; // max-width: 20em; width: auto; min-width: 100% !important; } .navbar { width: 100%; height: 4.1rem; position: relative; left: 0.3rem; display: flex; align-items: center; //background-color: red; .navbar-grow { width: 100%; display: flex; justify-content: space-between; div { margin-left: 1.2rem; } } } .hover-border { position: relative; border: 2px solid transparent; border-radius: 5px; } .hover-border:hover { .nav-icon-container { color: black !important; } } .hover-border1 { position: relative; top: 6px; right: 6px; } .search-main { display: flex; flex-direction: row-reverse; position: absolute; z-index: 999; top: 24px; background-color: #fff; // background-color: red; .button-search { position: relative; width: 35px; height: 31px; margin-right: 5px; margin-left: 5px; margin-top: 2px; border: none; background-color: #fff; &:hover { border-radius: 5px; border: 1px solid #a0acb8; background-color: #a0acb8; color: #fff; } span { position: absolute; top: 3px; right: 0px; } } } .hidean-search { display: none !important; } .home-list__content { top: -14px; } .navbar { top: -14px; } } //mehdi </style>