mirror of
https://github.com/TeamPiped/Piped.git
synced 2026-03-29 20:06:58 +00:00
254 lines
7.9 KiB
Vue
254 lines
7.9 KiB
Vue
<template>
|
|
<div
|
|
class="flex min-h-screen w-full flex-col bg-white px-[1vw] py-5 text-black antialiased dark:bg-dark-900 dark:text-white"
|
|
:class="[theme]"
|
|
>
|
|
<div class="flex-1">
|
|
<NavBar />
|
|
<router-view v-slot="{ Component }">
|
|
<keep-alive :max="5">
|
|
<component :is="Component" :key="$route.fullPath" />
|
|
</keep-alive>
|
|
</router-view>
|
|
</div>
|
|
|
|
<FooterComponent />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
import NavBar from "./components/NavBar.vue";
|
|
import FooterComponent from "./components/FooterComponent.vue";
|
|
import { testLocalStorage, usePreferenceString } from "@/composables/usePreferences.js";
|
|
import { getDefaultLanguage, TimeAgo, TimeAgoConfig } from "@/composables/useFormatting.js";
|
|
import { fetchSubscriptions } from "@/composables/useSubscriptions.js";
|
|
import { getChannelGroups, createOrUpdateChannelGroup } from "@/composables/useChannelGroups.js";
|
|
|
|
const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)");
|
|
|
|
const themePreference = usePreferenceString("theme", "dark");
|
|
const localePreference = usePreferenceString("hl", "en");
|
|
const theme = ref("dark");
|
|
|
|
function setTheme() {
|
|
const themes = {
|
|
dark: "dark",
|
|
light: "light",
|
|
auto: darkModePreference.matches ? "dark" : "light",
|
|
};
|
|
|
|
theme.value = themes[themePreference.value] ?? themes.dark;
|
|
|
|
changeTitleBarColor();
|
|
|
|
const root = document.querySelector(":root");
|
|
theme.value === "dark" ? root.classList.add("dark") : root.classList.remove("dark");
|
|
}
|
|
|
|
function changeTitleBarColor() {
|
|
const currentColor = { dark: "#0F0F0F", light: "#FFF" };
|
|
const themeColor = document.querySelector("meta[name='theme-color']");
|
|
themeColor.setAttribute("content", currentColor[theme.value]);
|
|
}
|
|
|
|
async function applyLocale(locale = localePreference.value) {
|
|
const resolvedLocale = locale || (await getDefaultLanguage());
|
|
|
|
if (resolvedLocale !== TimeAgoConfig.locale) {
|
|
const localeTime = await import(`../node_modules/javascript-time-ago/locale/${resolvedLocale}.json`)
|
|
.catch(() => null)
|
|
.then(module => module?.default);
|
|
if (localeTime) {
|
|
TimeAgo.addLocale(localeTime);
|
|
TimeAgoConfig.locale = resolvedLocale;
|
|
}
|
|
}
|
|
|
|
if (window.i18n.global.locale.value !== resolvedLocale) {
|
|
if (!window.i18n.global.availableLocales.includes(resolvedLocale)) {
|
|
const messages = await import(`./locales/${resolvedLocale}.json`).then(module => module.default);
|
|
window.i18n.global.setLocaleMessage(resolvedLocale, messages);
|
|
}
|
|
window.i18n.global.locale.value = resolvedLocale;
|
|
}
|
|
}
|
|
|
|
function handlePreferredColorSchemeChange() {
|
|
if (themePreference.value === "auto") setTheme();
|
|
}
|
|
|
|
watch(
|
|
themePreference,
|
|
() => {
|
|
setTheme();
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
onMounted(() => {
|
|
darkModePreference.addEventListener("change", handlePreferredColorSchemeChange);
|
|
|
|
if ("indexedDB" in window) {
|
|
const request = indexedDB.open("piped-db", 6);
|
|
request.onupgradeneeded = ev => {
|
|
const db = request.result;
|
|
console.log("Upgrading object store.");
|
|
if (!db.objectStoreNames.contains("watch_history")) {
|
|
const store = db.createObjectStore("watch_history", { keyPath: "videoId" });
|
|
store.createIndex("video_id_idx", "videoId", { unique: true });
|
|
store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
|
|
}
|
|
if (ev.oldVersion < 2) {
|
|
const store = request.transaction.objectStore("watch_history");
|
|
store.createIndex("watchedAt", "watchedAt", { unique: false });
|
|
}
|
|
if (!db.objectStoreNames.contains("playlist_bookmarks")) {
|
|
const store = db.createObjectStore("playlist_bookmarks", { keyPath: "playlistId" });
|
|
store.createIndex("playlist_id_idx", "playlistId", { unique: true });
|
|
store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
|
|
}
|
|
if (!db.objectStoreNames.contains("channel_groups")) {
|
|
const store = db.createObjectStore("channel_groups", { keyPath: "groupName" });
|
|
store.createIndex("groupName", "groupName", { unique: true });
|
|
}
|
|
if (!db.objectStoreNames.contains("playlists")) {
|
|
const playlistStore = db.createObjectStore("playlists", { keyPath: "playlistId" });
|
|
playlistStore.createIndex("playlistId", "playlistId", { unique: true });
|
|
const playlistVideosStore = db.createObjectStore("playlist_videos", { keyPath: "videoId" });
|
|
playlistVideosStore.createIndex("videoId", "videoId", { unique: true });
|
|
}
|
|
// migration to fix an invalid previous length of channel ids: 11 -> 24
|
|
(async () => {
|
|
if (ev.oldVersion < 6) {
|
|
const subscriptions = await fetchSubscriptions();
|
|
const channelGroups = await getChannelGroups();
|
|
for (let group of channelGroups) {
|
|
for (let i = 0; i < group.channels.length; i++) {
|
|
const tooShortChannelId = group.channels[i];
|
|
const foundChannel = subscriptions.find(
|
|
channel => channel.url.substr(-11) == tooShortChannelId,
|
|
);
|
|
if (foundChannel) group.channels[i] = foundChannel.url.substr(-24);
|
|
}
|
|
createOrUpdateChannelGroup(group);
|
|
}
|
|
}
|
|
})();
|
|
};
|
|
request.onsuccess = e => {
|
|
window.db = e.target.result;
|
|
};
|
|
} else console.log("This browser doesn't support IndexedDB");
|
|
|
|
(async function () {
|
|
const initialLocale =
|
|
testLocalStorage() && localStorage.getItem("hl") === null
|
|
? await getDefaultLanguage()
|
|
: localePreference.value;
|
|
await applyLocale(initialLocale);
|
|
|
|
watch(localePreference, locale => {
|
|
applyLocale(locale);
|
|
});
|
|
})();
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
darkModePreference.removeEventListener("change", handlePreferredColorSchemeChange);
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
@reference "./app.css";
|
|
|
|
@layer base {
|
|
h1,
|
|
p,
|
|
a,
|
|
b {
|
|
unicode-bidi: plaintext;
|
|
text-align: start;
|
|
}
|
|
|
|
:root {
|
|
color-scheme: only light;
|
|
scrollbar-color: #4b4f52 #d1d5db;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
background-color: #d1d5db;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background-color: #4b4f52;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background-color: #5b6469;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:active {
|
|
background-color: #485053;
|
|
}
|
|
|
|
::-webkit-scrollbar-corner {
|
|
background-color: #0b0e0f;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar {
|
|
background-color: #15191a;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-thumb {
|
|
background-color: #4b4f52;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-thumb:hover {
|
|
background-color: #5b6469;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-thumb:active {
|
|
background-color: #485053;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-corner {
|
|
background-color: #0b0e0f;
|
|
}
|
|
|
|
:root.dark {
|
|
scrollbar-color: #4b4f52 #15191a;
|
|
}
|
|
|
|
* {
|
|
font-family: var(--font-sans);
|
|
}
|
|
|
|
hr {
|
|
margin-top: 0.5rem !important;
|
|
margin-bottom: 0.75rem !important;
|
|
border-color: #d1d5db;
|
|
}
|
|
|
|
.dark hr {
|
|
border-color: var(--color-dark-100);
|
|
}
|
|
|
|
h1,
|
|
h2 {
|
|
margin: 0;
|
|
font-weight: 700;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 3rem !important;
|
|
line-height: 1;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.875rem !important;
|
|
line-height: 2.25rem;
|
|
}
|
|
}
|
|
</style>
|