Последняя версия с сервера прошлого разработчика
This commit is contained in:
150
resources/js/Shared/Overlay/ModalFeed.vue
Executable file
150
resources/js/Shared/Overlay/ModalFeed.vue
Executable file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<TransitionRoot as="template" :show="open">
|
||||
<Dialog as="div" static
|
||||
class="fixed z-[1000] inset-0 overflow-y-auto" :open="open"
|
||||
@close="staticCloseModal"
|
||||
>
|
||||
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
enter-from="opacity-0" enter-to="opacity-100"
|
||||
leave="ease-in duration-200" leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<DialogOverlay class="fixed inset-0 bg-indigo-200 bg-opacity-75 transition-opacity" />
|
||||
</TransitionChild>
|
||||
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
||||
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enter-to="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200" leave-from="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div class="inline-block align-bottom bg-indigo-300 md:rounded-md text-left overflow-hidden shadow-xl transform transition-all md:align-middle md:max-w-6xl w-full">
|
||||
<div class="md:grid grid-cols-12">
|
||||
<button class="flex md:hidden w-full items-center justify-center p-3 bg-indigo-300 text-gray-light text-sm border-b border-indigo-100" @click="staticCloseModal">
|
||||
Закрыть
|
||||
</button>
|
||||
|
||||
<modal-feed-media
|
||||
:type="feed.type"
|
||||
:feed_id="feed.id"
|
||||
:title="feed.entity.title"
|
||||
:preview="feed.entity.preview"
|
||||
:medias="feed.entity.collection_medias"
|
||||
/>
|
||||
|
||||
<modal-feed-body
|
||||
:user="feed.user"
|
||||
:feed_id="feed.id"
|
||||
:entity="feed.entity"
|
||||
@openShare="showShareModal"
|
||||
@onRemoveFeed="onRemoveFeed"
|
||||
@disableModal="checkCloseModal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
<modal-warning
|
||||
:feed_id="modalFeedId"
|
||||
:open="warningShow"
|
||||
@action="deleteActionModal"
|
||||
/>
|
||||
<modal-share ref="shareModalNode" :entity="feed.entity" />
|
||||
</Dialog>
|
||||
</TransitionRoot>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
} from '@headlessui/vue'
|
||||
|
||||
import ModalWarning from '@/Shared/Overlay/ModalWarning.vue'
|
||||
import ModalShare from '@/Shared/Overlay/ModalShare.vue'
|
||||
import ModalFeedMedia from '@/Shared/Overlay/ModalFeedMedia.vue'
|
||||
import ModalFeedBody from '@/Shared/Overlay/ModalFeedBody.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
ModalFeedMedia,
|
||||
ModalFeedBody,
|
||||
ModalWarning,
|
||||
ModalShare,
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
is_exist_menu: this.is_exist_menu
|
||||
}
|
||||
},
|
||||
props: {
|
||||
modalFeed: Object,
|
||||
is_exist_menu: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['closeModal', 'destroyFeed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
disabled: 0,
|
||||
modalFeedId: 0,
|
||||
warningShow: false,
|
||||
feed: {},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
open(open) {
|
||||
if (open) {
|
||||
this.feed = this.modalFeed
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
deleteActionModal(removeFeed) {
|
||||
if (removeFeed) {
|
||||
this.$emit('destroyFeed')
|
||||
this.$nextTick(() => {
|
||||
this.staticCloseModal()
|
||||
})
|
||||
}
|
||||
this.warningShow = false
|
||||
},
|
||||
|
||||
showShareModal() {
|
||||
this.$refs.shareModalNode.openModal()
|
||||
},
|
||||
|
||||
onRemoveFeed(feed_id) {
|
||||
this.modalFeedId = feed_id
|
||||
this.warningShow = true
|
||||
},
|
||||
|
||||
checkCloseModal(value) {
|
||||
this.disabled = value
|
||||
},
|
||||
staticCloseModal() {
|
||||
if (!this.disabled) {
|
||||
this.$emit('closeModal', false)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
361
resources/js/Shared/Overlay/ModalFeedBody.vue
Executable file
361
resources/js/Shared/Overlay/ModalFeedBody.vue
Executable file
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<div class="col-span-5 modal-body flex flex-col">
|
||||
<div class="modal-head mb-3 px-2 py-4 md:py-4 md:px-6 flex justify-between items-center border-b border-indigo-100">
|
||||
<div v-if="entity.is_ads" class="flex items-center space-x-4">
|
||||
<user-avatar :user="user" size="small"
|
||||
class="w-12 h-12 md:w-16 md:h-16 border-2 border-orange text-lg"
|
||||
/>
|
||||
<p class="text-base md:text-xl font-semibold text-gray">
|
||||
{{ user.name }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else class="flex items-center space-x-4">
|
||||
<inertia-link :href="route('profile.user', user.username)" class="flex-shrink-0 block">
|
||||
<user-avatar :user="user" size="small"
|
||||
class="w-12 h-12 md:w-16 md:h-16 border-2 border-orange text-lg"
|
||||
/>
|
||||
</inertia-link>
|
||||
<inertia-link :href="route('profile.user', user.username)" class="flex flex-col">
|
||||
<p class="text-base md:text-xl font-semibold text-gray">
|
||||
{{ user.name }}
|
||||
</p>
|
||||
<p class="min-h-[24px]">
|
||||
<span v-show="user.count_posts" class="md:mt-1 text-sm text-gray-light">{{ user.count_posts }} {{ countPosts }}</span>
|
||||
</p>
|
||||
</inertia-link>
|
||||
</div>
|
||||
<div v-if="is_exist_menu && entity.is_ads == false" class="text-white">
|
||||
<dropdown-menu-point v-if="$page.props.auth.user">
|
||||
<MenuItems class="origin-top-right absolute right-0 mt-2 w-64 bg-indigo-200 shadow-lg max-h-60 rounded-md text-base ring-1 ring-indigo-100 overflow-auto focus:outline-none">
|
||||
<MenuItem v-if="user.id === $page.props.auth.user.id">
|
||||
<inertia-link :href="route(`${entity.type}.edit`, entity.slug)" class="group flex items-center px-4 py-2 text-base hover:bg-indigo-300 text-gray-light">
|
||||
<PencilAltIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
|
||||
Редактировать
|
||||
</inertia-link>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem v-if="user.id === $page.props.auth.user.id">
|
||||
<button class="w-full group flex items-center px-4 py-2 text-base hover:bg-indigo-300 text-gray-light" @click="onRemoveFeed()">
|
||||
<MinusCircleIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
|
||||
Удалить
|
||||
</button>
|
||||
</MenuItem>
|
||||
<MenuItem v-if="user.id !== $page.props.auth.user.id">
|
||||
<inertia-link :href="route('complaint.reason', feed_id)" class="group flex items-center px-4 py-2 text-base hover:bg-indigo-300 text-gray-light">
|
||||
<ExclamationIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
|
||||
Пожаловаться
|
||||
</inertia-link>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</dropdown-menu-point>
|
||||
</div>
|
||||
</div>
|
||||
<!-- -->
|
||||
<!-- max-h-[20rem] h-[20rem] -->
|
||||
<div data-simplebar class="modal-feed min-h-[4rem] md:min-h-[22rem] max-h-[22rem] overflow-auto">
|
||||
<div class="p-2 md:py-4 md:px-6 space-y-3 md:space-y-6">
|
||||
<div v-if="entity.tags.length" class="">
|
||||
<feed-tags :tags="entity.tags" />
|
||||
</div>
|
||||
|
||||
<div v-if="entity.body" class="comment-line comment-line--head flex flex-col text-gray text-sm">
|
||||
<div v-if="entity.is_ads" class="prose-content"
|
||||
v-html="entity.body"
|
||||
></div>
|
||||
<div v-else class="flex group">
|
||||
<inertia-link :href="route('profile.user', user.username)" class="flex-shrink-0 block mr-3">
|
||||
<user-avatar :user="user" size="small"
|
||||
class="w-10 h-10 border border-orange text-xs"
|
||||
/>
|
||||
</inertia-link>
|
||||
<div>
|
||||
<inertia-link :href="route('profile.user', user.username)" class="font-semibold underline inline-block mr-2">
|
||||
{{ user.username }}
|
||||
</inertia-link>
|
||||
{{ entity.body }}
|
||||
<div class="transition-opacity opacity-0 group-hover:opacity-100 flex space-x-4">
|
||||
<span class="text-xs text-gray-light">{{ entity.created_at_humans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-for="comment in comments" :key="comment.id"
|
||||
class="comment-line flex flex-col text-gray text-sm"
|
||||
>
|
||||
<user-comment :creator_id="user.id" :comment="comment"
|
||||
@showChildren="loadChildrenComment" @toAnswer="sendAnswerParent"
|
||||
@removeCommentItem="removeCommentItem"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="cursor" class="flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
class="cursor-pointer text-white" @click="loadComments"
|
||||
><circle cx="12" cy="12"
|
||||
r="10"
|
||||
></circle><line x1="12" y1="8"
|
||||
x2="12" y2="16"
|
||||
></line><line x1="8" y1="12"
|
||||
x2="16" y2="12"
|
||||
></line></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="$page.props.auth.user" class="modal-footer mt-auto p-2 md:py-4 md:px-6 border-t border-indigo-100">
|
||||
<feed-paid-block
|
||||
:is_paid="entity.is_paid"
|
||||
:user_id="user.id"
|
||||
:price="entity.price"
|
||||
:feed_id="feed_id"
|
||||
:paid_open="entity.paid_open"
|
||||
class="text-white"
|
||||
@onReplaceFeed="onReplaceFeed"
|
||||
/>
|
||||
|
||||
<div class="misc-info mt-2 md:mt-0 mb-2 flex flex-row-reverse">
|
||||
<div class="flex space-x-4">
|
||||
<template v-if="entity.is_ads == false">
|
||||
<like-count :likes="entity.likes" :liked="entity.liked"
|
||||
@likeFeed="likeFeed"
|
||||
/>
|
||||
<comment-count :comments="entity.comments" />
|
||||
<share-count @click="openShareModal" />
|
||||
</template>
|
||||
<view-count :count="entity.views_count" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="entity.is_ads == false" class="modal-send flex">
|
||||
<div class="flex-1">
|
||||
<textarea v-model="new_comment"
|
||||
class="pl-0 pr-2 resize-none bg-indigo-300 w-full outline-none focus:ring-transparent focus:border-transparent focus:ring-offset-0 text-base text-gray border-transparent" placeholder="Написать комментарий..."
|
||||
> </textarea>
|
||||
</div>
|
||||
<div class="mt-1 flex-shrink-0 text-pink">
|
||||
<button class="default" @click="addComment">
|
||||
<svg class="w-8 h-8 flex-shrink-0">
|
||||
<use xlink:href="#arrow-circle"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="modal-footer mt-auto p-2 md:py-4 md:px-6 border-t border-indigo-100 flex justify-center">
|
||||
<a rel="nofollow" href="/login"
|
||||
class="my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-1 md:py-3 justify-center text-sm md:text-base rounded-md text-white bg-pink focus:outline-none"
|
||||
>
|
||||
Купить
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Inertia } from '@inertiajs/inertia'
|
||||
import axios from 'axios'
|
||||
import { MenuItem, MenuItems } from '@headlessui/vue'
|
||||
import {
|
||||
MinusCircleIcon,
|
||||
ExclamationIcon,
|
||||
PencilAltIcon,
|
||||
} from '@heroicons/vue/solid'
|
||||
import helper from '@/includes/helper'
|
||||
|
||||
import DropdownMenuPoint from '@/Shared/Form/DropdownMenuPoint.vue'
|
||||
import FeedPaidBlock from '@/Shared/FeedList/FeedPaidBlock.vue'
|
||||
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
|
||||
import LikeCount from '@/Shared/Misc/LikeCount.vue'
|
||||
import FeedTags from '@/Shared/Misc/FeedTags.vue'
|
||||
import CommentCount from '@/Shared/Misc/CommentCount.vue'
|
||||
import ShareCount from '@/Shared/Misc/ShareCount.vue'
|
||||
import UserComment from '@/Shared/Partials/UserComment.vue'
|
||||
import ViewCount from '@/Shared/Misc/ViewCount.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DropdownMenuPoint,
|
||||
MenuItem,
|
||||
MenuItems,
|
||||
MinusCircleIcon,
|
||||
ExclamationIcon,
|
||||
PencilAltIcon,
|
||||
UserAvatar,
|
||||
LikeCount,
|
||||
ShareCount,
|
||||
CommentCount,
|
||||
UserComment,
|
||||
FeedTags,
|
||||
FeedPaidBlock,
|
||||
ViewCount,
|
||||
},
|
||||
inject: ['is_exist_menu'],
|
||||
props: {
|
||||
user: Object,
|
||||
entity: Object,
|
||||
feed_id: Number,
|
||||
},
|
||||
emits: ['disableModal', 'onRemoveFeed', 'openShare'],
|
||||
data() {
|
||||
return {
|
||||
new_comment: null,
|
||||
to_user: null,
|
||||
parent_send_comment: null,
|
||||
comments: [],
|
||||
cursor: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
countPosts() {
|
||||
return helper.declNumPosts(this.user.count_posts)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if(this.entity.is_ads == false){
|
||||
this.loadComments()
|
||||
this.loadCountPostsUser()
|
||||
}
|
||||
|
||||
this.addView()
|
||||
},
|
||||
unmounted() {
|
||||
this.comments = []
|
||||
this.cursor = null
|
||||
},
|
||||
methods: {
|
||||
openShareModal(){
|
||||
this.$emit('openShare')
|
||||
},
|
||||
sendAnswerParent(id, to_user_id, username) {
|
||||
this.parent_send_comment = id
|
||||
this.to_user = to_user_id
|
||||
|
||||
if (this.new_comment) {
|
||||
this.new_comment = `@${username} ${this.new_comment}`
|
||||
} else {
|
||||
this.new_comment = `@${username} `
|
||||
}
|
||||
},
|
||||
onReplaceFeed(data) {
|
||||
this.entity.collection_medias = data.collection
|
||||
this.entity.preview = data.preview
|
||||
this.entity.paid_open = 1
|
||||
},
|
||||
|
||||
loadCountPostsUser() {
|
||||
if (
|
||||
typeof this.user.count_posts === 'undefined' ||
|
||||
parseInt(this.user.count_posts, 10) === 0
|
||||
) {
|
||||
axios.post(route('user.posts.count', this.user.id)).then(({ data }) => {
|
||||
this.user.count_posts = data
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
onRemoveFeed() {
|
||||
this.$emit('onRemoveFeed', this.feed_id)
|
||||
},
|
||||
loadComments() {
|
||||
this.$emit('disableModal', 1)
|
||||
const that = this
|
||||
axios.post(route('comments.show', this.feed_id), { cursor: this.cursor }).then(({ data }) => {
|
||||
data.items.forEach(element => {
|
||||
that.comments.push(element)
|
||||
})
|
||||
that.cursor = data.nextCursor
|
||||
that.$emit('disableModal', 0)
|
||||
})
|
||||
},
|
||||
|
||||
loadChildrenComment(id, items) {
|
||||
const index = this.comments.findIndex((comment) => comment.id === id)
|
||||
this.comments[index].closed_children_comments = 0
|
||||
items.forEach(element => {
|
||||
this.comments[index].childrens.push(element)
|
||||
})
|
||||
},
|
||||
removeCommentItem(id, remove_count = 1, parent_id = null) {
|
||||
if(parent_id){
|
||||
const index = this.comments.findIndex((comment) => comment.id === parent_id)
|
||||
const indexChild = this.comments[index].childrens.findIndex((comment) => comment.id === id)
|
||||
this.comments[index].childrens.splice(indexChild, 1)
|
||||
}else{
|
||||
const index = this.comments.findIndex((comment) => comment.id === id)
|
||||
this.comments.splice(index, 1)
|
||||
}
|
||||
this.entity.comments = this.entity.comments - remove_count
|
||||
},
|
||||
addView() {
|
||||
axios
|
||||
.post(route('add.view.feed', this.feed_id))
|
||||
.then(({ data }) => {
|
||||
data && this.entity.views_count++
|
||||
})
|
||||
},
|
||||
addComment() {
|
||||
if (!this.new_comment) {
|
||||
return false
|
||||
}
|
||||
this.$emit('disableModal', 1)
|
||||
axios
|
||||
.post(route('comments.store', this.feed_id), {
|
||||
body: this.new_comment,
|
||||
parent: this.parent_send_comment,
|
||||
to: this.to_user,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (this.parent_send_comment) {
|
||||
const index = this.comments.findIndex(
|
||||
(comment) => comment.id === this.parent_send_comment
|
||||
)
|
||||
this.comments[index].childrens.push(data)
|
||||
} else {
|
||||
this.comments.push(data)
|
||||
}
|
||||
this.entity.comments++
|
||||
this.parent_send_comment = null
|
||||
this.to_user = null
|
||||
this.$emit('disableModal', 0)
|
||||
})
|
||||
this.new_comment = null
|
||||
},
|
||||
|
||||
likeFeed() {
|
||||
|
||||
axios
|
||||
.post(route('feed.like', this.feed_id))
|
||||
.then(() => {
|
||||
if (this.entity.liked) {
|
||||
this.entity.liked = false
|
||||
this.entity.likes--
|
||||
} else {
|
||||
this.entity.liked = true
|
||||
this.entity.likes++
|
||||
}
|
||||
})
|
||||
|
||||
// Inertia.post(
|
||||
// route('feed.like', this.feed_id),
|
||||
// {},
|
||||
// {
|
||||
// preserveScroll: true,
|
||||
// preserveState: true,
|
||||
// }
|
||||
// )
|
||||
// if (this.entity.liked) {
|
||||
// this.entity.liked = false
|
||||
// this.entity.likes--
|
||||
// } else {
|
||||
// this.entity.liked = true
|
||||
// this.entity.likes++
|
||||
// }
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
38
resources/js/Shared/Overlay/ModalFeedMedia.vue
Executable file
38
resources/js/Shared/Overlay/ModalFeedMedia.vue
Executable file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="col-span-7 modal-media modal-media-height border-r border-indigo-100">
|
||||
<component
|
||||
:title="title"
|
||||
:feed_id="feed_id"
|
||||
:preview="preview"
|
||||
:medias="medias"
|
||||
:is="currentModalMedia"
|
||||
></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalFeedMediaImages from "@/Shared/Overlay/ModalFeedMediaImages.vue";
|
||||
import ModalFeedMediaVideos from "@/Shared/Overlay/ModalFeedMediaVideos.vue";
|
||||
import ModalFeedMediaMusics from "@/Shared/Overlay/ModalFeedMediaMusics.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModalFeedMediaImages,
|
||||
ModalFeedMediaVideos,
|
||||
ModalFeedMediaMusics,
|
||||
},
|
||||
props: {
|
||||
medias: Array,
|
||||
type: String,
|
||||
feed_id: Number,
|
||||
title: String,
|
||||
preview: String,
|
||||
},
|
||||
computed: {
|
||||
currentModalMedia() {
|
||||
return "modal-feed-media-" + this.type.toLowerCase();
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
33
resources/js/Shared/Overlay/ModalFeedMediaImages.vue
Executable file
33
resources/js/Shared/Overlay/ModalFeedMediaImages.vue
Executable file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<swiper class="h-full" :grabCursor="true" navigation :pagination="{ clickable: true }">
|
||||
<swiper-slide v-for="media in medias" :key="media.id">
|
||||
<img class="w-full h-full object-contain" :src="media.url" alt="" />
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import SwiperCore, { Navigation, Pagination } from 'swiper/core';
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
|
||||
import "swiper/swiper-bundle.css";
|
||||
// import "swiper/components/navigation/navigation.min.css";
|
||||
// import "swiper/components/pagination/pagination.min.css";
|
||||
|
||||
SwiperCore.use([Navigation, Pagination]);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Swiper,
|
||||
SwiperSlide,
|
||||
},
|
||||
props: {
|
||||
medias: Array,
|
||||
title: String,
|
||||
preview: String,
|
||||
},
|
||||
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
110
resources/js/Shared/Overlay/ModalFeedMediaMusics.vue
Executable file
110
resources/js/Shared/Overlay/ModalFeedMediaMusics.vue
Executable file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="modal-media h-full">
|
||||
<div class="flex items-center border-b border-indigo-100">
|
||||
<div class="flex-shrink-0 mr-5">
|
||||
<feed-preview
|
||||
class="h-28 w-28 md:w-56 md:h-56 object-cover"
|
||||
type="music"
|
||||
:source="preview"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-base md:text-xl font-semibold text-gray">{{
|
||||
title
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-simplebar class="max-h-96 overflow-auto">
|
||||
<div class="divide-y divide-indigo-100">
|
||||
<div
|
||||
@click.prevent="startPlay(media)"
|
||||
v-for="media in medias"
|
||||
:key="media.id"
|
||||
:class="[
|
||||
currentSong?.id === media.id
|
||||
? 'bg-indigo-200 bg-opacity-25'
|
||||
: 'hover:bg-indigo-200 hover:bg-opacity-25',
|
||||
'p-4 flex items-center space-x-4',
|
||||
]"
|
||||
>
|
||||
<div class="flex">
|
||||
<toggle-play-button :media_id="media.id" />
|
||||
</div>
|
||||
<div class="flex-1 text-sm text-gray-light">
|
||||
{{ media.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-light">
|
||||
<div v-show="currentSong?.id === media.id">{{seek}}</div>
|
||||
<div v-show="currentSong?.id !== media.id">{{media.time}}</div>
|
||||
</div>
|
||||
<music-add-count v-if="$page.props.auth.user" @addAudio="likeAudio" :media_id='media.id' :liked='media.liked' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState, mapGetters } from "vuex";
|
||||
import { Inertia } from "@inertiajs/inertia";
|
||||
import find from "lodash/find";
|
||||
|
||||
import FeedPreview from "@/Shared/Feed/FeedPreview.vue";
|
||||
import TogglePlayButton from "@/Shared/Misc/TogglePlayButton.vue";
|
||||
import MusicAddCount from "@/Shared/Misc/MusicAddCount.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FeedPreview,
|
||||
TogglePlayButton,
|
||||
MusicAddCount,
|
||||
},
|
||||
props: {
|
||||
medias: Array,
|
||||
title: String,
|
||||
preview: String,
|
||||
feed_id: Number,
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions(["toggleAudio", "newCurrentPlaylist", "skipToIndexByMusic"]),
|
||||
startPlay(music) {
|
||||
if (this.currentSong?.id === music.id) {
|
||||
this.toggleAudio();
|
||||
return;
|
||||
}
|
||||
// if (this.playlist_id === this.feed_id) {
|
||||
// this.skipToIndexByMusic(music);
|
||||
// return;
|
||||
// }
|
||||
this.newCurrentPlaylist([music, this.medias, this.feed_id]);
|
||||
},
|
||||
|
||||
likeAudio(media_id) {
|
||||
Inertia.post(
|
||||
route("feed.audio.like", media_id),
|
||||
{},
|
||||
{
|
||||
preserveScroll: true,
|
||||
preserveState: true,
|
||||
}
|
||||
);
|
||||
let media = find(this.medias, (item) => item.id === media_id);
|
||||
if(media){
|
||||
media.liked = !media.liked;
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(["playing"]),
|
||||
...mapState({
|
||||
playlist_id: (state) => state.player.playlist_id,
|
||||
seek: (state) => state.player.seek,
|
||||
currentSong: (state) => state.player.currentSong,
|
||||
}),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
32
resources/js/Shared/Overlay/ModalFeedMediaVideos.vue
Executable file
32
resources/js/Shared/Overlay/ModalFeedMediaVideos.vue
Executable file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<swiper class="h-full" :grabCursor="true" navigation :pagination="{ clickable: true }">
|
||||
<swiper-slide v-for="media in medias" class="aspect-w-16 aspect-h-12 md:aspect-h-9" :key="media.id">
|
||||
<video :src="media.url" controls></video>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import SwiperCore, { Navigation, Pagination } from 'swiper/core';
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
|
||||
import "swiper/swiper-bundle.css";
|
||||
// import "swiper/components/navigation/navigation.min.css";
|
||||
// import "swiper/components/pagination/pagination.min.css";
|
||||
|
||||
SwiperCore.use([Navigation, Pagination]);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Swiper,
|
||||
SwiperSlide,
|
||||
},
|
||||
props: {
|
||||
medias: Array,
|
||||
title: String,
|
||||
preview: String,
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
175
resources/js/Shared/Overlay/ModalShare.vue
Executable file
175
resources/js/Shared/Overlay/ModalShare.vue
Executable file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<TransitionRoot as="template" :show="open">
|
||||
<Dialog as="div" static
|
||||
class="fixed z-[1000] inset-0 overflow-y-auto" :open="open"
|
||||
@close="staticCloseModal"
|
||||
>
|
||||
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
enter-from="opacity-0" enter-to="opacity-100"
|
||||
leave="ease-in duration-200" leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<DialogOverlay class="fixed inset-0 bg-indigo-200 bg-opacity-75 transition-opacity" />
|
||||
</TransitionChild>
|
||||
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enter-to="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200" leave-from="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div class="inline-block align-bottom bg-indigo-300 rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
|
||||
<div class="">
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-light">
|
||||
Поделиться
|
||||
</DialogTitle>
|
||||
<div class="my-4">
|
||||
<input :value="route(`${entity.type}.show`, entity.slug)" class="js-input-text-share w-full focus:ring-4 focus:ring-offset-1 focus:ring-orange focus:ring-opacity-20 focus:ring-offset-orange focus:border-transparent text-gray border border-indigo-300 bg-indigo-200 rounded-md placeholder-gray-light"
|
||||
type="text" readonly
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-end -mx-1">
|
||||
<div class="transition-shadow shadow-none hover:shadow-classic2 p-2 bg-[#4C75A3] text-white mx-1 cursor-pointer" title="Скопировать текст"
|
||||
@click="copyText"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24"
|
||||
height="24" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" class="feather feather-copy"
|
||||
><rect x="9" y="9"
|
||||
width="13" height="13"
|
||||
rx="2" ry="2"
|
||||
></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
|
||||
</div>
|
||||
|
||||
<div class="transition-shadow shadow-none hover:shadow-classic2 p-2 bg-[#4C75A3] text-white mx-1 cursor-pointer" @click="shareVK">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24"
|
||||
height="24" viewBox="0 0 576 512"
|
||||
><path fill="currentColor" d="M545 117.7c3.7-12.5 0-21.7-17.8-21.7h-58.9c-15 0-21.9 7.9-25.6 16.7 0 0-30 73.1-72.4 120.5-13.7 13.7-20 18.1-27.5 18.1-3.7 0-9.4-4.4-9.4-16.9V117.7c0-15-4.2-21.7-16.6-21.7h-92.6c-9.4 0-15 7-15 13.5 0 14.2 21.2 17.5 23.4 57.5v86.8c0 19-3.4 22.5-10.9 22.5-20 0-68.6-73.4-97.4-157.4-5.8-16.3-11.5-22.9-26.6-22.9H38.8c-16.8 0-20.2 7.9-20.2 16.7 0 15.6 20 93.1 93.1 195.5C160.4 378.1 229 416 291.4 416c37.5 0 42.1-8.4 42.1-22.9 0-66.8-3.4-73.1 15.4-73.1 8.7 0 23.7 4.4 58.7 38.1 40 40 46.6 57.9 69 57.9h58.9c16.8 0 25.3-8.4 20.4-25-11.2-34.9-86.9-106.7-90.3-111.5-8.7-11.2-6.2-16.2 0-26.2 .1-.1 72-101.3 79.4-135.6z"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex">
|
||||
<button type="button"
|
||||
class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none"
|
||||
@click="staticCloseModal"
|
||||
>
|
||||
Отменить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</Dialog>
|
||||
</TransitionRoot>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
} from '@headlessui/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
},
|
||||
// emits: {
|
||||
// close: null,
|
||||
// },
|
||||
props: {
|
||||
entity: Object,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
open: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openModal() {
|
||||
this.open = true
|
||||
},
|
||||
staticCloseModal() {
|
||||
this.open = false
|
||||
},
|
||||
copyText(){
|
||||
const text = document.querySelector('.js-input-text-share')
|
||||
text.select()
|
||||
text.setSelectionRange(0, 99999)
|
||||
navigator.clipboard.writeText(text.value)
|
||||
},
|
||||
shareTwitter() {
|
||||
const urlInner = route(`${this.entity.type}.show`, this.entity.slug)
|
||||
const body = this.entity.body ?? ''
|
||||
const url = `https://twitter.com/intent/tweet?text=${body}&url=${urlInner}`
|
||||
this.openWindow(url)
|
||||
},
|
||||
|
||||
shareFB() {
|
||||
const urlInner = route(`${this.entity.type}.show`, this.entity.slug)
|
||||
const body = this.entity.body ?? ''
|
||||
const url = `https://www.facebook.com/sharer.php?u=${urlInner}&t=${body}`
|
||||
this.openWindow(url)
|
||||
},
|
||||
|
||||
shareVK() {
|
||||
const urlInner = route(`${this.entity.type}.show`, this.entity.slug)
|
||||
const title = this.entity.title ?? ''
|
||||
const url = `https://vk.com/share.php?url=${urlInner}&title=${title}`
|
||||
this.openWindow(url)
|
||||
},
|
||||
|
||||
openWindow(url) {
|
||||
this.popupCenter({url, title: 'share', w: 600, h: 400})
|
||||
},
|
||||
|
||||
popupCenter({ url, title, w, h }) {
|
||||
const dualScreenLeft =
|
||||
window.screenLeft !== undefined ? window.screenLeft : window.screenX
|
||||
const dualScreenTop =
|
||||
window.screenTop !== undefined ? window.screenTop : window.screenY
|
||||
|
||||
const width = window.innerWidth
|
||||
? window.innerWidth
|
||||
: document.documentElement.clientWidth
|
||||
? document.documentElement.clientWidth
|
||||
: screen.width
|
||||
const height = window.innerHeight
|
||||
? window.innerHeight
|
||||
: document.documentElement.clientHeight
|
||||
? document.documentElement.clientHeight
|
||||
: screen.height
|
||||
|
||||
const systemZoom = width / window.screen.availWidth
|
||||
const left = (width - w) / 2 / systemZoom + dualScreenLeft
|
||||
const top = (height - h) / 2 / systemZoom + dualScreenTop
|
||||
const newWindow = window.open(
|
||||
url,
|
||||
title,
|
||||
`
|
||||
scrollbars=yes,
|
||||
width=${w / systemZoom},
|
||||
height=${h / systemZoom},
|
||||
top=${top},
|
||||
left=${left}
|
||||
`
|
||||
)
|
||||
|
||||
if (window.focus) newWindow.focus()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
98
resources/js/Shared/Overlay/ModalWarning.vue
Executable file
98
resources/js/Shared/Overlay/ModalWarning.vue
Executable file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<TransitionRoot as="template" :show="open">
|
||||
<Dialog as="div" static
|
||||
class="fixed z-[1000] inset-0 overflow-y-auto" :open="open"
|
||||
@close="staticCloseModal"
|
||||
>
|
||||
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
enter-from="opacity-0" enter-to="opacity-100"
|
||||
leave="ease-in duration-200" leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<DialogOverlay class="fixed inset-0 bg-indigo-200 bg-opacity-75 transition-opacity" />
|
||||
</TransitionChild>
|
||||
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
||||
<TransitionChild as="template" enter="ease-out duration-300"
|
||||
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enter-to="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200" leave-from="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div class="inline-block align-bottom bg-indigo-300 rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-orange sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ExclamationIcon class="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-light">
|
||||
Удалить контент
|
||||
</DialogTitle>
|
||||
<div class="mt-2">
|
||||
<p class="text-sm text-gray-light">
|
||||
Вы уверены, что хотите удалить этот контент? Восстановить можно, но будет сложно!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button type="button"
|
||||
class="mx-3 my-1 transition shadow-none hover:shadow-pink inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-pink focus:outline-none"
|
||||
@click="remove"
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
class="mx-3 my-1 transition shadow-none hover:shadow-classic inline-flex items-center px-8 py-3 justify-center text-base rounded-md text-white bg-indigo-300 focus:outline-none"
|
||||
@click="staticCloseModal"
|
||||
>
|
||||
Отменить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</Dialog>
|
||||
</TransitionRoot>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
} from '@headlessui/vue'
|
||||
import { ExclamationIcon } from '@heroicons/vue/outline'
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dialog,
|
||||
DialogOverlay,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
ExclamationIcon,
|
||||
},
|
||||
props: {
|
||||
feed_id: Number,
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['action'],
|
||||
|
||||
methods: {
|
||||
staticCloseModal() {
|
||||
this.$emit('action', false)
|
||||
},
|
||||
remove() {
|
||||
this.$emit('action', true)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user