-
-
-
-
![]()
-
-
- {{title}}
-
-
-
- {{title}}
-
-
-
-
-
+
+
+
+
![]()
+
+
+ {{ title }}
+
+
+
+ {{ title }}
+
+
+
+
+
-
-
\ No newline at end of file
+
diff --git a/src/plugins/Toast/index.js b/src/plugins/Toast/index.js
deleted file mode 100644
index 6bf8f19..0000000
--- a/src/plugins/Toast/index.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import Vue from 'vue'
-import ToastComponent from './ToastComponent.vue'
-
-const optionsDefaults = {
- data: {
- type: 'info',
- show: true,
- timeout: 3000,
-
- onCreate(created = null) {
- },
- onEdit(editted = null) {
- },
- onRemove(removed = null) {
- }
- }
-}
-
-function toast(options, router) {
- // merge the default options with the passed options.
- const root = new Vue({
- data: {
- ...optionsDefaults.data,
- ...options,
- router
- },
- render: createElement => createElement(ToastComponent)
- })
-
- root.$mount(document.body.appendChild(document.createElement('div')))
-}
-
-
-export default {
- install(vue, opts) {
- console.log('installing toast plugin!')
- console.log('plugin options', opts)
-
- Vue.prototype.$notifications = {
- info(options) {
- toast({ type: 'info', ...options })
- },
- success(options) {
- toast({ type: 'success', ...options })
- },
- warning(options) {
- toast({ type: 'warning', ...options })
- },
- error(options) {
- toast({ type: 'error', ...options })
- },
- simple(options) {
- toast({ type: 'simple', ...options})
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/plugins/Toast/index.ts b/src/plugins/Toast/index.ts
new file mode 100644
index 0000000..cf5775f
--- /dev/null
+++ b/src/plugins/Toast/index.ts
@@ -0,0 +1,68 @@
+import { createApp } from "vue";
+import router from "../../routes";
+import ToastComponent from "./ToastComponent.vue";
+
+const optionsDefaults = {
+ data: {
+ type: "info",
+ show: true,
+ timeout: 3000,
+
+ onCreate(created = null) {},
+ onEdit(editted = null) {},
+ onRemove(removed = null) {}
+ }
+};
+
+function toast(options) {
+ // merge the default options with the passed options.
+ const toastComponent = createApp(ToastComponent, {
+ ...optionsDefaults.data,
+ ...options
+ });
+
+ toastComponent.use(router);
+
+ console.log("toastComponent:", toastComponent);
+ toastComponent.mount(
+ document.body.appendChild(document.createElement("div"))
+ );
+}
+
+export default {
+ install(app, options) {
+ console.log("installing toast plugin!");
+ console.log("plugin options", options);
+
+ function info(options) {
+ toast({ type: "info", ...options });
+ }
+
+ function success(options) {
+ toast({ type: "success", ...options });
+ }
+
+ function warning(options) {
+ toast({ type: "warning", ...options });
+ }
+
+ function error(options) {
+ toast({ type: "error", ...options });
+ }
+
+ function simple(options) {
+ toast({ type: "simple", ...options });
+ }
+
+ const notifications = { info, success, warning, error, simple };
+ app.config.globalProperties.$notifications = notifications;
+
+ app.provide("notifications", notifications);
+ }
+};
+
+declare module "@vue/runtime-core" {
+ interface ComponentCustomProperties {
+ $notifications: (info: object) => void;
+ }
+}
diff --git a/src/routes.ts b/src/routes.ts
index 4004eaf..45afce8 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -1,5 +1,11 @@
-import Vue from "vue";
-import VueRouter, { NavigationGuardNext, Route, RouteConfig } from "vue-router";
+import { defineAsyncComponent } from "vue";
+import {
+ createRouter,
+ createWebHistory,
+ RouteRecordRaw,
+ NavigationGuardNext,
+ RouteLocationNormalized
+} from "vue-router";
import store from "./store";
declare global {
@@ -8,57 +14,61 @@ declare global {
}
}
-Vue.use(VueRouter);
-
-let routes = [
+let routes: Array
= [
{
name: "home",
path: "/",
- component: resolve => resolve("./pages/Home.vue")
+ component: () => import("./pages/Home.vue")
},
{
name: "activity",
path: "/activity",
meta: { requiresAuth: true },
- component: resolve => resolve("./pages/ActivityPage.vue")
+ component: () => import("./pages/ActivityPage.vue")
},
{
name: "profile",
path: "/profile",
meta: { requiresAuth: true },
- component: resolve => resolve("./pages/ProfilePage.vue")
+ component: () => import("./pages/ProfilePage.vue")
},
{
- name: "list",
+ name: "requests-list",
path: "/list/requests",
- component: resolve => resolve("./pages/RequestPage.vue")
+ component: () => import("./pages/RequestPage.vue")
},
{
name: "list",
path: "/list/:name",
- component: resolve => resolve("./pages/ListPage.vue")
+ component: () => import("./pages/ListPage.vue")
},
{
name: "search",
path: "/search",
- component: resolve => resolve("./pages/SearchPage.vue")
+ component: () => import("./pages/SearchPage.vue")
},
{
name: "register",
path: "/register",
- component: resolve => resolve("./pages/RegisterPage.vue")
+ component: () => import("./pages/RegisterPage.vue")
},
{
name: "settings",
path: "/settings",
meta: { requiresAuth: true },
- component: resolve => resolve("./pages/SettingsPage.vue")
+ component: () => import("./pages/SettingsPage.vue")
},
{
name: "signin",
path: "/signin",
alias: "/login",
- component: resolve => resolve("./pages/SigninPage.vue")
+ component: () => import("./pages/SigninPage.vue")
+ },
+ {
+ name: "torrents",
+ path: "/torrents",
+ meta: { requiresAuth: true },
+ component: () => import("./pages/TorrentsPage.vue")
},
// {
// name: 'user-requests',
@@ -70,21 +80,21 @@ let routes = [
{
name: "404",
path: "/404",
- component: resolve => resolve("./pages/404.vue")
- },
- {
- path: "*",
- redirect: "/"
- },
- {
- path: "/request",
- redirect: "/"
+ component: () => import("./pages/404.vue")
}
+ // {
+ // path: "*",
+ // redirect: "/"
+ // },
+ // {
+ // path: "/request",
+ // redirect: "/"
+ // }
];
-const router = new VueRouter({
- mode: "history",
- base: "/",
+const router = createRouter({
+ history: createWebHistory("/"),
+ // base: "/",
routes,
linkActiveClass: "is-active"
});
@@ -93,35 +103,24 @@ const loggedIn = () => store.getters["user/loggedIn"];
const popupIsOpen = () => store.getters["popup/isOpen"];
const hamburgerIsOpen = () => store.getters["hamburger/isOpen"];
-window.preventPushState = false;
-window.onpopstate = () => (window.preventPushState = true);
+router.beforeEach(
+ (to: RouteLocationNormalized, from: RouteLocationNormalized, next: any) => {
+ store.dispatch("documentTitle/updateTitle", to.name);
+ store.dispatch("popup/resetStateFromUrlQuery", to.query);
-router.beforeEach((to: Route, from: Route, next: NavigationGuardNext) => {
- store.dispatch("documentTitle/updateTitle", to.name);
- const { movie, show, person } = to.query;
+ // Every route change we close hamburger if open
+ if (hamburgerIsOpen()) store.dispatch("hamburger/close");
- if (movie) store.dispatch("popup/open", { id: movie, type: "movie" });
- else if (show) store.dispatch("popup/open", { id: show, type: "show" });
- else if (person) store.dispatch("popup/open", { id: person, type: "person" });
- else store.dispatch("popup/close");
-
- if (hamburgerIsOpen()) store.dispatch("hamburger/close");
-
- // Toggle mobile nav
- if (document.querySelector(".nav__hamburger--active")) {
- document
- .querySelector(".nav__hamburger")
- .classList.remove("nav__hamburger--active");
- document.querySelector(".nav__list").classList.remove("nav__list--active");
- }
-
- if (to.matched.some(record => record.meta.requiresAuth)) {
- if (!loggedIn) {
- next({ path: "/signin" });
+ // If pages has meta 'requiresAuth' and user not logged in
+ // send user to signin page.
+ if (to.matched.some(record => record.meta.requiresAuth)) {
+ if (!loggedIn) {
+ next({ path: "/signin" });
+ }
}
- }
- next();
-});
+ next();
+ }
+);
export default router;
diff --git a/src/store.ts b/src/store.ts
index 426699d..b319ecf 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -1,5 +1,4 @@
-import Vue from "vue";
-import Vuex from "vuex";
+import { createStore } from "vuex";
import darkmodeModule from "./modules/darkmodeModule";
import documentTitle from "./modules/documentTitle";
@@ -8,9 +7,7 @@ import user from "./modules/user";
import popup from "./modules/popup";
import hamburger from "./modules/hamburger";
-Vue.use(Vuex);
-
-const store = new Vuex.Store({
+const store = createStore({
modules: {
darkmodeModule,
documentTitle,
diff --git a/src/utils.ts b/src/utils.ts
index cbfb108..204987d 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,8 +1,8 @@
-export const sortableSize = (string: string) => {
+export const sortableSize = (string: string): number => {
const UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const [numStr, unit] = string.split(" ");
- if (UNITS.indexOf(unit) === -1) return string;
+ if (UNITS.indexOf(unit) === -1) return null;
const exponent = UNITS.indexOf(unit) * 3;
return Number(numStr) * Math.pow(10, exponent);
@@ -36,3 +36,65 @@ export const buildImageProxyUrl = (
return `${proxyHost}${proxySizeOptions}${assetUrl}`;
};
+
+export function focusFirstFormInput(formElement: HTMLFormElement): void {
+ if (!formElement) return;
+
+ const firstInput = formElement?.getElementsByTagName("input")[0];
+ if (!firstInput) return;
+
+ firstInput.focus();
+}
+
+export function focusOnNextElement(elementEvent: KeyboardEvent): void {
+ const { target } = elementEvent;
+ console.log("target:", target);
+ if (!target) return;
+
+ const form = document.getElementsByTagName("form")[0];
+ console.log("form:", form);
+ if (!form) return;
+
+ const inputElements = form.getElementsByTagName("input");
+ console.log("inputElements:", inputElements);
+ const targetIndex = Array.from(inputElements).findIndex(
+ element => element === target
+ );
+ console.log("targetIndex:", targetIndex);
+ if (targetIndex < inputElements.length) {
+ inputElements[targetIndex + 1].focus();
+ }
+}
+
+export function humanMinutes(minutes) {
+ if (minutes instanceof Array) {
+ minutes = minutes[0];
+ }
+
+ const hours = Math.floor(minutes / 60);
+ const minutesLeft = minutes - hours * 60;
+
+ if (minutesLeft == 0) {
+ return hours > 1 ? `${hours} hours` : `${hours} hour`;
+ } else if (hours == 0) {
+ return `${minutesLeft} min`;
+ }
+
+ return `${hours}h ${minutesLeft}m`;
+}
+
+export function getValueFromUrlQuery(queryParameter: string): string | null {
+ const params = new URLSearchParams(window.location.search);
+ return params.get(queryParameter) || null;
+}
+
+export function setUrlQueryParameter(parameter: string, value: string): void {
+ const params = new URLSearchParams();
+ params.append(parameter, value);
+
+ const url = `${window.location.protocol}//${window.location.hostname}${
+ window.location.port ? `:${window.location.port}` : ""
+ }${window.location.pathname}${params.toString().length ? `?${params}` : ""}`;
+
+ window.history.pushState({}, "search", url);
+}
diff --git a/webpack.config.js b/webpack.config.js
index dda7f31..8564ba7 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -7,7 +7,7 @@ const TerserPlugin = require("terser-webpack-plugin");
const sourcePath = path.resolve(__dirname, "src");
const indexFile = path.join(sourcePath, "index.html");
-const javascriptEntry = path.join(sourcePath, "main.js");
+const javascriptEntry = path.join(sourcePath, "main.ts");
const publicPath = path.resolve(__dirname, "public");
const isProd = process.env.NODE_ENV === "production";
@@ -36,9 +36,12 @@ module.exports = {
use: ["vue-loader"]
},
{
- test: /\.tsx?$/,
+ test: /\.ts$/,
loader: "ts-loader",
- exclude: /node_modules/
+ exclude: /node_modules/,
+ options: {
+ appendTsSuffixTo: [/\.vue$/]
+ }
},
{
test: /\.scss$/,
@@ -69,7 +72,7 @@ module.exports = {
resolve: {
extensions: [".js", ".ts", ".vue", ".json", ".scss"],
alias: {
- vue$: "vue/dist/vue.common.js",
+ vue: "@vue/runtime-dom",
"@": path.resolve(__dirname, "src"),
src: path.resolve(__dirname, "src"),
assets: `${publicPath}/assets`,