Kaynağa Gözat

Added component for confirmation of actions

Owen Diffey 3 yıl önce
ebeveyn
işleme
9f2b0ff390

+ 25 - 0
frontend/src/components/Confirm.vue

@@ -0,0 +1,25 @@
+<template>
+	<tippy
+		interactive="true"
+		placement="top"
+		theme="confirm"
+		ref="confirm"
+		trigger="click"
+		@click.shift="confirm()"
+	>
+		<template #trigger @click.shift="confirm()">
+			<slot />
+		</template>
+		<a @click.prevent="confirm()"> Confirm </a>
+	</tippy>
+</template>
+
+<script>
+export default {
+	methods: {
+		confirm() {
+			this.$emit("confirm");
+		}
+	}
+};
+</script>

+ 3 - 0
frontend/src/components/SongItem.vue

@@ -276,6 +276,9 @@ export default {
 				font-size: 17px;
 				margin-bottom: 5px;
 			}
+			.verified-song {
+				margin-left: 5px;
+			}
 		}
 
 		.song-request-time {

+ 29 - 59
frontend/src/components/modals/EditPlaylist.vue

@@ -266,35 +266,25 @@
 												v-tippy
 												>queue</i
 											>
-											<tippy
+											<confirm
 												v-if="
 													userId ===
 														playlist.createdBy ||
 														isEditable()
 												"
-												interactive="true"
-												placement="top"
-												theme="confirm"
-												trigger="click"
+												@confirm="
+													removeSongFromPlaylist(
+														song.songId
+													)
+												"
 											>
-												<template #trigger>
-													<i
-														class="material-icons delete-icon"
-														content="Remove Song from Playlist"
-														v-tippy
-														>delete_forever</i
-													>
-												</template>
-												<a
-													@click="
-														removeSongFromPlaylist(
-															song.songId
-														)
-													"
-												>
-													Confirm</a
+												<i
+													class="material-icons delete-icon"
+													content="Remove Song from Playlist"
+													v-tippy
+													>delete_forever</i
 												>
-											</tippy>
+											</confirm>
 											<i
 												class="material-icons"
 												v-if="isEditable() && index > 0"
@@ -355,46 +345,25 @@
 				Download Playlist
 			</a>
 			<div class="right">
-				<tippy
+				<confirm
 					v-if="playlist.type === 'station'"
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
+					@confirm="clearAndRefillStationPlaylist()"
 				>
-					<template #trigger>
-						<a class="button is-danger">
-							Clear and refill station playlist
-						</a>
-					</template>
-					<a @click="clearAndRefillStationPlaylist()"> Confirm </a>
-				</tippy>
-				<tippy
+					<a class="button is-danger">
+						Clear and refill station playlist
+					</a>
+				</confirm>
+				<confirm
 					v-if="playlist.type === 'genre'"
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
-				>
-					<template #trigger>
-						<a class="button is-danger">
-							Clear and refill genre playlist
-						</a>
-					</template>
-					<a @click="clearAndRefillGenrePlaylist()"> Confirm </a>
-				</tippy>
-				<tippy
-					v-if="isEditable()"
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
+					@confirm="clearAndRefillGenrePlaylist()"
 				>
-					<template #trigger>
-						<a class="button is-danger"> Remove Playlist </a>
-					</template>
-					<a @click="removePlaylist()"> Confirm </a>
-				</tippy>
+					<a class="button is-danger">
+						Clear and refill genre playlist
+					</a>
+				</confirm>
+				<confirm v-if="isEditable()" @confirm="removePlaylist()">
+					<a class="button is-danger"> Remove Playlist </a>
+				</confirm>
 			</div>
 		</div>
 	</modal>
@@ -408,6 +377,7 @@ import Toast from "toasters";
 import SearchYoutube from "@/mixins/SearchYoutube.vue";
 
 import validation from "@/validation";
+import Confirm from "@/components/Confirm.vue";
 import Modal from "../Modal.vue";
 import SearchQueryItem from "../SearchQueryItem.vue";
 import SongItem from "../SongItem.vue";
@@ -415,7 +385,7 @@ import SongItem from "../SongItem.vue";
 import utils from "../../../js/utils";
 
 export default {
-	components: { Modal, draggable, SearchQueryItem, SongItem },
+	components: { Modal, draggable, Confirm, SearchQueryItem, SongItem },
 	mixins: [SearchYoutube],
 	data() {
 		return {

+ 11 - 24
frontend/src/components/modals/EditStation.vue

@@ -473,31 +473,17 @@
 		<template #footer>
 			<save-button ref="saveButton" @clicked="saveChanges()" />
 			<div class="right">
-				<tippy
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
-				>
-					<template #trigger>
-						<a class="button is-danger">
-							Clear and refill station queue
-						</a>
-					</template>
-					<a @click="clearAndRefillStationQueue()"> Confirm </a>
-				</tippy>
-				<tippy
+				<confirm @confirm="clearAndRefillStationQueue()">
+					<a class="button is-danger">
+						Clear and refill station queue
+					</a>
+				</confirm>
+				<confirm
 					v-if="station && station.type === 'community'"
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
+					@confirm="deleteStation()"
 				>
-					<template #trigger>
-						<button class="button is-danger">Delete station</button>
-					</template>
-					<a @click="deleteStation()"> Confirm </a>
-				</tippy>
+					<button class="button is-danger">Delete station</button>
+				</confirm>
 			</div>
 		</template>
 	</modal>
@@ -509,11 +495,12 @@ import { mapState, mapGetters, mapActions } from "vuex";
 import Toast from "toasters";
 
 import validation from "@/validation";
+import Confirm from "@/components/Confirm.vue";
 import Modal from "../Modal.vue";
 import SaveButton from "../SaveButton.vue";
 
 export default {
-	components: { Modal, SaveButton },
+	components: { Modal, Confirm, SaveButton },
 	props: {
 		stationId: { type: String, default: "" },
 		sector: { type: String, default: "admin" }

+ 6 - 13
frontend/src/pages/Admin/tabs/News.vue

@@ -31,19 +31,9 @@
 							>
 								Edit
 							</button>
-							<tippy
-								interactive="true"
-								placement="top"
-								theme="confirm"
-								trigger="click"
-							>
-								<template #trigger>
-									<button class="button is-danger">
-										Remove
-									</button>
-								</template>
-								<a @click="remove(news)"> Confirm</a>
-							</tippy>
+							<confirm @confirm="remove(news)">
+								<button class="button is-danger">Remove</button>
+							</confirm>
 						</td>
 					</tr>
 				</tbody>
@@ -227,8 +217,11 @@ import { mapActions, mapState, mapGetters } from "vuex";
 import Toast from "toasters";
 import ws from "@/ws";
 
+import Confirm from "@/components/Confirm.vue";
+
 export default {
 	components: {
+		Confirm,
 		EditNews: () => import("@/components/modals/EditNews.vue")
 	},
 	data() {

+ 6 - 12
frontend/src/pages/Admin/tabs/Stations.vue

@@ -61,17 +61,9 @@
 							<a class="button is-info" @click="edit(station)"
 								>Edit</a
 							>
-							<tippy
-								interactive="true"
-								placement="top"
-								theme="confirm"
-								trigger="click"
-							>
-								<template #trigger>
-									<a class="button is-danger">Remove</a>
-								</template>
-								<a @click="removeStation(index)"> Confirm</a>
-							</tippy>
+							<confirm @confirm="removeStation(index)">
+								<a class="button is-danger">Remove</a>
+							</confirm>
 						</td>
 					</tr>
 				</tbody>
@@ -198,12 +190,14 @@ import { mapState, mapActions, mapGetters } from "vuex";
 
 import Toast from "toasters";
 import UserIdToUsername from "@/components/UserIdToUsername.vue";
+import Confirm from "@/components/Confirm.vue";
 import ws from "@/ws";
 
 export default {
 	components: {
 		EditStation: () => import("@/components/modals/EditStation.vue"),
-		UserIdToUsername
+		UserIdToUsername,
+		Confirm
 	},
 	data() {
 		return {

+ 12 - 20
frontend/src/pages/Admin/tabs/UnverifiedSongs.vue

@@ -102,25 +102,15 @@
 							>
 								<i class="material-icons">check_circle</i>
 							</button>
-							<tippy
-								interactive="true"
-								placement="top"
-								theme="confirm"
-								trigger="click"
-							>
-								<template #trigger>
-									<button
-										class="button is-danger"
-										content="Hide Song"
-										v-tippy
-									>
-										<i class="material-icons"
-											>visibility_off</i
-										>
-									</button>
-								</template>
-								<a @click="hide(song._id, index)"> Confirm</a>
-							</tippy>
+							<confirm @confirm="hide(song._id, index)">
+								<button
+									class="button is-danger"
+									content="Hide Song"
+									v-tippy
+								>
+									<i class="material-icons">visibility_off</i>
+								</button>
+							</confirm>
 						</td>
 					</tr>
 				</tbody>
@@ -198,6 +188,7 @@ import Toast from "toasters";
 
 import UserIdToUsername from "@/components/UserIdToUsername.vue";
 import FloatingBox from "@/components/FloatingBox.vue";
+import Confirm from "@/components/Confirm.vue";
 
 import ScrollAndFetchHandler from "@/mixins/ScrollAndFetchHandler.vue";
 
@@ -207,7 +198,8 @@ export default {
 	components: {
 		EditSong: () => import("@/components/modals/EditSong.vue"),
 		UserIdToUsername,
-		FloatingBox
+		FloatingBox,
+		Confirm
 	},
 	mixins: [ScrollAndFetchHandler],
 	data() {

+ 12 - 18
frontend/src/pages/Admin/tabs/VerifiedSongs.vue

@@ -123,23 +123,15 @@
 							>
 								<i class="material-icons">edit</i>
 							</button>
-							<tippy
-								interactive="true"
-								placement="top"
-								theme="confirm"
-								trigger="click"
-							>
-								<template #trigger>
-									<button
-										class="button is-danger"
-										content="Unverify Song"
-										v-tippy
-									>
-										<i class="material-icons">cancel</i>
-									</button>
-								</template>
-								<a @click="remove(song._id, index)"> Confirm</a>
-							</tippy>
+							<confirm @confirm="remove(song._id, index)">
+								<button
+									class="button is-danger"
+									content="Unverify Song"
+									v-tippy
+								>
+									<i class="material-icons">cancel</i>
+								</button>
+							</confirm>
 						</td>
 					</tr>
 				</tbody>
@@ -215,6 +207,7 @@ import Toast from "toasters";
 
 import UserIdToUsername from "@/components/UserIdToUsername.vue";
 import FloatingBox from "@/components/FloatingBox.vue";
+import Confirm from "@/components/Confirm.vue";
 
 import ScrollAndFetchHandler from "@/mixins/ScrollAndFetchHandler.vue";
 
@@ -224,7 +217,8 @@ export default {
 	components: {
 		EditSong: () => import("@/components/modals/EditSong.vue"),
 		UserIdToUsername,
-		FloatingBox
+		FloatingBox,
+		Confirm
 	},
 	mixins: [ScrollAndFetchHandler],
 	data() {

+ 10 - 15
frontend/src/pages/Profile/tabs/RecentActivity.vue

@@ -19,22 +19,16 @@
 					:activity="activity"
 				>
 					<div slot="actions">
-						<tippy
+						<confirm
 							v-if="userId === myUserId"
-							interactive="true"
-							placement="top"
-							theme="confirm"
-							trigger="click"
+							@confirm="hideActivity(activity._id)"
 						>
-							<template #trigger>
-								<a content="Hide Activity" v-tippy>
-									<i class="material-icons hide-icon"
-										>visibility_off</i
-									>
-								</a>
-							</template>
-							<a @click="hideActivity(activity._id)"> Confirm</a>
-						</tippy>
+							<a content="Hide Activity" v-tippy>
+								<i class="material-icons hide-icon"
+									>visibility_off</i
+								>
+							</a>
+						</confirm>
 					</div>
 				</activity-item>
 			</div>
@@ -51,9 +45,10 @@ import Toast from "toasters";
 
 import ActivityItem from "@/components/ActivityItem.vue";
 import ws from "@/ws";
+import Confirm from "@/components/Confirm.vue";
 
 export default {
-	components: { ActivityItem },
+	components: { ActivityItem, Confirm },
 	props: {
 		userId: {
 			type: String,

+ 15 - 30
frontend/src/pages/Settings/tabs/Account.vue

@@ -74,35 +74,19 @@
 		<hr class="section-horizontal-rule" />
 
 		<div class="row">
-			<tippy
-				interactive="true"
-				placement="top"
-				theme="confirm"
-				trigger="click"
-			>
-				<template #trigger>
-					<a class="button is-warning">
-						<i class="material-icons icon-with-button">clear</i>
-						Clear my activities
-					</a>
-				</template>
-				<a @click="removeActivities()"> Confirm</a>
-			</tippy>
-
-			<tippy
-				interactive="true"
-				placement="top"
-				theme="confirm"
-				trigger="click"
-			>
-				<template #trigger>
-					<a class="button is-danger">
-						<i class="material-icons icon-with-button">delete</i>
-						Remove my account
-					</a>
-				</template>
-				<a @click="removeAccount()"> Confirm</a>
-			</tippy>
+			<confirm @confirm="removeActivities()">
+				<a class="button is-warning">
+					<i class="material-icons icon-with-button">clear</i>
+					Clear my activities
+				</a>
+			</confirm>
+
+			<confirm @confirm="removeAccount()">
+				<a class="button is-danger">
+					<i class="material-icons icon-with-button">delete</i>
+					Remove my account
+				</a>
+			</confirm>
 		</div>
 	</div>
 </template>
@@ -114,9 +98,10 @@ import Toast from "toasters";
 import InputHelpBox from "@/components/InputHelpBox.vue";
 import SaveButton from "@/components/SaveButton.vue";
 import validation from "@/validation";
+import Confirm from "@/components/Confirm.vue";
 
 export default {
-	components: { InputHelpBox, SaveButton },
+	components: { InputHelpBox, SaveButton, Confirm },
 	data() {
 		return {
 			validation: {

+ 22 - 49
frontend/src/pages/Settings/tabs/Security.vue

@@ -97,38 +97,18 @@
 			<hr class="section-horizontal-rule" />
 
 			<div class="row">
-				<tippy
-					v-if="isPasswordLinked"
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
-				>
-					<template #trigger>
-						<a class="button is-danger">
-							<i class="material-icons icon-with-button">close</i>
-							Remove password
-						</a>
-					</template>
-					<a @click="unlinkPassword()"> Confirm</a>
-				</tippy>
-				<tippy
-					v-if="isGithubLinked"
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
-				>
-					<template #trigger>
-						<a class="button is-danger">
-							<i class="material-icons icon-with-button"
-								>link_off</i
-							>
-							Remove GitHub from account
-						</a>
-					</template>
-					<a @click="unlinkGitHub()"> Confirm</a>
-				</tippy>
+				<confirm v-if="isPasswordLinked" @confirm="unlinkPassword()">
+					<a class="button is-danger">
+						<i class="material-icons icon-with-button">close</i>
+						Remove password
+					</a>
+				</confirm>
+				<confirm v-if="isGithubLinked" @confirm="unlinkGitHub()">
+					<a class="button is-danger">
+						<i class="material-icons icon-with-button">link_off</i>
+						Remove GitHub from account
+					</a>
+				</confirm>
 			</div>
 
 			<div class="section-margin-bottom" />
@@ -142,22 +122,14 @@
 
 			<hr class="section-horizontal-rule" />
 			<div class="row">
-				<tippy
-					interactive="true"
-					placement="top"
-					theme="confirm"
-					trigger="click"
-				>
-					<template #trigger>
-						<a class="button is-warning">
-							<i class="material-icons icon-with-button"
-								>exit_to_app</i
-							>
-							Logout everywhere
-						</a>
-					</template>
-					<a @click="removeSessions()"> Confirm</a>
-				</tippy>
+				<confirm @confirm="removeSessions()">
+					<a class="button is-warning">
+						<i class="material-icons icon-with-button"
+							>exit_to_app</i
+						>
+						Logout everywhere
+					</a>
+				</confirm>
 			</div>
 		</div>
 	</div>
@@ -169,9 +141,10 @@ import { mapGetters, mapState } from "vuex";
 
 import InputHelpBox from "@/components/InputHelpBox.vue";
 import validation from "@/validation";
+import Confirm from "@/components/Confirm.vue";
 
 export default {
-	components: { InputHelpBox },
+	components: { InputHelpBox, Confirm },
 	data() {
 		return {
 			apiDomain: "",

+ 11 - 19
frontend/src/pages/Station/Sidebar/Queue.vue

@@ -33,26 +33,17 @@
 						class="song-actions"
 						slot="actions"
 					>
-						<tippy
+						<confirm
 							v-if="isOwnerOnly() || isAdminOnly()"
-							interactive="true"
-							placement="top"
-							theme="confirm"
-							trigger="click"
+							@confirm="removeFromQueue(song.songId)"
 						>
-							<template #trigger>
-								<i
-									class="material-icons delete-icon"
-									content="Remove Song from Queue"
-									v-tippy
-									@click.shift="removeFromQueue(song.songId)"
-									>delete_forever</i
-								>
-							</template>
-							<a @click="removeFromQueue(song.songId)">
-								Confirm
-							</a>
-						</tippy>
+							<i
+								class="material-icons delete-icon"
+								content="Remove Song from Queue"
+								v-tippy
+								>delete_forever</i
+							>
+						</confirm>
 						<i
 							class="material-icons"
 							v-if="index > 0"
@@ -141,9 +132,10 @@ import draggable from "vuedraggable";
 import Toast from "toasters";
 
 import SongItem from "@/components/SongItem.vue";
+import Confirm from "@/components/Confirm.vue";
 
 export default {
-	components: { draggable, SongItem },
+	components: { draggable, SongItem, Confirm },
 	data() {
 		return {
 			dismissedWarning: false,