Sharing #1

Merged
artem merged 3 commits from dev into main 2023-02-17 00:06:14 +01:00
18 changed files with 629 additions and 76 deletions

View File

@ -1,5 +1,5 @@
{ {
"backend_de": "http://localhost:31204", "backend_dev": "http://localhost:31204",
"backend_dev": "https://webplay.rocks", "backend_de": "https://webplay.rocks",
"backend": "https://webplay.rocks" "backend": "https://webplay.rocks"
} }

View File

@ -272,6 +272,7 @@ td.fillCell>* {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
flex-grow: 1; flex-grow: 1;
text-align: left;
} }
.radioCover { .radioCover {
@ -593,6 +594,7 @@ td.fillCell>* {
.z1 { .z1 {
z-index: 1; z-index: 1;
} }
.z2 { .z2 {
z-index: 2; z-index: 2;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<template> <template>
<div id="app"> <div id="app">
<nav <nav
v-show="$route.path != '/login' && $route.path != '/setup'" v-show="$route.path != '/login' && $route.path != '/setup' && $route.path != '/share' "
:class="{ slideOverTop: $store.getters.isDialogOpen }" :class="{ slideOverTop: $store.getters.isDialogOpen }"
> >
<div> <div>
@ -245,7 +245,7 @@ export default {
}, },
login() { login() {
let hash = window.location.hash.replace("#/", ""); let hash = window.location.hash.replace("#/", "");
if (!hash.startsWith("login") && !hash.startsWith("setup")) { if (!hash.startsWith("login") && !hash.startsWith("setup") && !hash.startsWith("share")) {
let redirect = encodeURIComponent(hash); let redirect = encodeURIComponent(hash);
if (redirect) { if (redirect) {
this.$router.replace({ this.$router.replace({

View File

@ -0,0 +1,72 @@
<template>
<div id="content" class="flex-column">
<div class="flex-column ma-horizontal">
<h1>{{ album.title }}</h1>
<h2>{{ album.artist_name }}</h2>
</div>
<div id="tracks" class="flex-row border-top">
<div class="flex-column">
<img id="cover" class="shadow ma24" :src="cover" />
<p class="center ma-off hideOnMobile">
<b>{{ album.tracks.length }}</b> Tracks
</p>
</div>
<ul id="trackList" class="tracks">
<li v-for="track in album.tracks" :key="track._id">
<TrackItem :track="track" :showCover="false" />
</li>
</ul>
</div>
</div>
</template>
<script>
import TrackItem from "../components/Track";
export default {
data() {
return {
album: {
covers: {},
tracks: [],
},
};
},
methods: {
show(album) {
this.album = album;
},
},
computed: {
cover() {
if (!this.album.covers) {
return "/static/icons/dummy/album.svg";
}
return this.album.covers.cover256;
},
},
components: {
TrackItem,
},
};
</script>
<style scoped>
#cover {
align-self: center;
width: 256px;
}
#content,
#tracks {
overflow: auto;
}
@media (max-width: 480px) {
#tracks {
flex-direction: column;
}
#trackList {
border-top: 1px solid var(--light-border);
}
}
</style>

View File

@ -0,0 +1,78 @@
<template>
<div id="content" class="flex-column">
<div class="flex-column ma-horizontal">
<h1>{{ box.title }}</h1>
</div>
<div id="videos" class="flex-row border-top">
<div class="flex-column">
<img id="cover" class="shadow ma24" :src="cover" />
<p class="center ma-off hideOnMobile">
<b>{{ box.videos.length }}</b> Videos
</p>
</div>
<ul id="videoList" class="videos">
<li v-for="video in box.videos" :key="video._id">
<VideoItem :video="video" />
</li>
</ul>
</div>
</div>
</template>
<script>
import VideoItem from "../components/Video.vue";
export default {
data() {
return {
box: {
covers: {},
videos: [],
},
};
},
methods: {
show(box) {
this.box = box;
},
},
computed: {
cover() {
if (!this.box.covers) {
return "/static/icons/dummy/box.svg";
}
return this.box.covers.cover256;
},
},
components: {
VideoItem,
},
};
</script>
<style scoped>
#cover {
align-self: center;
width: 256px;
}
#content,
#videos {
overflow: auto;
}
.video {
max-width: 256px;
}
@media (max-width: 480px) {
#videos {
flex-direction: column;
}
#videoList {
border-top: 1px solid var(--light-border);
}
.video {
max-width: inherit;
}
}
</style>

View File

@ -181,6 +181,13 @@ export default {
this.audio.pause(); this.audio.pause();
this.audio.src = url; this.audio.src = url;
this.pushHistoryItem();
},
pushHistoryItem() {
if (!this.currentUser._id) {
return;
}
let item = { let item = {
id: this.currentTrackParent._id, id: this.currentTrackParent._id,
type: this.currentTrackParentType, type: this.currentTrackParentType,
@ -258,6 +265,7 @@ export default {
} }
}, },
gotoContainer() { gotoContainer() {
if (this.currentUser._id) {
switch (this.selectedTrack.parentType) { switch (this.selectedTrack.parentType) {
case "album": case "album":
this.$router.push("/albums?id=" + this.selectedTrack.parent._id); this.$router.push("/albums?id=" + this.selectedTrack.parent._id);
@ -268,6 +276,7 @@ export default {
); );
break; break;
} }
}
}, },
switchShuffle() { switchShuffle() {
this.$store.dispatch("player/toggleShuffleMode"); this.$store.dispatch("player/toggleShuffleMode");
@ -275,9 +284,15 @@ export default {
}, },
switchRepeatType() { switchRepeatType() {
this.$store.dispatch("player/switchPlayerRepeatMode"); this.$store.dispatch("player/switchPlayerRepeatMode");
if (!this.currentUser._id) {
return;
}
this.saveUserSettings(); this.saveUserSettings();
}, },
saveUserSettings() { saveUserSettings() {
if (!this.currentUser._id) {
return;
}
this.$store.dispatch("user/savePlayerSettings"); this.$store.dispatch("user/savePlayerSettings");
}, },
timeUpdate(event) { timeUpdate(event) {
@ -314,6 +329,9 @@ export default {
} }
return type; return type;
}, },
currentUser() {
return this.$store.getters["user/user"];
},
formatedD() { formatedD() {
let m = Math.floor(this.duration / 60); let m = Math.floor(this.duration / 60);
let s = Math.floor(this.duration - m * 60); let s = Math.floor(this.duration - m * 60);

View File

@ -40,7 +40,13 @@
<button v-if="!selectedAlbum.share._id" @click="shareEnable"> <button v-if="!selectedAlbum.share._id" @click="shareEnable">
<awesome-icon icon="share" />Share this album <awesome-icon icon="share" />Share this album
</button> </button>
<button v-else @click="shareDisable"> <button
v-if="selectedAlbum.share._id"
@click="addShareUrlToClipboard"
>
<awesome-icon icon="clipboard" />Copy url into clipboard
</button>
<button v-if="selectedAlbum.share._id" @click="shareDisable">
<awesome-icon icon="share" />Remove share <awesome-icon icon="share" />Remove share
</button> </button>
</div> </div>
@ -102,7 +108,6 @@ import { mapGetters } from "vuex";
export default { export default {
data() { data() {
return { return {
move: 152,
albums: [], albums: [],
scrollTimer: 0, scrollTimer: 0,
loadingPrev: false, loadingPrev: false,
@ -110,12 +115,12 @@ export default {
elements: {}, elements: {},
}; };
}, },
mounted() {
if (window.innerWidth <= 480 || window.innerHeight <= 480) {
this.move = 120;
}
},
methods: { methods: {
addShareUrlToClipboard() {
let url =
window.location.origin + "/#/share?id=" + this.selectedAlbum.share._id;
navigator.clipboard.writeText(url);
},
dblclick() { dblclick() {
this.$store.commit("tracks/resetSelectedTrack"); this.$store.commit("tracks/resetSelectedTrack");
this.$store.commit("radios/resetSelectedRadio"); this.$store.commit("radios/resetSelectedRadio");
@ -214,7 +219,11 @@ export default {
this.$store.dispatch("albums/selectAlbum", album); this.$store.dispatch("albums/selectAlbum", album);
}, },
shareEnable() { shareEnable() {
this.$store.dispatch("albums/shareEnable", this.selectedAlbum); this.$store
.dispatch("albums/shareEnable", this.selectedAlbum)
.then(() => {
this.addShareUrlToClipboard();
});
}, },
shareDisable() { shareDisable() {
this.$store.dispatch("albums/shareDisable", this.selectedAlbum); this.$store.dispatch("albums/shareDisable", this.selectedAlbum);
@ -348,7 +357,7 @@ export default {
border-top: 1px solid #ffffff20; border-top: 1px solid #ffffff20;
border-bottom: 1px solid #00000020; border-bottom: 1px solid #00000020;
} }
#stats button { .dropdown-activator button {
width: 32px; width: 32px;
height: 32px; height: 32px;
} }

View File

@ -41,7 +41,13 @@
<button v-if="!selectedBox.share._id" @click="shareEnable"> <button v-if="!selectedBox.share._id" @click="shareEnable">
<awesome-icon icon="share" />Share this box <awesome-icon icon="share" />Share this box
</button> </button>
<button v-else @click="shareDisable"> <button
v-if="selectedBox.share._id"
@click="addShareUrlToClipboard"
>
<awesome-icon icon="clipboard" />Copy url into clipboard
</button>
<button v-if="selectedBox.share._id" @click="shareDisable">
<awesome-icon icon="share" />Remove share <awesome-icon icon="share" />Remove share
</button> </button>
</div> </div>
@ -95,6 +101,11 @@ import { mapGetters } from "vuex";
export default { export default {
methods: { methods: {
addShareUrlToClipboard() {
let url =
window.location.origin + "/#/share?id=" + this.selectedBox.share._id;
navigator.clipboard.writeText(url);
},
dblclick() { dblclick() {
this.$store.commit("tracks/resetSelectedTrack"); this.$store.commit("tracks/resetSelectedTrack");
this.$store.commit("radios/resetSelectedRadio"); this.$store.commit("radios/resetSelectedRadio");
@ -174,7 +185,9 @@ export default {
this.$store.dispatch("boxes/resetCover", this.selectedBox); this.$store.dispatch("boxes/resetCover", this.selectedBox);
}, },
shareEnable() { shareEnable() {
this.$store.dispatch("boxes/shareEnable", this.selectedBox); this.$store.dispatch("boxes/shareEnable", this.selectedBox).then(() => {
this.addShareUrlToClipboard();
});
}, },
shareDisable() { shareDisable() {
this.$store.dispatch("boxes/shareDisable", this.selectedBox); this.$store.dispatch("boxes/shareDisable", this.selectedBox);
@ -274,7 +287,7 @@ export default {
border-top: 1px solid #ffffff20; border-top: 1px solid #ffffff20;
border-bottom: 1px solid #00000020; border-bottom: 1px solid #00000020;
} }
#stats button { .dropdown-activator button {
width: 32px; width: 32px;
height: 32px; height: 32px;
} }

View File

@ -89,6 +89,9 @@ export default {
this.$store.dispatch("videos/playNextTo", this.selectedVideo); this.$store.dispatch("videos/playNextTo", this.selectedVideo);
}, },
pushHistoryItem() { pushHistoryItem() {
if (!this.currentUser._id) {
return;
}
let item = { let item = {
id: this.selectedVideo.parent._id, id: this.selectedVideo.parent._id,
type: "box", type: "box",
@ -154,6 +157,9 @@ export default {
}, },
}, },
computed: { computed: {
currentUser() {
return this.$store.getters["user/user"];
},
selectedVideo() { selectedVideo() {
return this.$store.getters["videos/selectedVideo"]; return this.$store.getters["videos/selectedVideo"];
}, },

View File

@ -7,6 +7,7 @@ import RadiosView from "./views/Radios";
import BoxesView from "./views/Boxes"; import BoxesView from "./views/Boxes";
import SearchView from "./views/Search"; import SearchView from "./views/Search";
import SetupView from "./views/Setup"; import SetupView from "./views/Setup";
import ShareView from "./views/Share";
import HomeView from "./views/Home"; import HomeView from "./views/Home";
import UsersView from "./views/Users"; import UsersView from "./views/Users";
import FavouritesView from "./views/Favourites" import FavouritesView from "./views/Favourites"
@ -56,6 +57,10 @@ const routes = [
path: "/setup", path: "/setup",
component: SetupView component: SetupView
}, },
{
path: "/share",
component: ShareView
},
{ {
path: "/me", path: "/me",
component: UsersView component: UsersView

View File

@ -15,6 +15,7 @@ import user from "./modules/user"
import videos from "./modules/videos" import videos from "./modules/videos"
import system from "./modules/system" import system from "./modules/system"
import search from "./modules/search" import search from "./modules/search"
import share from "./modules/share"
export default createStore({ export default createStore({
state, state,
@ -31,6 +32,7 @@ export default createStore({
user, user,
videos, videos,
system, system,
search search,
share
} }
}) })

View File

@ -155,8 +155,11 @@ export default {
axios.put(context.rootGetters.server + "/api/albums/" + album._id, body, context.rootGetters.headers); axios.put(context.rootGetters.server + "/api/albums/" + album._id, body, context.rootGetters.headers);
}, },
shareEnable(context, album) { shareEnable(context, album) {
return new Promise(resolve => {
axios.post(context.rootGetters.server + "/api/albums/" + album._id + "/share", {}, context.rootGetters.headers).then(res => { axios.post(context.rootGetters.server + "/api/albums/" + album._id + "/share", {}, context.rootGetters.headers).then(res => {
album.share = res.data; album.share = res.data;
resolve();
});
}); });
}, },
shareDisable(context, album) { shareDisable(context, album) {

View File

@ -131,8 +131,11 @@ export default {
}); });
}, },
shareEnable(context, box) { shareEnable(context, box) {
return new Promise(resolve => {
axios.post(context.rootGetters.server + "/api/boxes/" + box._id + "/share", {}, context.rootGetters.headers).then(res => { axios.post(context.rootGetters.server + "/api/boxes/" + box._id + "/share", {}, context.rootGetters.headers).then(res => {
box.share = res.data; box.share = res.data;
resolve();
});
}); });
}, },
shareDisable(context, box) { shareDisable(context, box) {

View File

@ -0,0 +1,29 @@
import axios from 'axios'
export default {
get(context, id) {
return new Promise((resolve) => {
axios.get(context.rootGetters.server + "/api/shares/" + id, context.rootGetters.headers).then((res) => {
if (!res.data._id) {
resolve(res.data);
return;
}
switch (res.data.object.type) {
case "album":
res.data.object.tracks.forEach(track => {
track.parent = res.data.object;
track.parentType = "album"
});
break;
case "box":
res.data.object.videos.forEach(video => {
video.parent = res.data.object;
video.parentType = "box"
});
break;
}
resolve(res.data);
});
});
},
}

View File

@ -0,0 +1,9 @@
//import state from './state.js';
//import getters from './getters.js';
//import mutations from './mutations.js';
import actions from './actions.js';
export default {
namespaced: true,
actions
}

View File

@ -4,7 +4,7 @@ import router from '../../../router'
export default { export default {
play(context, video) { play(context, video) {
context.commit("selectVideo", video); context.commit("selectVideo", video);
if (context.rootGetters.routerQuery.play != video._id) { if (context.rootGetters.routerQuery.play != video._id && context.rootGetters.routerPath != "/share") {
router.push("/boxes?id=" + video.parent._id + "&play=" + video._id); router.push("/boxes?id=" + video.parent._id + "&play=" + video._id);
} }
}, },

68
src/views/Share.vue Normal file
View File

@ -0,0 +1,68 @@
<template>
<div ref="share" id="share" class="flex-column">
<AlbumContent ref="album" v-if="type == 'album'" />
<BoxContent ref="box" v-if="type == 'box'" />
</div>
</template>
<script>
import AlbumContent from "../components/AlbumContent.vue";
import BoxContent from "../components/BoxContent.vue";
export default {
name: "ShareView",
data() {
return {
type: "",
};
},
methods: {
loadShare() {
if (this.$route.query.id) {
this.$store.dispatch("share/get", this.$route.query.id).then((res) => {
if (!res._id) {
this.$router.replace("/login");
return;
}
this.type = res.object.type;
this.$nextTick(() => {
switch (this.type) {
case "album":
this.$refs.album.show(res.object);
break;
case "box":
this.$refs.box.show(res.object);
break;
}
});
});
} else {
this.$router.replace("/login");
}
},
},
computed: {
server() {
return this.$store.getters.server;
},
},
components: {
AlbumContent,
BoxContent,
},
watch: {
server() {
this.loadShare();
},
"$route.query.id": function () {
this.loadShare();
},
},
};
</script>
<style scoped>
#share {
overflow: auto;
}
</style>