mirror of
https://github.com/TeamPiped/Piped.git
synced 2026-03-29 11:56:58 +00:00
implement fixes for review
This commit is contained in:
@@ -3,12 +3,12 @@
|
|||||||
<div class="flex min-w-[50vw] flex-col">
|
<div class="flex min-w-[50vw] flex-col">
|
||||||
<div class="h-[70vh] overflow-y-scroll pr-4">
|
<div class="h-[70vh] overflow-y-scroll pr-4">
|
||||||
<span v-t="'actions.add_to_group'" class="mb-3 inline-block w-max text-2xl" />
|
<span v-t="'actions.add_to_group'" class="mb-3 inline-block w-max text-2xl" />
|
||||||
<div v-for="(group, index) in channelGroups" :key="group.groupName" class="px-1">
|
<div v-for="group in channelGroups" :key="group.groupName" class="px-1">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span>{{ group.groupName }}</span>
|
<span>{{ group.groupName }}</span>
|
||||||
<UiCheckbox
|
<UiCheckbox
|
||||||
:model-value="group.channels.includes(channelId)"
|
:model-value="group.channels.includes(channelId)"
|
||||||
@update:model-value="onCheckedChange(index, group)"
|
@update:model-value="onCheckedChange(group)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<hr class="h-1 w-full" />
|
<hr class="h-1 w-full" />
|
||||||
@@ -56,9 +56,12 @@ onMounted(() => {
|
|||||||
loadChannelGroups();
|
loadChannelGroups();
|
||||||
});
|
});
|
||||||
|
|
||||||
function onCheckedChange(index, group) {
|
function onCheckedChange(group) {
|
||||||
if (group.channels.includes(props.channelId)) {
|
if (group.channels.includes(props.channelId)) {
|
||||||
group.channels.splice(index, 1);
|
const channelIndex = group.channels.indexOf(props.channelId);
|
||||||
|
if (channelIndex !== -1) {
|
||||||
|
group.channels.splice(channelIndex, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
group.channels.push(props.channelId);
|
group.channels.push(props.channelId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
<a
|
<a
|
||||||
:href="getRssUrl"
|
:href="getRssUrl"
|
||||||
class="inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
class="inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
||||||
|
:aria-label="$t('actions.rss_feed')"
|
||||||
>
|
>
|
||||||
<i-fa6-solid-rss />
|
<i-fa6-solid-rss />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
<a
|
<a
|
||||||
class="mr-1 inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
class="mr-1 inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
||||||
:href="getRssUrl"
|
:href="getRssUrl"
|
||||||
|
:aria-label="$t('actions.playlist_rss_feed')"
|
||||||
>
|
>
|
||||||
<i-fa6-solid-rss />
|
<i-fa6-solid-rss />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
<div class="flex flex-wrap justify-between">
|
<div class="flex flex-wrap justify-between">
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<!-- import json/csv -->
|
<!-- import json/csv -->
|
||||||
<button
|
<router-link
|
||||||
|
v-t="'actions.import_from_json_csv'"
|
||||||
|
to="/import"
|
||||||
class="inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
class="inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
||||||
>
|
/>
|
||||||
<router-link v-t="'actions.import_from_json_csv'" to="/import" />
|
|
||||||
</button>
|
|
||||||
<!-- export to json -->
|
<!-- export to json -->
|
||||||
<button
|
<button
|
||||||
v-t="'actions.export_to_json'"
|
v-t="'actions.export_to_json'"
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
<button
|
<button
|
||||||
:type="type"
|
:type="type"
|
||||||
:class="[
|
:class="[
|
||||||
'inline-block w-auto cursor-pointer rounded-sm py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:text-gray-400 dark:hover:bg-dark-300',
|
'inline-block w-auto cursor-pointer rounded-sm py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-sm focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:text-gray-400 dark:hover:bg-dark-300',
|
||||||
variant === 'primary' ? 'bg-gray-300 dark:bg-dark-400' : 'bg-gray-300 dark:bg-dark-400',
|
variant === 'primary'
|
||||||
|
? 'bg-gray-300 dark:bg-dark-400'
|
||||||
|
: 'border border-gray-500 bg-transparent dark:border-gray-400',
|
||||||
customClass,
|
customClass,
|
||||||
]"
|
]"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
|
|||||||
@@ -1,25 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<CheckboxRoot
|
<CheckboxRoot
|
||||||
v-if="hasModelValue"
|
|
||||||
:id="id"
|
:id="id"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:value="value"
|
|
||||||
:disabled="disabled"
|
|
||||||
:class="[
|
|
||||||
'inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-gray-500 bg-gray-300 text-white outline-none focus:shadow-red-400 focus:outline-2 focus:outline-red-500 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-red-500 data-[state=checked]:bg-red-500 dark:border-gray-400 dark:bg-dark-400 dark:data-[state=checked]:border-red-400 dark:data-[state=checked]:bg-red-400',
|
|
||||||
customClass,
|
|
||||||
]"
|
|
||||||
@update:model-value="handleUpdate"
|
|
||||||
>
|
|
||||||
<CheckboxIndicator class="inline-flex items-center justify-center text-current">
|
|
||||||
<i-fa6-solid-check class="size-3" />
|
|
||||||
</CheckboxIndicator>
|
|
||||||
</CheckboxRoot>
|
|
||||||
|
|
||||||
<CheckboxRoot
|
|
||||||
v-else
|
|
||||||
:id="id"
|
|
||||||
:value="value"
|
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="[
|
:class="[
|
||||||
'inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-gray-500 bg-gray-300 text-white outline-none focus:shadow-red-400 focus:outline-2 focus:outline-red-500 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-red-500 data-[state=checked]:bg-red-500 dark:border-gray-400 dark:bg-dark-400 dark:data-[state=checked]:border-red-400 dark:data-[state=checked]:bg-red-400',
|
'inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-gray-500 bg-gray-300 text-white outline-none focus:shadow-red-400 focus:outline-2 focus:outline-red-500 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-red-500 data-[state=checked]:bg-red-500 dark:border-gray-400 dark:bg-dark-400 dark:data-[state=checked]:border-red-400 dark:data-[state=checked]:bg-red-400',
|
||||||
@@ -34,25 +16,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from "vue";
|
|
||||||
import { CheckboxRoot, CheckboxIndicator } from "reka-ui";
|
import { CheckboxRoot, CheckboxIndicator } from "reka-ui";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UiCheckbox",
|
name: "UiCheckbox",
|
||||||
});
|
});
|
||||||
|
|
||||||
const props = defineProps({
|
defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [Boolean, String, Number, Array],
|
type: Boolean,
|
||||||
default: undefined,
|
default: false,
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: [Boolean, String, Number],
|
|
||||||
default: true,
|
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -66,10 +43,9 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(["update:modelValue", "change"]);
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
|
|
||||||
const hasModelValue = computed(() => props.modelValue !== undefined);
|
|
||||||
|
|
||||||
function handleUpdate(value) {
|
function handleUpdate(value) {
|
||||||
emit("update:modelValue", value);
|
const nextValue = value === true;
|
||||||
emit("change", value);
|
emit("update:modelValue", nextValue);
|
||||||
|
emit("change", nextValue);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="absolute right-3 size-4 cursor-pointer rounded-full bg-gray-300 text-center text-[10px] text-black opacity-50 hover:opacity-70 dark:bg-gray-400"
|
class="absolute right-3 size-4 cursor-pointer rounded-full bg-gray-300 text-center text-[10px] text-black opacity-50 hover:opacity-70 dark:bg-gray-400"
|
||||||
:aria-label="$t('actions.clear', { count: 0 }) || 'Clear'"
|
:aria-label="$t('actions.clear')"
|
||||||
@click="$emit('clear')"
|
@click="$emit('clear')"
|
||||||
>
|
>
|
||||||
⨉
|
⨉
|
||||||
@@ -10,8 +10,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
defineEmits(["clear"]);
|
defineEmits(["clear"]);
|
||||||
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
const { t: $t } = useI18n();
|
const { t: $t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="ml-2 inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
class="ml-2 inline-block w-auto cursor-pointer rounded-sm bg-gray-300 py-2 text-gray-600 hover:bg-gray-500 hover:text-white focus:shadow-red-400 focus:outline-2 focus:outline-red-500 max-md:px-2 md:px-4 dark:bg-dark-400 dark:text-gray-400 dark:hover:bg-dark-300"
|
||||||
|
:aria-label="modelValue ? $t('actions.hide_password') : $t('actions.show_password')"
|
||||||
|
:aria-pressed="modelValue"
|
||||||
@click="$emit('update:modelValue', !modelValue)"
|
@click="$emit('update:modelValue', !modelValue)"
|
||||||
>
|
>
|
||||||
<i-fa6-solid-eye v-if="!modelValue" />
|
<i-fa6-solid-eye v-if="!modelValue" />
|
||||||
@@ -10,6 +12,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -18,4 +22,6 @@ defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["update:modelValue"]);
|
defineEmits(["update:modelValue"]);
|
||||||
|
|
||||||
|
const { t: $t } = useI18n();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -32,12 +32,19 @@ function createPreferenceRefForValue(key, valueForTypeInference) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function usePreferenceString(key, defaultVal) {
|
export function usePreferenceString(key, defaultVal) {
|
||||||
return getOrCreatePreferenceRef(key, () =>
|
const preferenceRef = getOrCreatePreferenceRef(key, () =>
|
||||||
useLocalStorage(key, defaultVal ?? null, {
|
useLocalStorage(key, defaultVal ?? null, {
|
||||||
serializer: StorageSerializers.any,
|
serializer: StorageSerializers.any,
|
||||||
writeDefaults: false,
|
writeDefaults: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const queryValue = getQueryPreference(key);
|
||||||
|
if (queryValue !== null && preferenceRef.value !== queryValue) {
|
||||||
|
preferenceRef.value = queryValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferenceRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePreferenceBoolean(key, defaultVal = false) {
|
export function usePreferenceBoolean(key, defaultVal = false) {
|
||||||
@@ -104,14 +111,20 @@ export function getPreferenceBoolean(key, defaultVal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPreferenceString(key, defaultVal) {
|
export function getPreferenceString(key, defaultVal) {
|
||||||
const queryValue = getQueryPreference(key);
|
|
||||||
if (queryValue !== null) return queryValue;
|
|
||||||
|
|
||||||
if (testLocalStorage()) {
|
if (testLocalStorage()) {
|
||||||
const value = usePreferenceString(key, defaultVal).value;
|
const preferenceRef = usePreferenceString(key, defaultVal);
|
||||||
|
const queryValue = getQueryPreference(key);
|
||||||
|
if (queryValue !== null && preferenceRef.value !== queryValue) {
|
||||||
|
preferenceRef.value = queryValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = preferenceRef.value;
|
||||||
return value ?? defaultVal;
|
return value ?? defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryValue = getQueryPreference(key);
|
||||||
|
if (queryValue !== null) return queryValue;
|
||||||
|
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +146,13 @@ export function getPreferenceNumber(key, defaultVal) {
|
|||||||
|
|
||||||
export function getPreferenceJSON(key, defaultVal) {
|
export function getPreferenceJSON(key, defaultVal) {
|
||||||
const queryValue = getQueryPreference(key);
|
const queryValue = getQueryPreference(key);
|
||||||
if (queryValue !== null) return JSON.parse(queryValue);
|
if (queryValue !== null) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(queryValue);
|
||||||
|
} catch {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (testLocalStorage()) {
|
if (testLocalStorage()) {
|
||||||
const value = usePreferenceJSON(key, defaultVal).value;
|
const value = usePreferenceJSON(key, defaultVal).value;
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
"search": "Search (Ctrl+K)",
|
"search": "Search (Ctrl+K)",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"loading": "Loading…",
|
"loading": "Loading…",
|
||||||
|
"clear": "Clear",
|
||||||
"clear_history": "Clear History",
|
"clear_history": "Clear History",
|
||||||
"hide_replies": "Hide Replies",
|
"hide_replies": "Hide Replies",
|
||||||
"load_more_replies": "Load more Replies",
|
"load_more_replies": "Load more Replies",
|
||||||
@@ -172,7 +173,11 @@
|
|||||||
"export": "Export",
|
"export": "Export",
|
||||||
"never": "Never",
|
"never": "Never",
|
||||||
"playlists_only": "Playlists only",
|
"playlists_only": "Playlists only",
|
||||||
"always": "Always"
|
"always": "Always",
|
||||||
|
"show_password": "Show password",
|
||||||
|
"hide_password": "Hide password",
|
||||||
|
"rss_feed": "RSS feed",
|
||||||
|
"playlist_rss_feed": "Playlist RSS feed"
|
||||||
},
|
},
|
||||||
"comment": {
|
"comment": {
|
||||||
"pinned_by": "Pinned by {author}",
|
"pinned_by": "Pinned by {author}",
|
||||||
|
|||||||
Reference in New Issue
Block a user