@ozdao/martyrs 0.2.495 → 0.2.496
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder.cjs +1 -1
- package/dist/builder.js +1 -1
- package/dist/globals.server.cjs +13 -2
- package/dist/globals.server.js +13 -2
- package/dist/martyrs/src/components/Block/Block.vue.cjs +1 -1
- package/dist/martyrs/src/components/Block/Block.vue.js +1 -1
- package/dist/martyrs/src/components/Chips/{Chips.vue.cjs → Chips.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Chips/Chips.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Chips/{Chips.vue.js → Chips.vue2.js} +2 -2
- package/dist/martyrs/src/components/Chips/Chips.vue2.js.map +1 -0
- package/dist/martyrs/src/components/Dropdown/{Dropdown.vue.cjs → Dropdown.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Dropdown/Dropdown.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Dropdown/{Dropdown.vue.js → Dropdown.vue2.js} +2 -2
- package/dist/martyrs/src/components/Dropdown/{Dropdown.vue.cjs.map → Dropdown.vue2.js.map} +1 -1
- package/dist/martyrs/src/components/Feed/Feed.vue.cjs +1 -1
- package/dist/martyrs/src/components/Feed/Feed.vue.js +1 -1
- package/dist/martyrs/src/components/Menu/{Menu.vue2.cjs → Menu.vue.cjs} +2 -2
- package/dist/martyrs/src/components/Menu/Menu.vue.cjs.map +1 -0
- package/dist/martyrs/src/components/Menu/{Menu.vue2.js → Menu.vue.js} +2 -2
- package/dist/martyrs/src/components/Menu/Menu.vue.js.map +1 -0
- package/dist/martyrs/src/components/Tooltip/{Tooltip.vue2.cjs → Tooltip.vue.cjs} +2 -2
- package/dist/martyrs/src/components/Tooltip/{Tooltip.vue2.js.map → Tooltip.vue.cjs.map} +1 -1
- package/dist/martyrs/src/components/Tooltip/{Tooltip.vue2.js → Tooltip.vue.js} +2 -2
- package/dist/martyrs/src/components/Tooltip/Tooltip.vue.js.map +1 -0
- package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.cjs +2 -2
- package/dist/martyrs/src/modules/auth/views/components/pages/Profile.vue.js +2 -2
- package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.cjs +1 -1
- package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.js +1 -1
- package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.cjs +1 -1
- package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/blocks/CardEvent.vue.cjs +1 -1
- package/dist/martyrs/src/modules/events/components/blocks/CardEvent.vue.js +1 -1
- package/dist/martyrs/src/modules/events/components/pages/Event.vue.cjs +2 -2
- package/dist/martyrs/src/modules/events/components/pages/Event.vue.js +2 -2
- package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.cjs +1 -1
- package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.cjs +2 -2
- package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.js +2 -2
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.js +1 -1
- package/dist/martyrs/src/modules/inventory/components/pages/Inventory.vue.cjs +2 -2
- package/dist/martyrs/src/modules/inventory/components/pages/Inventory.vue.js +1 -1
- package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/inventory/components/pages/InventoryEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.cjs +95 -0
- package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.cjs.map +1 -0
- package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js +95 -0
- package/dist/martyrs/src/modules/music/components/cards/ArtistCardSmall.vue.js.map +1 -0
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs +86 -139
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.js +96 -149
- package/dist/martyrs/src/modules/music/components/pages/Album.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Artist.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs +85 -135
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js +101 -151
- package/dist/martyrs/src/modules/music/components/pages/Playlist.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs +146 -158
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js +171 -183
- package/dist/martyrs/src/modules/music/components/pages/Track.vue.js.map +1 -1
- package/dist/martyrs/src/modules/music/router/music.cjs +2 -2
- package/dist/martyrs/src/modules/music/router/music.js +2 -2
- package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/sections/FormDelivery.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/blocks/CardOrganization.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +2 -2
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/DetailsTabSection.vue.cjs +2 -2
- package/dist/martyrs/src/modules/organizations/components/sections/DetailsTabSection.vue.js +2 -2
- package/dist/martyrs/src/modules/organizations/router/organizations.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/router/organizations.js +1 -1
- package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/blocks/ProductDiscounts.vue.cjs +2 -2
- package/dist/martyrs/src/modules/products/components/blocks/ProductDiscounts.vue.js +2 -2
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/blocks/CardSpot.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/blocks/CardSpot.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.js +1 -1
- package/dist/martyrs/src/modules/wallet/views/components/pages/Wallet.vue.cjs +3 -3
- package/dist/martyrs/src/modules/wallet/views/components/pages/Wallet.vue.js +3 -3
- package/dist/martyrs.css +1 -1
- package/dist/style.css +13 -0
- package/package.json +1 -1
- package/src/builder/rspack/rspack.config.ssr.client.js +1 -1
- package/src/modules/globals/controllers/classes/globals.websocket.js +11 -2
- package/src/modules/music/README.md +2 -0
- package/src/modules/music/components/cards/ArtistCardSmall.vue +92 -0
- package/src/modules/music/components/pages/Album.vue +20 -56
- package/src/modules/music/components/pages/Artist.vue +1 -1
- package/src/modules/music/components/pages/Playlist.vue +26 -44
- package/src/modules/music/components/pages/Track.vue +51 -74
- package/src/styles/base/all.scss +1 -0
- package/dist/martyrs/src/components/Chips/Chips.vue.cjs.map +0 -1
- package/dist/martyrs/src/components/Chips/Chips.vue.js.map +0 -1
- package/dist/martyrs/src/components/Dropdown/Dropdown.vue.js.map +0 -1
- package/dist/martyrs/src/components/Menu/Menu.vue2.cjs.map +0 -1
- package/dist/martyrs/src/components/Menu/Menu.vue2.js.map +0 -1
- package/dist/martyrs/src/components/Tooltip/Tooltip.vue2.cjs.map +0 -1
|
@@ -5,10 +5,11 @@ const vueRouter = require("vue-router");
|
|
|
5
5
|
const Button = require("../../../../components/Button/Button.vue.cjs");
|
|
6
6
|
const Loader = require("../../../../components/Loader/Loader.vue2.cjs");
|
|
7
7
|
const Media = require("../../../../components/Media/Media.vue.cjs");
|
|
8
|
-
const Dropdown = require("../../../../components/Dropdown/Dropdown.
|
|
8
|
+
const Dropdown = require("../../../../components/Dropdown/Dropdown.vue2.cjs");
|
|
9
9
|
const Popup = require("../../../../components/Popup/Popup.vue.cjs");
|
|
10
10
|
const Feed = require("../../../../components/Feed/Feed.vue.cjs");
|
|
11
11
|
const IconPlay = require("../../../icons/navigation/IconPlay.vue.cjs");
|
|
12
|
+
const IconPause = require("../../../icons/navigation/IconPause.vue.cjs");
|
|
12
13
|
const IconLike = require("../../../icons/navigation/IconLike.vue.cjs");
|
|
13
14
|
const IconEllipsis = require("../../../icons/navigation/IconEllipsis.vue.cjs");
|
|
14
15
|
const IconAdd = require("../../../icons/navigation/IconAdd.vue.cjs");
|
|
@@ -17,6 +18,7 @@ const IconCalendar = require("../../../icons/entities/IconCalendar.vue.cjs");
|
|
|
17
18
|
const IconCheckmark = require("../../../icons/navigation/IconCheckmark.vue.cjs");
|
|
18
19
|
const IconShow = require("../../../icons/actions/IconShow.vue.cjs");
|
|
19
20
|
const TrackListCard = require("../cards/TrackListCard.vue.cjs");
|
|
21
|
+
const ArtistCardSmall = require("../cards/ArtistCardSmall.vue.cjs");
|
|
20
22
|
const tracks = require("../../store/tracks.cjs");
|
|
21
23
|
const player = require("../../store/player.cjs");
|
|
22
24
|
const auth = require("../../../auth/views/store/auth.cjs");
|
|
@@ -34,71 +36,66 @@ const _hoisted_4 = {
|
|
|
34
36
|
class: "track-content cols-2-fit-content mobile:cols-1 gap-big"
|
|
35
37
|
};
|
|
36
38
|
const _hoisted_5 = { class: "pos-sticky pos-t-0 mobile:pos-relative track-cover-section" };
|
|
37
|
-
const _hoisted_6 = { class: "cover-container relative mn-b-medium radius-big
|
|
38
|
-
const _hoisted_7 = { class: "
|
|
39
|
-
const _hoisted_8 = { class: "
|
|
40
|
-
const _hoisted_9 = { class: "
|
|
41
|
-
const _hoisted_10 = { class: "
|
|
42
|
-
const _hoisted_11 = { class: "
|
|
43
|
-
const _hoisted_12 = { class: "
|
|
44
|
-
const _hoisted_13 = { class: "
|
|
45
|
-
const _hoisted_14 = {
|
|
39
|
+
const _hoisted_6 = { class: "cover-container pos-relative mn-b-medium radius-big o-hidden" };
|
|
40
|
+
const _hoisted_7 = { class: "cover-overlay w-100 h-100 pos-absolute pos-t-0 pos-r-0 bg-black-transp-40 flex flex-center" };
|
|
41
|
+
const _hoisted_8 = { class: "stats-grid grid cols-2 gap-small" };
|
|
42
|
+
const _hoisted_9 = { class: "stat-card bg-light pd-medium radius-medium t-center" };
|
|
43
|
+
const _hoisted_10 = { class: "mn-b-thin" };
|
|
44
|
+
const _hoisted_11 = { class: "stat-card bg-light pd-medium radius-medium t-center" };
|
|
45
|
+
const _hoisted_12 = { class: "mn-b-thin" };
|
|
46
|
+
const _hoisted_13 = { class: "track-details-section" };
|
|
47
|
+
const _hoisted_14 = { class: "h1 mn-b-medium" };
|
|
48
|
+
const _hoisted_15 = { class: "flex gap-small mn-b-medium" };
|
|
49
|
+
const _hoisted_16 = { class: "dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin" };
|
|
50
|
+
const _hoisted_17 = { class: "artists-section mn-b-medium" };
|
|
51
|
+
const _hoisted_18 = {
|
|
46
52
|
key: 0,
|
|
47
|
-
class: "
|
|
53
|
+
class: "t-medium mn-b-small"
|
|
48
54
|
};
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
class: "w-4r h-4r radius-full bg-primary flex-center"
|
|
57
|
-
};
|
|
58
|
-
const _hoisted_21 = { class: "flex items-center gap-thin" };
|
|
59
|
-
const _hoisted_22 = { class: "t-large" };
|
|
60
|
-
const _hoisted_23 = { class: "metadata-grid grid cols-2 gap-small mn-b-big" };
|
|
61
|
-
const _hoisted_24 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
62
|
-
const _hoisted_25 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
|
|
55
|
+
const _hoisted_19 = { class: "flex flex-col gap-small" };
|
|
56
|
+
const _hoisted_20 = { class: "metadata-grid grid cols-2 gap-small mn-b-medium" };
|
|
57
|
+
const _hoisted_21 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
58
|
+
const _hoisted_22 = { class: "t-medium" };
|
|
59
|
+
const _hoisted_23 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
60
|
+
const _hoisted_24 = { class: "t-medium" };
|
|
61
|
+
const _hoisted_25 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
63
62
|
const _hoisted_26 = { class: "t-medium" };
|
|
64
63
|
const _hoisted_27 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
65
|
-
const _hoisted_28 = { class: "
|
|
66
|
-
const _hoisted_29 = {
|
|
67
|
-
const _hoisted_30 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
68
|
-
const _hoisted_31 = { class: "icon-wrapper bg-success-transp-20 w-3r h-3r radius-small flex-center" };
|
|
69
|
-
const _hoisted_32 = { class: "t-medium t-success" };
|
|
70
|
-
const _hoisted_33 = { class: "metadata-card bg-light pd-medium radius-medium flex items-center gap-medium" };
|
|
71
|
-
const _hoisted_34 = { class: "icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center" };
|
|
72
|
-
const _hoisted_35 = { class: "t-medium" };
|
|
73
|
-
const _hoisted_36 = {
|
|
64
|
+
const _hoisted_28 = { class: "t-medium" };
|
|
65
|
+
const _hoisted_29 = {
|
|
74
66
|
key: 0,
|
|
75
|
-
class: "
|
|
67
|
+
class: "t-medium mn-b-small"
|
|
76
68
|
};
|
|
77
|
-
const
|
|
78
|
-
const _hoisted_38 = {
|
|
69
|
+
const _hoisted_30 = {
|
|
79
70
|
key: 1,
|
|
80
|
-
class: "
|
|
71
|
+
class: "album-card bg-light pd-medium radius-medium mn-b-medium"
|
|
81
72
|
};
|
|
82
|
-
const
|
|
83
|
-
const
|
|
73
|
+
const _hoisted_31 = { class: "t-medium" };
|
|
74
|
+
const _hoisted_32 = {
|
|
84
75
|
key: 2,
|
|
85
|
-
class: "
|
|
76
|
+
class: "tags-section mn-b-medium"
|
|
86
77
|
};
|
|
87
|
-
const
|
|
88
|
-
const
|
|
78
|
+
const _hoisted_33 = { class: "flex gap-thin flex-wrap" };
|
|
79
|
+
const _hoisted_34 = {
|
|
89
80
|
key: 3,
|
|
81
|
+
class: "description-section bg-light pd-medium radius-medium mn-b-medium"
|
|
82
|
+
};
|
|
83
|
+
const _hoisted_35 = { class: "t-transp" };
|
|
84
|
+
const _hoisted_36 = {
|
|
85
|
+
key: 4,
|
|
90
86
|
class: "lyrics-section bg-light pd-medium radius-medium"
|
|
91
87
|
};
|
|
92
|
-
const
|
|
93
|
-
const
|
|
88
|
+
const _hoisted_37 = { class: "t-transp t-small" };
|
|
89
|
+
const _hoisted_38 = {
|
|
94
90
|
key: 3,
|
|
95
91
|
class: "related-section mn-t-big"
|
|
96
92
|
};
|
|
97
|
-
const
|
|
93
|
+
const _hoisted_39 = { class: "bg-light radius-medium o-hidden" };
|
|
98
94
|
const _sfc_main = {
|
|
99
95
|
__name: "Track",
|
|
100
96
|
emits: ["page-loading", "page-loaded"],
|
|
101
97
|
setup(__props, { emit: __emit }) {
|
|
98
|
+
const isPlaying = vue.computed(() => player.state.isPlaying);
|
|
102
99
|
const route = vueRouter.useRoute();
|
|
103
100
|
const router = vueRouter.useRouter();
|
|
104
101
|
const emits = __emit;
|
|
@@ -205,26 +202,47 @@ const _sfc_main = {
|
|
|
205
202
|
url: track.value.coverUrl || track.value.album && track.value.album.coverUrl || "/logo/logo-placeholder.jpg",
|
|
206
203
|
alt: track.value.title,
|
|
207
204
|
class: "aspect-1x1 w-100 radius-medium o-hidden"
|
|
208
|
-
}, null, 8, ["url", "alt"])
|
|
205
|
+
}, null, 8, ["url", "alt"]),
|
|
206
|
+
vue.createElementVNode("div", _hoisted_7, [
|
|
207
|
+
vue.createVNode(Button.default, {
|
|
208
|
+
onClick: playTrack,
|
|
209
|
+
color: "white",
|
|
210
|
+
size: "big",
|
|
211
|
+
class: "w-5r h-5r radius-big bg-main shadow-big hover-scale-110"
|
|
212
|
+
}, {
|
|
213
|
+
default: vue.withCtx(() => [
|
|
214
|
+
!isPlaying.value ? (vue.openBlock(), vue.createBlock(IconPlay.default, {
|
|
215
|
+
key: 0,
|
|
216
|
+
fill: "rgb(var(--white))",
|
|
217
|
+
class: "i-medium"
|
|
218
|
+
})) : (vue.openBlock(), vue.createBlock(IconPause.default, {
|
|
219
|
+
key: 1,
|
|
220
|
+
fill: "rgb(var(--white))",
|
|
221
|
+
class: "i-medium"
|
|
222
|
+
}))
|
|
223
|
+
]),
|
|
224
|
+
_: 1
|
|
225
|
+
})
|
|
226
|
+
])
|
|
209
227
|
]),
|
|
210
|
-
vue.createElementVNode("div",
|
|
211
|
-
vue.createElementVNode("div",
|
|
212
|
-
vue.createElementVNode("div",
|
|
228
|
+
vue.createElementVNode("div", _hoisted_8, [
|
|
229
|
+
vue.createElementVNode("div", _hoisted_9, [
|
|
230
|
+
vue.createElementVNode("div", _hoisted_10, vue.toDisplayString(formatNumber(track.value.playCount)), 1),
|
|
213
231
|
_cache[4] || (_cache[4] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Plays", -1))
|
|
214
232
|
]),
|
|
215
|
-
vue.createElementVNode("div",
|
|
216
|
-
vue.createElementVNode("div",
|
|
233
|
+
vue.createElementVNode("div", _hoisted_11, [
|
|
234
|
+
vue.createElementVNode("div", _hoisted_12, vue.toDisplayString(formatNumber(track.value.views)), 1),
|
|
217
235
|
_cache[5] || (_cache[5] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Views", -1))
|
|
218
236
|
])
|
|
219
237
|
])
|
|
220
238
|
]),
|
|
221
|
-
vue.createElementVNode("div",
|
|
222
|
-
vue.createElementVNode("div",
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
]),
|
|
226
|
-
vue.createElementVNode("h1",
|
|
227
|
-
vue.createElementVNode("div",
|
|
239
|
+
vue.createElementVNode("div", _hoisted_13, [
|
|
240
|
+
_cache[19] || (_cache[19] = vue.createElementVNode("div", { class: "flex items-center gap-small mn-b-small" }, [
|
|
241
|
+
vue.createElementVNode("span", { class: "bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase" }, "Single"),
|
|
242
|
+
vue.createElementVNode("span", { class: "bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase" }, "Explicit")
|
|
243
|
+
], -1)),
|
|
244
|
+
vue.createElementVNode("h1", _hoisted_14, vue.toDisplayString(track.value.title), 1),
|
|
245
|
+
vue.createElementVNode("div", _hoisted_15, [
|
|
228
246
|
vue.createVNode(Button.default, {
|
|
229
247
|
onClick: playTrack,
|
|
230
248
|
color: "primary",
|
|
@@ -232,11 +250,16 @@ const _sfc_main = {
|
|
|
232
250
|
class: "flex-1 t-white bg-black radius-thin flex-center gap-thin"
|
|
233
251
|
}, {
|
|
234
252
|
default: vue.withCtx(() => [
|
|
235
|
-
vue.
|
|
253
|
+
!isPlaying.value ? (vue.openBlock(), vue.createBlock(IconPlay.default, {
|
|
254
|
+
key: 0,
|
|
236
255
|
fill: "rgb(var(--white))",
|
|
237
256
|
class: "i-medium"
|
|
238
|
-
}),
|
|
239
|
-
|
|
257
|
+
})) : (vue.openBlock(), vue.createBlock(IconPause.default, {
|
|
258
|
+
key: 1,
|
|
259
|
+
fill: "rgb(var(--white))",
|
|
260
|
+
class: "i-medium"
|
|
261
|
+
})),
|
|
262
|
+
vue.createTextVNode(" " + vue.toDisplayString(!isPlaying.value ? "Play" : "Pause"), 1)
|
|
240
263
|
]),
|
|
241
264
|
_: 1
|
|
242
265
|
}),
|
|
@@ -248,7 +271,7 @@ const _sfc_main = {
|
|
|
248
271
|
}, {
|
|
249
272
|
default: vue.withCtx(() => [
|
|
250
273
|
vue.createVNode(IconAdd.default, { class: "i-medium" }),
|
|
251
|
-
_cache[
|
|
274
|
+
_cache[6] || (_cache[6] = vue.createTextVNode(" Add to Queue "))
|
|
252
275
|
]),
|
|
253
276
|
_: 1
|
|
254
277
|
}),
|
|
@@ -261,7 +284,7 @@ const _sfc_main = {
|
|
|
261
284
|
default: vue.withCtx(() => [
|
|
262
285
|
vue.createVNode(IconLike.default, {
|
|
263
286
|
class: "i-medium",
|
|
264
|
-
fill: isFavorite.value
|
|
287
|
+
fill: isFavorite.value ? "rgb(var(--main)" : "rgb(var(--black)"
|
|
265
288
|
}, null, 8, ["fill"]),
|
|
266
289
|
vue.createTextVNode(" " + vue.toDisplayString(isFavorite.value ? "Liked" : "Like"), 1)
|
|
267
290
|
]),
|
|
@@ -286,14 +309,14 @@ const _sfc_main = {
|
|
|
286
309
|
})
|
|
287
310
|
]),
|
|
288
311
|
default: vue.withCtx(() => [
|
|
289
|
-
vue.createElementVNode("div",
|
|
312
|
+
vue.createElementVNode("div", _hoisted_16, [
|
|
290
313
|
vue.createVNode(Button.default, {
|
|
291
314
|
onClick: _cache[0] || (_cache[0] = ($event) => showAddToPlaylistModal.value = true),
|
|
292
315
|
color: "transp",
|
|
293
316
|
size: "small",
|
|
294
317
|
class: "w-100 justify-start"
|
|
295
318
|
}, {
|
|
296
|
-
default: vue.withCtx(() => _cache[
|
|
319
|
+
default: vue.withCtx(() => _cache[7] || (_cache[7] = [
|
|
297
320
|
vue.createTextVNode(" Add to Playlist ")
|
|
298
321
|
])),
|
|
299
322
|
_: 1
|
|
@@ -304,20 +327,20 @@ const _sfc_main = {
|
|
|
304
327
|
size: "small",
|
|
305
328
|
class: "w-100 justify-start"
|
|
306
329
|
}, {
|
|
307
|
-
default: vue.withCtx(() => _cache[
|
|
330
|
+
default: vue.withCtx(() => _cache[8] || (_cache[8] = [
|
|
308
331
|
vue.createTextVNode(" Copy Link ")
|
|
309
332
|
])),
|
|
310
333
|
_: 1
|
|
311
334
|
}),
|
|
312
335
|
isOwner.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
|
|
313
|
-
_cache[
|
|
336
|
+
_cache[11] || (_cache[11] = vue.createElementVNode("hr", { class: "mn-v-thin border-dark-transp-10" }, null, -1)),
|
|
314
337
|
vue.createVNode(Button.default, {
|
|
315
338
|
onClick: editTrack,
|
|
316
339
|
color: "transp",
|
|
317
340
|
size: "small",
|
|
318
341
|
class: "w-100 justify-start"
|
|
319
342
|
}, {
|
|
320
|
-
default: vue.withCtx(() => _cache[
|
|
343
|
+
default: vue.withCtx(() => _cache[9] || (_cache[9] = [
|
|
321
344
|
vue.createTextVNode(" Edit Track ")
|
|
322
345
|
])),
|
|
323
346
|
_: 1
|
|
@@ -328,7 +351,7 @@ const _sfc_main = {
|
|
|
328
351
|
size: "small",
|
|
329
352
|
class: "w-100 justify-start"
|
|
330
353
|
}, {
|
|
331
|
-
default: vue.withCtx(() => _cache[
|
|
354
|
+
default: vue.withCtx(() => _cache[10] || (_cache[10] = [
|
|
332
355
|
vue.createTextVNode(" Delete Track ")
|
|
333
356
|
])),
|
|
334
357
|
_: 1
|
|
@@ -339,130 +362,95 @@ const _sfc_main = {
|
|
|
339
362
|
_: 1
|
|
340
363
|
}, 8, ["label", "modelValue"])
|
|
341
364
|
]),
|
|
342
|
-
vue.createElementVNode("div",
|
|
343
|
-
vue.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
class: "w-4r h-4r radius-full object-cover"
|
|
354
|
-
}, null, 8, ["src", "alt"])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_20, vue.toDisplayString(track.value.artist.name.charAt(0)), 1))
|
|
355
|
-
]),
|
|
356
|
-
vue.createElementVNode("div", null, [
|
|
357
|
-
vue.createElementVNode("div", _hoisted_21, [
|
|
358
|
-
vue.createElementVNode("span", _hoisted_22, vue.toDisplayString(track.value.artist.name), 1),
|
|
359
|
-
track.value.artist.isVerified ? (vue.openBlock(), vue.createBlock(IconCheckmark.default, {
|
|
360
|
-
key: 0,
|
|
361
|
-
class: "w-1r h-1r t-primary"
|
|
362
|
-
})) : vue.createCommentVNode("", true)
|
|
363
|
-
]),
|
|
364
|
-
_cache[14] || (_cache[14] = vue.createElementVNode("span", { class: "t-small t-transp" }, "Artist", -1))
|
|
365
|
-
])
|
|
366
|
-
]),
|
|
367
|
-
_: 1
|
|
368
|
-
}, 8, ["to"]),
|
|
369
|
-
!isOwner.value ? (vue.openBlock(), vue.createBlock(Button.default, {
|
|
370
|
-
key: 0,
|
|
371
|
-
onClick: toggleFollowArtist,
|
|
372
|
-
color: isFollowingArtist.value ? "primary" : "transp",
|
|
373
|
-
size: "small"
|
|
374
|
-
}, {
|
|
375
|
-
default: vue.withCtx(() => [
|
|
376
|
-
vue.createTextVNode(vue.toDisplayString(isFollowingArtist.value ? "Following" : "Follow"), 1)
|
|
377
|
-
]),
|
|
378
|
-
_: 1
|
|
379
|
-
}, 8, ["color"])) : vue.createCommentVNode("", true)
|
|
365
|
+
vue.createElementVNode("div", _hoisted_17, [
|
|
366
|
+
track.value.artist ? (vue.openBlock(), vue.createElementBlock("h3", _hoisted_18, "Artist")) : vue.createCommentVNode("", true),
|
|
367
|
+
vue.createElementVNode("div", _hoisted_19, [
|
|
368
|
+
(vue.openBlock(), vue.createBlock(ArtistCardSmall.default, {
|
|
369
|
+
key: track.value.artist._id,
|
|
370
|
+
artist: track.value.artist,
|
|
371
|
+
"is-following": isFollowingArtist.value,
|
|
372
|
+
"show-follow-button": !isOwner.value,
|
|
373
|
+
onToggleFollow: toggleFollowArtist
|
|
374
|
+
}, null, 8, ["artist", "is-following", "show-follow-button"]))
|
|
375
|
+
])
|
|
380
376
|
]),
|
|
381
|
-
vue.createElementVNode("
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
]),
|
|
377
|
+
_cache[20] || (_cache[20] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Metadata", -1)),
|
|
378
|
+
vue.createElementVNode("div", _hoisted_20, [
|
|
379
|
+
vue.createElementVNode("div", _hoisted_21, [
|
|
380
|
+
vue.createVNode(IconCalendar.default, { class: "i-regular t-primary" }),
|
|
386
381
|
vue.createElementVNode("div", null, [
|
|
387
|
-
_cache[
|
|
388
|
-
vue.createElementVNode("div",
|
|
382
|
+
_cache[12] || (_cache[12] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Released", -1)),
|
|
383
|
+
vue.createElementVNode("div", _hoisted_22, vue.toDisplayString(formatDate(track.value.releaseDate)), 1)
|
|
389
384
|
])
|
|
390
385
|
]),
|
|
391
|
-
vue.createElementVNode("div",
|
|
392
|
-
vue.
|
|
393
|
-
vue.createVNode(IconCalendar.default, { class: "i-regular t-primary" })
|
|
394
|
-
]),
|
|
386
|
+
vue.createElementVNode("div", _hoisted_23, [
|
|
387
|
+
vue.createVNode(IconTime.default, { class: "i-regular t-primary" }),
|
|
395
388
|
vue.createElementVNode("div", null, [
|
|
396
|
-
_cache[
|
|
397
|
-
vue.createElementVNode("div",
|
|
389
|
+
_cache[13] || (_cache[13] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Duration", -1)),
|
|
390
|
+
vue.createElementVNode("div", _hoisted_24, vue.toDisplayString(formatDuration(track.value.duration)), 1)
|
|
398
391
|
])
|
|
399
392
|
]),
|
|
400
|
-
vue.createElementVNode("div",
|
|
401
|
-
vue.
|
|
402
|
-
vue.createVNode(IconCheckmark.default, { class: "i-regular t-success" })
|
|
403
|
-
]),
|
|
393
|
+
vue.createElementVNode("div", _hoisted_25, [
|
|
394
|
+
vue.createVNode(IconCheckmark.default, { class: "i-regular t-primary" }),
|
|
404
395
|
vue.createElementVNode("div", null, [
|
|
405
|
-
_cache[
|
|
406
|
-
vue.createElementVNode("div",
|
|
396
|
+
_cache[14] || (_cache[14] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Status", -1)),
|
|
397
|
+
vue.createElementVNode("div", _hoisted_26, vue.toDisplayString(track.value.status), 1)
|
|
407
398
|
])
|
|
408
399
|
]),
|
|
409
|
-
vue.createElementVNode("div",
|
|
410
|
-
vue.
|
|
411
|
-
vue.createVNode(IconShow.default, { class: "i-regular t-primary" })
|
|
412
|
-
]),
|
|
400
|
+
vue.createElementVNode("div", _hoisted_27, [
|
|
401
|
+
vue.createVNode(IconShow.default, { class: "i-regular t-primary" }),
|
|
413
402
|
vue.createElementVNode("div", null, [
|
|
414
|
-
_cache[
|
|
415
|
-
vue.createElementVNode("div",
|
|
403
|
+
_cache[15] || (_cache[15] = vue.createElementVNode("div", { class: "t-small t-transp t-uppercase" }, "Visibility", -1)),
|
|
404
|
+
vue.createElementVNode("div", _hoisted_28, vue.toDisplayString(track.value.isPublic ? "Public" : "Private"), 1)
|
|
416
405
|
])
|
|
417
406
|
])
|
|
418
407
|
]),
|
|
419
|
-
track.value.album ? (vue.openBlock(), vue.createElementBlock("
|
|
420
|
-
|
|
408
|
+
track.value.album ? (vue.openBlock(), vue.createElementBlock("h3", _hoisted_29, "From Album")) : vue.createCommentVNode("", true),
|
|
409
|
+
track.value.album ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_30, [
|
|
421
410
|
vue.createVNode(_component_router_link, {
|
|
422
411
|
to: `/album/${track.value.album.url}`,
|
|
423
|
-
class: "flex
|
|
412
|
+
class: "flex flex-v-center gap-thin hover-opacity"
|
|
424
413
|
}, {
|
|
425
414
|
default: vue.withCtx(() => [
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
src: track.value.album.coverUrl,
|
|
415
|
+
vue.createVNode(Media.default, {
|
|
416
|
+
url: track.value.album.coverArt || "/logo/logo-placeholder.jpg",
|
|
429
417
|
alt: track.value.album.title,
|
|
430
|
-
class: "w-3r h-3r radius-
|
|
431
|
-
}, null, 8, ["
|
|
432
|
-
vue.createElementVNode("span",
|
|
418
|
+
class: "w-3r h-3r radius-thin o-hidden object-cover"
|
|
419
|
+
}, null, 8, ["url", "alt"]),
|
|
420
|
+
vue.createElementVNode("span", _hoisted_31, vue.toDisplayString(track.value.album.title), 1)
|
|
433
421
|
]),
|
|
434
422
|
_: 1
|
|
435
423
|
}, 8, ["to"])
|
|
436
424
|
])) : vue.createCommentVNode("", true),
|
|
437
|
-
track.value.genre && track.value.genre.length || track.value.tags && track.value.tags.length ? (vue.openBlock(), vue.createElementBlock("div",
|
|
438
|
-
_cache[
|
|
439
|
-
vue.createElementVNode("div",
|
|
425
|
+
track.value.genre && track.value.genre.length || track.value.tags && track.value.tags.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_32, [
|
|
426
|
+
_cache[16] || (_cache[16] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Genres & Tags", -1)),
|
|
427
|
+
vue.createElementVNode("div", _hoisted_33, [
|
|
440
428
|
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(track.value.genre, (genre) => {
|
|
441
429
|
return vue.openBlock(), vue.createElementBlock("span", {
|
|
442
430
|
key: genre,
|
|
443
|
-
class: "tag bg-
|
|
444
|
-
}, vue.toDisplayString(genre), 1);
|
|
431
|
+
class: "tag bg-main t-medium pd-thin radius-thin t-small cursor-pointer"
|
|
432
|
+
}, vue.toDisplayString(genre.name || genre), 1);
|
|
445
433
|
}), 128)),
|
|
446
434
|
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(track.value.tags, (tag) => {
|
|
447
435
|
return vue.openBlock(), vue.createElementBlock("span", {
|
|
448
436
|
key: tag,
|
|
449
437
|
class: "tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer"
|
|
450
|
-
}, " #" + vue.toDisplayString(tag), 1);
|
|
438
|
+
}, " #" + vue.toDisplayString(tag.name || tag), 1);
|
|
451
439
|
}), 128))
|
|
452
440
|
])
|
|
453
441
|
])) : vue.createCommentVNode("", true),
|
|
454
|
-
track.value.description ? (vue.openBlock(), vue.createElementBlock("div",
|
|
455
|
-
_cache[
|
|
456
|
-
vue.createElementVNode("p",
|
|
442
|
+
track.value.description ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_34, [
|
|
443
|
+
_cache[17] || (_cache[17] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "About", -1)),
|
|
444
|
+
vue.createElementVNode("p", _hoisted_35, vue.toDisplayString(track.value.description), 1)
|
|
457
445
|
])) : vue.createCommentVNode("", true),
|
|
458
|
-
track.value.lyrics ? (vue.openBlock(), vue.createElementBlock("div",
|
|
459
|
-
_cache[
|
|
460
|
-
vue.createElementVNode("pre",
|
|
446
|
+
track.value.lyrics ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_36, [
|
|
447
|
+
_cache[18] || (_cache[18] = vue.createElementVNode("h3", { class: "t-medium mn-b-small" }, "Lyrics", -1)),
|
|
448
|
+
vue.createElementVNode("pre", _hoisted_37, vue.toDisplayString(track.value.lyrics), 1)
|
|
461
449
|
])) : vue.createCommentVNode("", true)
|
|
462
450
|
])
|
|
463
451
|
])) : vue.createCommentVNode("", true),
|
|
464
|
-
track.value && relatedTracks.value && relatedTracks.value.length ? (vue.openBlock(), vue.createElementBlock("section",
|
|
465
|
-
_cache[
|
|
452
|
+
track.value && relatedTracks.value && relatedTracks.value.length ? (vue.openBlock(), vue.createElementBlock("section", _hoisted_38, [
|
|
453
|
+
_cache[21] || (_cache[21] = vue.createElementVNode("h2", { class: "h2 mn-b-medium" }, "Related Tracks", -1)),
|
|
466
454
|
vue.createVNode(Feed.default, {
|
|
467
455
|
store: {
|
|
468
456
|
read: () => new _ctx.Promise((resolve) => resolve(relatedTracks.value || [])),
|
|
@@ -480,7 +468,7 @@ const _sfc_main = {
|
|
|
480
468
|
class: "grid cols-2 cols-m-3 cols-l-4 gap-medium"
|
|
481
469
|
}, {
|
|
482
470
|
default: vue.withCtx(({ items }) => [
|
|
483
|
-
vue.createElementVNode("div",
|
|
471
|
+
vue.createElementVNode("div", _hoisted_39, [
|
|
484
472
|
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(items, (relatedTrack, index) => {
|
|
485
473
|
return vue.openBlock(), vue.createBlock(TrackListCard.default, {
|
|
486
474
|
key: relatedTrack._id,
|
|
@@ -500,7 +488,7 @@ const _sfc_main = {
|
|
|
500
488
|
onClosePopup: _cache[2] || (_cache[2] = ($event) => showAddToPlaylistModal.value = false),
|
|
501
489
|
class: "bg-white pd-medium w-m-25r radius-medium"
|
|
502
490
|
}, {
|
|
503
|
-
default: vue.withCtx(() => _cache[
|
|
491
|
+
default: vue.withCtx(() => _cache[22] || (_cache[22] = [
|
|
504
492
|
vue.createElementVNode("h3", { class: "h3 mn-b-medium" }, "Add to Playlist", -1),
|
|
505
493
|
vue.createElementVNode("p", { class: "t-transp" }, "Playlist selector coming soon...", -1)
|
|
506
494
|
])),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Track.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/Track.vue"],"sourcesContent":["<!-- components/pages/Track.vue -->\n<template>\n <div class=\"track-page pd-small\">\n <!-- Loading -->\n <div v-if=\"isLoading\" class=\"w-100 h-25r flex-center flex\">\n <Loader />\n </div>\n\n <!-- Not Found -->\n <div v-if=\"hasLoaded && !track\" class=\"t-center pd-big\">\n <h2 class=\"\">Track not found</h2>\n <p class=\"t-transp t-medium\">The track you're looking for doesn't exist or has been removed.</p>\n </div>\n\n <!-- Track Content -->\n <div v-if=\"track\" class=\"track-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative track-cover-section\">\n <!-- Cover with Play Overlay -->\n <div class=\"cover-container relative mn-b-medium radius-big overflow-hidden shadow-big\">\n <Media \n :url=\"track.coverUrl || (track.album && track.album.coverUrl) || '/logo/logo-placeholder.jpg'\" \n :alt=\"track.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n <!-- <div class=\"cover-overlay absolute inset-0 bg-black-transp-40 flex-center opacity-0 hover-opacity-100 transition\">\n <Button\n @click=\"playTrack\"\n color=\"white\"\n size=\"big\"\n class=\"w-5r h-5r radius-full shadow-big hover-scale-110\"\n >\n <IconPlay class=\"w-2r h-2r\" />\n </Button>\n </div> -->\n </div>\n\n \n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.playCount) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Plays</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.views) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Views</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Track Details -->\n <div class=\"track-details-section\">\n <!-- Track Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"badge bg-primary-transp-20 t-primary pd-thin-big radius-small t-small t-uppercase\">Single</span>\n <span v-if=\"track.isExplicit\" class=\"badge bg-danger-transp-20 t-danger pd-thin-big radius-small t-small\">Explicit</span>\n </div>\n\n <!-- Track Title -->\n <h1 class=\"h1 mn-b-medium\">{{ track.title }}</h1>\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playTrack\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 t-white bg-black radius-thin flex-center gap-thin\"\n >\n <IconPlay fill=\"rgb(var(--white))\" class=\"i-medium\" />\n Play\n </Button>\n\n <Button\n @click=\"addToQueue\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconAdd class=\"i-medium\" />\n Add to Queue\n </Button>\n\n <Button\n @click=\"toggleFavorite\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconLike class=\"i-medium\" :fill=\"isFavorite\" />\n {{isFavorite ? 'Liked' : 'Like'}}\n </Button>\n\n <Dropdown :label=\"{component: IconEllipsis, class: 'bg-light radius-thin pd-thin i-big' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"showAddToPlaylistModal = true\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Playlist\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editTrack\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Track\n </Button>\n <Button @click=\"deleteTrack\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Track\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Artist Card -->\n <div class=\"artist-card bg-light pd-medium radius-medium flex items-center gap-medium mn-b-big\">\n <router-link \n :to=\"{ name: 'artist', params: { url: track.artist.url } }\"\n class=\"flex items-center gap-medium flex-1 hover-opacity\"\n >\n <div class=\"artist-avatar\">\n <Media \n v-if=\"track.artist.photoUrl\"\n :src=\"track.artist.photoUrl\"\n :alt=\"track.artist.name\"\n class=\"w-4r h-4r radius-full object-cover\"\n />\n <div v-else class=\"w-4r h-4r radius-full bg-primary flex-center \">\n {{ track.artist.name.charAt(0) }}\n </div>\n </div>\n <div>\n <div class=\"flex items-center gap-thin\">\n <span class=\"t-large \">{{ track.artist.name }}</span>\n <IconVerified v-if=\"track.artist.isVerified\" class=\"w-1r h-1r t-primary\" />\n </div>\n <span class=\"t-small t-transp\">Artist</span>\n </div>\n </router-link>\n <Button \n v-if=\"!isOwner\"\n @click=\"toggleFollowArtist\"\n :color=\"isFollowingArtist ? 'primary' : 'transp'\"\n size=\"small\"\n >\n {{ isFollowingArtist ? 'Following' : 'Follow' }}\n </Button>\n </div>\n\n\n\n <!-- Metadata Cards -->\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-big\">\n <!-- Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconClock class=\"i-regular t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ formatDuration(track.duration) }}</div>\n </div>\n </div>\n\n <!-- Release Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconCalendar class=\"i-regular t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Released</div>\n <div class=\"t-medium \">{{ formatDate(track.releaseDate) }}</div>\n </div>\n </div>\n\n <!-- Status -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-success-transp-20 w-3r h-3r radius-small flex-center\">\n <IconCheck class=\"i-regular t-success\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Status</div>\n <div class=\"t-medium t-success\">{{ track.status }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <div class=\"icon-wrapper bg-primary-transp-20 w-3r h-3r radius-small flex-center\">\n <IconEye class=\"i-regular t-primary\" />\n </div>\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ track.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n <!-- Album Info -->\n <div v-if=\"track.album\" class=\"album-card bg-light pd-medium radius-medium mn-b-medium\">\n <div class=\"t-small t-transp t-uppercase mn-b-thin\">From Album</div>\n <router-link \n :to=\"`/album/${track.album.url}`\"\n class=\"flex items-center gap-medium hover-opacity\"\n >\n <Media \n v-if=\"track.album.coverUrl\"\n :src=\"track.album.coverUrl\"\n :alt=\"track.album.title\"\n class=\"w-3r h-3r radius-small object-cover\"\n />\n <span class=\"t-medium \">{{ track.album.title }}</span>\n </router-link>\n </div>\n\n <!-- Genres & Tags -->\n <div v-if=\"(track.genre && track.genre.length) || (track.tags && track.tags.length)\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Genres & Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"genre in track.genre\" \n :key=\"genre\"\n class=\"tag bg-primary-transp-20 t-primary pd-thin-big radius-small t-small hover-bg-primary-transp-30 cursor-pointer\"\n >\n {{ genre }}\n </span>\n <span \n v-for=\"tag in track.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"track.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ track.description }}</p>\n </div>\n\n <!-- Lyrics -->\n <div v-if=\"track.lyrics\" class=\"lyrics-section bg-light pd-medium radius-medium\">\n <h3 class=\"t-medium mn-b-small\">Lyrics</h3>\n <pre class=\"t-transp t-small\">{{ track.lyrics }}</pre>\n </div>\n </div>\n </div>\n\n <!-- Related Tracks -->\n <section v-if=\"track && relatedTracks && relatedTracks.length\" class=\"related-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Related Tracks</h2>\n <Feed\n :store=\"{\n read: () => new Promise(resolve => resolve(relatedTracks || [])),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"relatedTracks\"\n :states=\"{\n empty: {\n title: 'No related tracks',\n description: 'Check back later for recommendations',\n class: 'pd-medium bg-light radius-medium'\n }\n }\"\n class=\"grid cols-2 cols-m-3 cols-l-4 gap-medium\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(relatedTrack, index) in items\"\n :key=\"relatedTrack._id\"\n :track=\"relatedTrack\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- Add to Playlist Modal -->\n <Popup \n v-if=\"showAddToPlaylistModal\" \n @close-popup=\"showAddToPlaylistModal = false\" \n class=\"bg-white pd-medium w-m-25r radius-medium\"\n >\n <h3 class=\"h3 mn-b-medium\">Add to Playlist</h3>\n <!-- <PlaylistSelector \n :trackId=\"track._id\" \n @added=\"showAddToPlaylistModal = false\"\n /> -->\n <p class=\"t-transp\">Playlist selector coming soon...</p>\n </Popup>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Popup from '@martyrs/src/components/Popup/Popup.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconAdd from '@martyrs/src/modules/icons/navigation/IconAdd.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconCheck from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\n// import PlaylistSelector from '../forms/PlaylistSelector.vue';\n\n// Store\nimport { state as tracksState, actions as tracksActions } from '../../store/tracks.js';\nimport { actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst showDropdown = ref(false);\nconst showAddToPlaylistModal = ref(false);\nconst isFavorite = ref(false);\nconst isFollowingArtist = ref(false);\n\n// Clear state\ntracksState.currentTrack = null;\ntracksState.relatedTracks = [];\n\n// Computed\nconst track = computed(() => tracksState.currentTrack);\nconst relatedTracks = computed(() => tracksState.relatedTracks || []);\n\nconst isOwner = computed(() => {\n return track.value?.owner?.target === authState.user?._id;\n});\n\n// Format helpers\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playTrack = () => {\n if (track.value) {\n playerActions.setQueue([track.value]);\n }\n};\n\nconst toggleFavorite = () => {\n isFavorite.value = !isFavorite.value;\n // TODO: Implement actual saving\n};\n\nconst toggleFollowArtist = () => {\n isFollowingArtist.value = !isFollowingArtist.value;\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (track.value) {\n playerActions.addToQueue(track.value);\n }\n};\n\nconst editTrack = () => {\n router.push({ name: 'track-edit', params: { url: track.value.url } });\n};\n\nconst deleteTrack = async () => {\n if (confirm('Are you sure you want to delete this track?')) {\n try {\n await tracksActions.deleteTrack(track.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Failed to delete track:', error);\n }\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\n// Data fetching\nconst fetchTrackData = async () => {\n try {\n await tracksActions.fetchTrackByUrl(route.params.url);\n await tracksActions.fetchRelatedTracks(route.params.url);\n } catch (error) {\n console.error('Error loading track:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchTrackData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>\n\n<style scoped>\n</style>"],"names":["useRoute","useRouter","ref","tracksState","computed","authState","playerActions","tracksActions","onMounted"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkVA,UAAM,QAAQA,UAAAA,SAAQ;AACtB,UAAM,SAASC,UAAAA,UAAS;AAGxB,UAAM,QAAQ;AAGd,UAAM,YAAYC,IAAAA,IAAI,KAAK;AAC3B,UAAM,eAAeA,IAAAA,IAAI,KAAK;AAC9B,UAAM,yBAAyBA,IAAAA,IAAI,KAAK;AACxC,UAAM,aAAaA,IAAAA,IAAI,KAAK;AAC5B,UAAM,oBAAoBA,IAAAA,IAAI,KAAK;AAGnCC,WAAAA,MAAY,eAAe;AAC3BA,WAAAA,MAAY,gBAAgB,CAAA;AAG5B,UAAM,QAAQC,IAAAA,SAAS,MAAMD,OAAAA,MAAY,YAAY;AACrD,UAAM,gBAAgBC,IAAAA,SAAS,MAAMD,aAAY,iBAAiB,CAAA,CAAE;AAEpE,UAAM,UAAUC,IAAAA,SAAS,MAAM;AAC7B,aAAO,MAAM,OAAO,OAAO,WAAWC,KAAAA,MAAU,MAAM;AAAA,IACxD,CAAC;AAGD,UAAM,iBAAiB,CAAC,YAAY;AAClC,UAAI,CAAC,QAAS,QAAO;AACrB,YAAM,IAAI,KAAK,MAAM,UAAU,IAAI;AACnC,YAAM,IAAI,KAAK,MAAO,UAAU,OAAQ,EAAE;AAC1C,YAAM,IAAI,KAAK,MAAM,UAAU,EAAE;AAEjC,UAAI,IAAI,GAAG;AACT,eAAO,GAAG,CAAC,IAAI,EAAE,SAAQ,EAAG,SAAS,GAAG,GAAG,CAAC,IAAI,EAAE,SAAQ,EAAG,SAAS,GAAG,GAAG,CAAC;AAAA,MAC/E;AACA,aAAO,GAAG,CAAC,IAAI,EAAE,SAAQ,EAAG,SAAS,GAAG,GAAG,CAAC;AAAA,IAC9C;AAEA,UAAM,aAAa,CAAC,eAAe;AACjC,UAAI,CAAC,WAAY,QAAO;AACxB,aAAO,IAAI,KAAK,UAAU,EAAE,mBAAmB,SAAS;AAAA,QACtD,MAAM;AAAA,QACN,OAAO;AAAA,QACP,KAAK;AAAA,MACT,CAAG;AAAA,IACH;AAEA,UAAM,eAAe,CAAC,QAAQ;AAC5B,UAAI,CAAC,IAAK,QAAO;AACjB,UAAI,OAAO,KAAS;AAClB,gBAAQ,MAAM,KAAS,QAAQ,CAAC,IAAI;AAAA,MACtC,WAAW,OAAO,KAAM;AACtB,gBAAQ,MAAM,KAAM,QAAQ,CAAC,IAAI;AAAA,MACnC;AACA,aAAO,IAAI,SAAQ;AAAA,IACrB;AAGA,UAAM,YAAY,MAAM;AACtB,UAAI,MAAM,OAAO;AACfC,eAAAA,QAAc,SAAS,CAAC,MAAM,KAAK,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,UAAM,iBAAiB,MAAM;AAC3B,iBAAW,QAAQ,CAAC,WAAW;AAAA,IAEjC;AAEA,UAAM,qBAAqB,MAAM;AAC/B,wBAAkB,QAAQ,CAAC,kBAAkB;AAAA,IAE/C;AAEA,UAAM,aAAa,MAAM;AACvB,UAAI,MAAM,OAAO;AACfA,uBAAc,WAAW,MAAM,KAAK;AAAA,MACtC;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AACtB,aAAO,KAAK,EAAE,MAAM,cAAc,QAAQ,EAAE,KAAK,MAAM,MAAM,IAAG,EAAE,CAAE;AAAA,IACtE;AAEA,UAAM,cAAc,YAAY;AAC9B,UAAI,QAAQ,6CAA6C,GAAG;AAC1D,YAAI;AACF,gBAAMC,OAAAA,QAAc,YAAY,MAAM,MAAM,GAAG;AAC/C,iBAAO,KAAK,EAAE,MAAM,gBAAe,CAAE;AAAA,QACvC,SAAS,OAAO;AACd,kBAAQ,MAAM,2BAA2B,KAAK;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM;AACrB,gBAAU,UAAU,UAAU,OAAO,SAAS,IAAI;AAClD,mBAAa,QAAQ;AAAA,IACvB;AAGA,UAAM,iBAAiB,YAAY;AACjC,UAAI;AACF,cAAMA,OAAAA,QAAc,gBAAgB,MAAM,OAAO,GAAG;AACpD,cAAMA,OAAAA,QAAc,mBAAmB,MAAM,OAAO,GAAG;AAAA,MACzD,SAAS,OAAO;AACd,gBAAQ,MAAM,wBAAwB,KAAK;AAAA,MAC7C;AAAA,IACF;AAGAC,QAAAA,UAAU,YAAY;AACpB,YAAM,cAAc;AAEpB,YAAM,eAAc;AAEpB,gBAAU,QAAQ;AAClB,YAAM,aAAa;AAAA,IACrB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"Track.vue.cjs","sources":["../../../../../../../src/modules/music/components/pages/Track.vue"],"sourcesContent":["<!-- components/pages/Track.vue -->\n<template>\n <div class=\"track-page pd-small\">\n <!-- Loading -->\n <div v-if=\"isLoading\" class=\"w-100 h-25r flex-center flex\">\n <Loader />\n </div>\n\n <!-- Not Found -->\n <div v-if=\"hasLoaded && !track\" class=\"t-center pd-big\">\n <h2 class=\"\">Track not found</h2>\n <p class=\"t-transp t-medium\">The track you're looking for doesn't exist or has been removed.</p>\n </div>\n\n <!-- Track Content -->\n <div v-if=\"track\" class=\"track-content cols-2-fit-content mobile:cols-1 gap-big\">\n <!-- Left Column - Cover & Stats -->\n <div class=\"pos-sticky pos-t-0 mobile:pos-relative track-cover-section\">\n <!-- Cover with Play Overlay -->\n <div class=\"cover-container pos-relative mn-b-medium radius-big o-hidden\">\n <Media \n :url=\"track.coverUrl || (track.album && track.album.coverUrl) || '/logo/logo-placeholder.jpg'\" \n :alt=\"track.title\"\n class=\"aspect-1x1 w-100 radius-medium o-hidden\"\n />\n <div class=\"cover-overlay w-100 h-100 pos-absolute pos-t-0 pos-r-0 bg-black-transp-40 flex flex-center\">\n <Button\n @click=\"playTrack\"\n color=\"white\"\n size=\"big\"\n class=\"w-5r h-5r radius-big bg-main shadow-big hover-scale-110\"\n >\n <IconPlay v-if=\"!isPlaying\" fill=\"rgb(var(--white))\" class=\"i-medium\" />\n <IconPause v-else fill=\"rgb(var(--white))\" class=\"i-medium\" />\n </Button>\n </div>\n </div>\n\n \n\n <!-- Quick Stats -->\n <div class=\"stats-grid grid cols-2 gap-small\">\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.playCount) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Plays</div>\n </div>\n <div class=\"stat-card bg-light pd-medium radius-medium t-center\">\n <div class=\" mn-b-thin\">{{ formatNumber(track.views) }}</div>\n <div class=\"t-small t-transp t-uppercase\">Views</div>\n </div>\n </div>\n </div>\n\n <!-- Right Column - Track Details -->\n <div class=\"track-details-section\">\n <!-- Track Type Badge -->\n <div class=\"flex items-center gap-small mn-b-small\">\n <span class=\"bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase\">Single</span>\n <span class=\"bg-light t-medium pd-thin radius-thin uppercase t-small t-uppercase\">Explicit</span>\n </div>\n\n <!-- Track Title -->\n <h1 class=\"h1 mn-b-medium\">{{ track.title }}</h1>\n <!-- Action Buttons -->\n <div class=\"flex gap-small mn-b-medium\">\n <Button\n @click=\"playTrack\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 t-white bg-black radius-thin flex-center gap-thin\"\n >\n <IconPlay v-if=\"!isPlaying\" fill=\"rgb(var(--white))\" class=\"i-medium\" />\n <IconPause v-else fill=\"rgb(var(--white))\" class=\"i-medium\" />\n {{ !isPlaying ? 'Play' : 'Pause'}}\n </Button>\n\n <Button\n @click=\"addToQueue\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconAdd class=\"i-medium\" />\n Add to Queue\n </Button>\n\n <Button\n @click=\"toggleFavorite\"\n color=\"primary\"\n size=\"medium\"\n class=\"flex-1 bg-light radius-thin flex-center gap-thin\"\n >\n <IconLike class=\"i-medium\" :fill=\"isFavorite ? 'rgb(var(--main)':'rgb(var(--black)'\" />\n {{isFavorite ? 'Liked' : 'Like'}}\n </Button>\n\n <Dropdown :label=\"{component: IconEllipsis, class: 'bg-light radius-thin pd-thin i-big' }\" v-model=\"showDropdown\" class=\"relative\">\n <template #trigger>\n <Button color=\"transp\" size=\"medium\" class=\"w-3r h-3r radius-full\">\n <IconEllipsis class=\"w-1-25r h-1-25r\" />\n </Button>\n </template>\n <template #default>\n <div class=\"dropdown-menu bg-dark pd-small radius-medium shadow-big mn-t-thin\">\n <Button @click=\"showAddToPlaylistModal = true\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Add to Playlist\n </Button>\n <Button @click=\"copyLink\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Copy Link\n </Button>\n <template v-if=\"isOwner\">\n <hr class=\"mn-v-thin border-dark-transp-10\" />\n <Button @click=\"editTrack\" color=\"transp\" size=\"small\" class=\"w-100 justify-start\">\n Edit Track\n </Button>\n <Button @click=\"deleteTrack\" color=\"danger\" size=\"small\" class=\"w-100 justify-start\">\n Delete Track\n </Button>\n </template>\n </div>\n </template>\n </Dropdown>\n </div>\n\n <!-- Artist Card -->\n <div class=\"artists-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\" v-if=\"track.artist\">Artist</h3>\n <div class=\"flex flex-col gap-small\">\n <ArtistCardSmall \n :key=\"track.artist._id\"\n :artist=\"track.artist\"\n :is-following=\"isFollowingArtist\"\n :show-follow-button=\"!isOwner\"\n @toggle-follow=\"toggleFollowArtist\"\n />\n </div>\n </div>\n\n <!-- Metadata Cards -->\n <h3 class=\"t-medium mn-b-small\">Metadata</h3>\n <div class=\"metadata-grid grid cols-2 gap-small mn-b-medium\">\n <!-- Release Date -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCalendar class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Released</div>\n <div class=\"t-medium \">{{ formatDate(track.releaseDate) }}</div>\n </div>\n </div>\n\n <!-- Total Duration -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconClock class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Duration</div>\n <div class=\"t-medium \">{{ formatDuration(track.duration) }}</div>\n </div>\n </div>\n\n <!-- Label -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconCheck class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Status</div>\n <div class=\"t-medium \">{{ track.status }}</div>\n </div>\n </div>\n\n <!-- Visibility -->\n <div class=\"metadata-card bg-light pd-medium radius-medium flex items-center gap-medium\">\n <IconEye class=\"i-regular t-primary\" />\n <div>\n <div class=\"t-small t-transp t-uppercase\">Visibility</div>\n <div class=\"t-medium \">{{ track.isPublic ? 'Public' : 'Private' }}</div>\n </div>\n </div>\n </div>\n\n\n <!-- Album Info -->\n <h3 v-if=\"track.album\" class=\"t-medium mn-b-small\">From Album</h3>\n <div v-if=\"track.album\" class=\"album-card bg-light pd-medium radius-medium mn-b-medium\">\n \n <router-link \n :to=\"`/album/${track.album.url}`\"\n class=\"flex flex-v-center gap-thin hover-opacity\"\n >\n <Media \n :url=\"track.album.coverArt || '/logo/logo-placeholder.jpg'\"\n :alt=\"track.album.title\"\n class=\"w-3r h-3r radius-thin o-hidden object-cover\"\n />\n <span class=\"t-medium \">{{ track.album.title }}</span>\n </router-link>\n </div>\n\n <!-- Genres & Tags -->\n <div v-if=\"(track.genre && track.genre.length) || (track.tags && track.tags.length)\" class=\"tags-section mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">Genres & Tags</h3>\n <div class=\"flex gap-thin flex-wrap\">\n <span \n v-for=\"genre in track.genre\" \n :key=\"genre\"\n class=\"tag bg-main t-medium pd-thin radius-thin t-small cursor-pointer\"\n >\n {{ genre.name || genre }}\n </span>\n <span \n v-for=\"tag in track.tags\" \n :key=\"tag\"\n class=\"tag bg-light t-transp pd-thin-big radius-small t-small hover-bg-light cursor-pointer\"\n >\n #{{ tag.name || tag }}\n </span>\n </div>\n </div>\n\n <!-- Description -->\n <div v-if=\"track.description\" class=\"description-section bg-light pd-medium radius-medium mn-b-medium\">\n <h3 class=\"t-medium mn-b-small\">About</h3>\n <p class=\"t-transp\">{{ track.description }}</p>\n </div>\n\n <!-- Lyrics -->\n <div v-if=\"track.lyrics\" class=\"lyrics-section bg-light pd-medium radius-medium\">\n <h3 class=\"t-medium mn-b-small\">Lyrics</h3>\n <pre class=\"t-transp t-small\">{{ track.lyrics }}</pre>\n </div>\n </div>\n </div>\n\n <!-- Related Tracks -->\n <section v-if=\"track && relatedTracks && relatedTracks.length\" class=\"related-section mn-t-big\">\n <h2 class=\"h2 mn-b-medium\">Related Tracks</h2>\n <Feed\n :store=\"{\n read: () => new Promise(resolve => resolve(relatedTracks || [])),\n state: { isLoading: false }\n }\"\n :external=\"true\"\n :items=\"relatedTracks\"\n :states=\"{\n empty: {\n title: 'No related tracks',\n description: 'Check back later for recommendations',\n class: 'pd-medium bg-light radius-medium'\n }\n }\"\n class=\"grid cols-2 cols-m-3 cols-l-4 gap-medium\"\n >\n <template #default=\"{ items }\">\n <div class=\"bg-light radius-medium o-hidden\">\n <TrackListCard\n v-for=\"(relatedTrack, index) in items\"\n :key=\"relatedTrack._id\"\n :track=\"relatedTrack\"\n :index=\"index\"\n :showAlbum=\"true\"\n :showCover=\"true\"\n />\n </div>\n </template>\n </Feed>\n </section>\n\n <!-- Add to Playlist Modal -->\n <Popup \n v-if=\"showAddToPlaylistModal\" \n @close-popup=\"showAddToPlaylistModal = false\" \n class=\"bg-white pd-medium w-m-25r radius-medium\"\n >\n <h3 class=\"h3 mn-b-medium\">Add to Playlist</h3>\n <!-- <PlaylistSelector \n :trackId=\"track._id\" \n @added=\"showAddToPlaylistModal = false\"\n /> -->\n <p class=\"t-transp\">Playlist selector coming soon...</p>\n </Popup>\n </div>\n</template>\n\n<script setup>\nimport { ref, computed, onMounted, watch } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport Button from '@martyrs/src/components/Button/Button.vue';\nimport Loader from '@martyrs/src/components/Loader/Loader.vue';\nimport Media from '@martyrs/src/components/Media/Media.vue';\nimport Dropdown from '@martyrs/src/components/Dropdown/Dropdown.vue';\nimport Popup from '@martyrs/src/components/Popup/Popup.vue';\nimport Feed from '@martyrs/src/components/Feed/Feed.vue';\n\n// Icons\nimport IconPlay from '@martyrs/src/modules/icons/navigation/IconPlay.vue';\nimport IconPause from '@martyrs/src/modules/icons/navigation/IconPause.vue';\nimport IconLike from '@martyrs/src/modules/icons/navigation/IconLike.vue';\nimport IconEllipsis from '@martyrs/src/modules/icons/navigation/IconEllipsis.vue';\nimport IconAdd from '@martyrs/src/modules/icons/navigation/IconAdd.vue';\nimport IconClock from '@martyrs/src/modules/icons/entities/IconTime.vue';\nimport IconCalendar from '@martyrs/src/modules/icons/entities/IconCalendar.vue';\nimport IconCheck from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\nimport IconEye from '@martyrs/src/modules/icons/actions/IconShow.vue';\nimport IconVerified from '@martyrs/src/modules/icons/navigation/IconCheckmark.vue';\n\n// Components\nimport TrackListCard from '../cards/TrackListCard.vue';\nimport ArtistCardSmall from '../cards/ArtistCardSmall.vue';\n// import PlaylistSelector from '../forms/PlaylistSelector.vue';\n\n// Store\nimport { state as tracksState, actions as tracksActions } from '../../store/tracks.js';\nimport { state as playerState, actions as playerActions } from '../../store/player.js';\nimport { state as authState } from '@martyrs/src/modules/auth/views/store/auth.js';\n\nconst isPlaying = computed(() => playerState.isPlaying);\n\nconst route = useRoute();\nconst router = useRouter();\n\n// Emits\nconst emits = defineEmits(['page-loading', 'page-loaded']);\n\n// State\nconst hasLoaded = ref(false);\nconst showDropdown = ref(false);\nconst showAddToPlaylistModal = ref(false);\nconst isFavorite = ref(false);\nconst isFollowingArtist = ref(false);\n\n// Clear state\ntracksState.currentTrack = null;\ntracksState.relatedTracks = [];\n\n// Computed\nconst track = computed(() => tracksState.currentTrack);\nconst relatedTracks = computed(() => tracksState.relatedTracks || []);\n\nconst isOwner = computed(() => {\n return track.value?.owner?.target === authState.user?._id;\n});\n\n// Format helpers\nconst formatDuration = (seconds) => {\n if (!seconds) return '0:00';\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n \n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n};\n\nconst formatDate = (dateString) => {\n if (!dateString) return 'Unknown';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n};\n\nconst formatNumber = (num) => {\n if (!num) return '0';\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1) + 'M';\n } else if (num >= 1000) {\n return (num / 1000).toFixed(1) + 'K';\n }\n return num.toString();\n};\n\n// Actions\nconst playTrack = () => {\n if (track.value) {\n playerActions.setQueue([track.value]);\n }\n};\n\nconst toggleFavorite = () => {\n isFavorite.value = !isFavorite.value;\n // TODO: Implement actual saving\n};\n\nconst toggleFollowArtist = () => {\n isFollowingArtist.value = !isFollowingArtist.value;\n // TODO: Implement actual following\n};\n\nconst addToQueue = () => {\n if (track.value) {\n playerActions.addToQueue(track.value);\n }\n};\n\nconst editTrack = () => {\n router.push({ name: 'track-edit', params: { url: track.value.url } });\n};\n\nconst deleteTrack = async () => {\n if (confirm('Are you sure you want to delete this track?')) {\n try {\n await tracksActions.deleteTrack(track.value._id);\n router.push({ name: 'music-library' });\n } catch (error) {\n console.error('Failed to delete track:', error);\n }\n }\n};\n\nconst copyLink = () => {\n navigator.clipboard.writeText(window.location.href);\n showDropdown.value = false;\n};\n\n// Data fetching\nconst fetchTrackData = async () => {\n try {\n await tracksActions.fetchTrackByUrl(route.params.url);\n await tracksActions.fetchRelatedTracks(route.params.url);\n } catch (error) {\n console.error('Error loading track:', error);\n }\n};\n\n// Lifecycle\nonMounted(async () => {\n emits('page-loading');\n \n await fetchTrackData();\n \n hasLoaded.value = true;\n emits('page-loaded');\n});\n</script>\n\n<style scoped>\n</style>"],"names":["computed","playerState","useRoute","useRouter","ref","tracksState","authState","playerActions","tracksActions","onMounted"],"mappings":"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"}
|