|
@@ -1,78 +1,119 @@
|
|
|
<template>
|
|
|
<div v-if="isUser">
|
|
|
<metadata v-bind:title="`Profile | ${user.username}`" />
|
|
|
+ <edit-playlist v-if="modals.editPlaylist" />
|
|
|
+ <create-playlist v-if="modals.createPlaylist" />
|
|
|
<main-header />
|
|
|
- <div class="container">
|
|
|
- <img
|
|
|
- class="avatar"
|
|
|
- :src="
|
|
|
- user.avatar
|
|
|
- ? `${user.avatar}?d=${notes}&s=250`
|
|
|
- : '/assets/notes.png'
|
|
|
- "
|
|
|
- onerror="this.src='/assets/notes.png'; this.onerror=''"
|
|
|
- />
|
|
|
- <h2 class="has-text-centered username">@{{ user.username }}</h2>
|
|
|
- <h5>A member since {{ user.createdAt }}</h5>
|
|
|
- <div
|
|
|
- v-if="role === 'admin' && userId !== user._id"
|
|
|
- class="admin-functionality"
|
|
|
- >
|
|
|
- <a
|
|
|
- v-if="user.role == 'default'"
|
|
|
- class="button is-small is-info is-outlined"
|
|
|
- @click="changeRank('admin')"
|
|
|
- >Promote to Admin</a
|
|
|
+ <div class="info-section">
|
|
|
+ <div class="picture-name-row">
|
|
|
+ <img
|
|
|
+ class="profile-picture"
|
|
|
+ :src="
|
|
|
+ user.avatar
|
|
|
+ ? `${user.avatar}?d=${notes}&s=250`
|
|
|
+ : '/assets/notes.png'
|
|
|
+ "
|
|
|
+ onerror="this.src='/assets/notes.png'; this.onerror=''"
|
|
|
+ />
|
|
|
+ <div>
|
|
|
+ <div class="name-role-row">
|
|
|
+ <p class="name">{{ user.name }}</p>
|
|
|
+ <span class="role admin" v-if="user.role === 'admin'"
|
|
|
+ >admin</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <p class="username">@{{ user.username }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="buttons" v-if="userId === user._id || role === 'admin'">
|
|
|
+ <router-link
|
|
|
+ :to="`/admin/users?userId=${user._id}`"
|
|
|
+ class="button is-primary"
|
|
|
+ v-if="role === 'admin'"
|
|
|
>
|
|
|
- <a
|
|
|
- v-if="user.role == 'admin'"
|
|
|
- class="button is-small is-danger is-outlined"
|
|
|
- @click="changeRank('default')"
|
|
|
- >Demote to User</a
|
|
|
+ Edit
|
|
|
+ </router-link>
|
|
|
+ <router-link
|
|
|
+ to="/settings"
|
|
|
+ class="button is-primary"
|
|
|
+ v-if="userId === user._id"
|
|
|
>
|
|
|
+ Settings
|
|
|
+ </router-link>
|
|
|
</div>
|
|
|
- <nav class="level">
|
|
|
- <div class="level-item has-text-centered">
|
|
|
- <p class="heading">
|
|
|
- Rank
|
|
|
- </p>
|
|
|
- <p class="title role">
|
|
|
- {{ user.role }}
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- <div class="level-item has-text-centered">
|
|
|
- <p class="heading">
|
|
|
- Songs Requested
|
|
|
- </p>
|
|
|
- <p class="title">
|
|
|
- {{ user.statistics.songsRequested }}
|
|
|
- </p>
|
|
|
+ <div class="bio-row" v-if="user.bio">
|
|
|
+ <i class="material-icons">notes</i>
|
|
|
+ <p>{{ user.bio }}</p>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="date-location-row"
|
|
|
+ v-if="user.createdAt || user.location"
|
|
|
+ >
|
|
|
+ <div class="date" v-if="user.createdAt">
|
|
|
+ <i class="material-icons">calendar_today</i>
|
|
|
+ <p>{{ user.createdAt }}</p>
|
|
|
</div>
|
|
|
- <div class="level-item has-text-centered">
|
|
|
- <p class="heading">
|
|
|
- Likes
|
|
|
- </p>
|
|
|
- <p class="title">
|
|
|
- {{ user.liked.length }}
|
|
|
- </p>
|
|
|
+ <div class="location" v-if="user.location">
|
|
|
+ <i class="material-icons">location_on</i>
|
|
|
+ <p>{{ user.location }}</p>
|
|
|
</div>
|
|
|
- <div class="level-item has-text-centered">
|
|
|
- <p class="heading">
|
|
|
- Dislikes
|
|
|
- </p>
|
|
|
- <p class="title">
|
|
|
- {{ user.disliked.length }}
|
|
|
- </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="bottom-section">
|
|
|
+ <div class="buttons">
|
|
|
+ <button
|
|
|
+ :class="{ active: activeTab === 'recentActivity' }"
|
|
|
+ @click="switchTab('recentActivity')"
|
|
|
+ >
|
|
|
+ Recent activity
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ :class="{ active: activeTab === 'playlists' }"
|
|
|
+ @click="switchTab('playlists')"
|
|
|
+ v-if="user._id === userId"
|
|
|
+ >
|
|
|
+ Playlists
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="content recent-activity-tab"
|
|
|
+ v-if="activeTab === 'recentActivity'"
|
|
|
+ >
|
|
|
+ Content here
|
|
|
+ </div>
|
|
|
+ <div class="content playlists-tab" v-if="activeTab === 'playlists'">
|
|
|
+ <div
|
|
|
+ class="playlist"
|
|
|
+ v-for="playlist in playlists"
|
|
|
+ :key="playlist._id"
|
|
|
+ >
|
|
|
+ <span>{{ playlist.displayName }}</span>
|
|
|
+ <button
|
|
|
+ class="button is-primary"
|
|
|
+ @click="editPlaylistClick(playlist._id)"
|
|
|
+ >
|
|
|
+ Edit
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- </nav>
|
|
|
+ <button
|
|
|
+ class="button is-primary"
|
|
|
+ @click="
|
|
|
+ openModal({
|
|
|
+ sector: 'station',
|
|
|
+ modal: 'createPlaylist'
|
|
|
+ })
|
|
|
+ "
|
|
|
+ >
|
|
|
+ Make new playlist
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<main-footer />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { mapState } from "vuex";
|
|
|
-import Toast from "toasters";
|
|
|
+import { mapState, mapActions } from "vuex";
|
|
|
import { format, parseISO } from "date-fns";
|
|
|
|
|
|
import MainHeader from "../MainHeader.vue";
|
|
@@ -80,17 +121,27 @@ import MainFooter from "../MainFooter.vue";
|
|
|
import io from "../../io";
|
|
|
|
|
|
export default {
|
|
|
- components: { MainHeader, MainFooter },
|
|
|
+ components: {
|
|
|
+ MainHeader,
|
|
|
+ MainFooter,
|
|
|
+ CreatePlaylist: () => import("../Modals/Playlists/Create.vue"),
|
|
|
+ EditPlaylist: () => import("../Modals/Playlists/Edit.vue")
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
user: {},
|
|
|
notes: "",
|
|
|
- isUser: false
|
|
|
+ isUser: false,
|
|
|
+ activeTab: "recentActivity",
|
|
|
+ playlists: []
|
|
|
};
|
|
|
},
|
|
|
computed: mapState({
|
|
|
role: state => state.user.auth.role,
|
|
|
- userId: state => state.user.auth.userId
|
|
|
+ userId: state => state.user.auth.userId,
|
|
|
+ ...mapState("modals", {
|
|
|
+ modals: state => state.modals.station
|
|
|
+ })
|
|
|
}),
|
|
|
mounted() {
|
|
|
lofig.get("frontendDomain").then(frontendDomain => {
|
|
@@ -112,28 +163,103 @@ export default {
|
|
|
"MMMM do yyyy"
|
|
|
);
|
|
|
this.isUser = true;
|
|
|
+
|
|
|
+ if (this.user._id === this.userId) {
|
|
|
+ this.socket.emit("playlists.indexForUser", res => {
|
|
|
+ if (res.status === "success")
|
|
|
+ this.playlists = res.data;
|
|
|
+ });
|
|
|
+ this.socket.on(
|
|
|
+ "event:playlist.create",
|
|
|
+ playlist => {
|
|
|
+ this.playlists.push(playlist);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ this.socket.on(
|
|
|
+ "event:playlist.delete",
|
|
|
+ playlistId => {
|
|
|
+ this.playlists.forEach(
|
|
|
+ (playlist, index) => {
|
|
|
+ if (playlist._id === playlistId) {
|
|
|
+ this.playlists.splice(index, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ this.socket.on("event:playlist.addSong", data => {
|
|
|
+ this.playlists.forEach((playlist, index) => {
|
|
|
+ if (playlist._id === data.playlistId) {
|
|
|
+ this.playlists[index].songs.push(
|
|
|
+ data.song
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ this.socket.on(
|
|
|
+ "event:playlist.removeSong",
|
|
|
+ data => {
|
|
|
+ this.playlists.forEach(
|
|
|
+ (playlist, index) => {
|
|
|
+ if (
|
|
|
+ playlist._id === data.playlistId
|
|
|
+ ) {
|
|
|
+ this.playlists[
|
|
|
+ index
|
|
|
+ ].songs.forEach(
|
|
|
+ (song, index2) => {
|
|
|
+ if (
|
|
|
+ song._id ===
|
|
|
+ data.songId
|
|
|
+ ) {
|
|
|
+ this.playlists[
|
|
|
+ index
|
|
|
+ ].songs.splice(
|
|
|
+ index2,
|
|
|
+ 1
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ this.socket.on(
|
|
|
+ "event:playlist.updateDisplayName",
|
|
|
+ data => {
|
|
|
+ this.playlists.forEach(
|
|
|
+ (playlist, index) => {
|
|
|
+ if (
|
|
|
+ playlist._id === data.playlistId
|
|
|
+ ) {
|
|
|
+ this.playlists[
|
|
|
+ index
|
|
|
+ ].displayName =
|
|
|
+ data.displayName;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
);
|
|
|
});
|
|
|
},
|
|
|
methods: {
|
|
|
- changeRank(newRank) {
|
|
|
- this.socket.emit(
|
|
|
- "users.updateRole",
|
|
|
- this.user._id,
|
|
|
- newRank === "admin" ? "admin" : "default",
|
|
|
- res => {
|
|
|
- if (res.status === "error")
|
|
|
- new Toast({ content: res.message, timeout: 2000 });
|
|
|
- else this.user.role = newRank;
|
|
|
- new Toast({
|
|
|
- content: `User ${this.$route.params.username}'s rank has been changed to: ${newRank}`,
|
|
|
- timeout: 2000
|
|
|
- });
|
|
|
- }
|
|
|
- );
|
|
|
- }
|
|
|
+ switchTab(tabName) {
|
|
|
+ this.activeTab = tabName;
|
|
|
+ },
|
|
|
+ editPlaylistClick(playlistId) {
|
|
|
+ console.log(playlistId);
|
|
|
+ this.editPlaylist(playlistId);
|
|
|
+ this.openModal({ sector: "station", modal: "editPlaylist" });
|
|
|
+ },
|
|
|
+ ...mapActions("modals", ["openModal"]),
|
|
|
+ ...mapActions("user/playlists", ["editPlaylist"])
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
@@ -141,48 +267,167 @@ export default {
|
|
|
<style lang="scss" scoped>
|
|
|
@import "styles/global.scss";
|
|
|
|
|
|
-.night-mode {
|
|
|
- .level .title {
|
|
|
- color: #ccc;
|
|
|
+.info-section {
|
|
|
+ width: 912px;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ margin-top: 32px;
|
|
|
+ padding: 24px;
|
|
|
+
|
|
|
+ .picture-name-row {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ column-gap: 32px;
|
|
|
+ justify-content: center;
|
|
|
+ margin-bottom: 24px;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-.container {
|
|
|
- padding: 25px;
|
|
|
-}
|
|
|
+ .profile-picture {
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ border-radius: 100%;
|
|
|
+ }
|
|
|
|
|
|
-.avatar {
|
|
|
- border-radius: 50%;
|
|
|
- width: 250px;
|
|
|
- height: 250px;
|
|
|
- display: block;
|
|
|
- margin: auto;
|
|
|
- background: #fff;
|
|
|
-}
|
|
|
+ .name-role-row {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ column-gap: 12px;
|
|
|
+ }
|
|
|
|
|
|
-h5 {
|
|
|
- text-align: center;
|
|
|
- margin-bottom: 25px;
|
|
|
- font-size: 17px;
|
|
|
-}
|
|
|
+ .name {
|
|
|
+ font-size: 34px;
|
|
|
+ line-height: 40px;
|
|
|
+ color: $dark-grey-3;
|
|
|
+ }
|
|
|
|
|
|
-.role {
|
|
|
- text-transform: capitalize;
|
|
|
-}
|
|
|
+ .role {
|
|
|
+ padding: 2px 24px;
|
|
|
+ color: $white;
|
|
|
+ text-transform: uppercase;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 14px;
|
|
|
+ height: 18px;
|
|
|
+ border-radius: 5px;
|
|
|
+
|
|
|
+ &.admin {
|
|
|
+ background-color: $red;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .username {
|
|
|
+ font-size: 24px;
|
|
|
+ line-height: 28px;
|
|
|
+ color: $dark-grey;
|
|
|
+ }
|
|
|
+
|
|
|
+ .buttons {
|
|
|
+ width: 388px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ column-gap: 20px;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ margin-bottom: 24px;
|
|
|
+
|
|
|
+ .button {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 17px;
|
|
|
+ line-height: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .bio-row,
|
|
|
+ .date-location-row {
|
|
|
+ i {
|
|
|
+ font-size: 24px;
|
|
|
+ color: $dark-grey-2;
|
|
|
+ margin-right: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ p {
|
|
|
+ font-size: 17px;
|
|
|
+ line-height: 20px;
|
|
|
+ color: $dark-grey-2;
|
|
|
+ word-break: break-word;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .bio-row {
|
|
|
+ max-width: 608px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ display: flex;
|
|
|
+ width: max-content;
|
|
|
+ }
|
|
|
|
|
|
-.level {
|
|
|
- margin-top: 40px;
|
|
|
+ .date-location-row {
|
|
|
+ max-width: 608px;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ display: flex;
|
|
|
+ width: max-content;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ column-gap: 48px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .date,
|
|
|
+ .location {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.admin-functionality {
|
|
|
- text-align: center;
|
|
|
- margin: 0 auto;
|
|
|
+.bottom-section {
|
|
|
+ width: 962px;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ margin-top: 32px;
|
|
|
+ padding: 24px;
|
|
|
+ display: flex;
|
|
|
+ column-gap: 64px;
|
|
|
+
|
|
|
+ .buttons {
|
|
|
+ height: 100%;
|
|
|
+ width: 250px;
|
|
|
+
|
|
|
+ button {
|
|
|
+ outline: none;
|
|
|
+ border: none;
|
|
|
+ box-shadow: none;
|
|
|
+ color: $musareBlue;
|
|
|
+ font-size: 22px;
|
|
|
+ line-height: 26px;
|
|
|
+ padding: 7px 0 7px 12px;
|
|
|
+ width: 100%;
|
|
|
+ text-align: left;
|
|
|
+ cursor: pointer;
|
|
|
+ border-radius: 5px;
|
|
|
+ background-color: transparent;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ color: $white;
|
|
|
+ background-color: $musareBlue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ outline: 1px solid black;
|
|
|
+ width: 600px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-@media (max-width: 350px) {
|
|
|
- .username {
|
|
|
- font-size: 2.9rem;
|
|
|
- word-wrap: break-all;
|
|
|
+.night-mode {
|
|
|
+ .name,
|
|
|
+ .username,
|
|
|
+ .bio-row i,
|
|
|
+ .bio-row p,
|
|
|
+ .date-location-row i,
|
|
|
+ .date-location-row p {
|
|
|
+ color: $light-grey;
|
|
|
}
|
|
|
}
|
|
|
</style>
|