Browse Source

Added progress bars and badges

staging
David Ludwig 4 years ago
parent
commit
ecda53a2f3
6 changed files with 175 additions and 28 deletions
  1. +27
    -4
      services/request/src/app/components/MovieList.vue
  2. +28
    -4
      services/request/src/app/components/MoviePoster.vue
  3. +97
    -0
      services/request/src/app/components/ProgressRing.vue
  4. +11
    -8
      services/request/src/app/views/Search.vue
  5. +11
    -10
      services/request/src/server/services/WebServer/routes/auth.ts
  6. +1
    -2
      services/request/tailwind.config.js

+ 27
- 4
services/request/src/app/components/MovieList.vue View File

@ -1,8 +1,31 @@
<template>
<ul class="w-full grid gap-8 grid-cols-2 sm:grid-cols-4 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-8 self-center ">
<li class="inline-block" v-for="(movie, index) in movies">
<movie-poster class="shadow-md hover:shadow-lg rounded-xl motion-safe:transform hover:scale-105 transition-transform ease-out cursor-pointer"
:src="movie.posterPath ?? undefined" size="w185" @click="$emit('onClickMovie', movie, index)"/>
<ul class="w-full flex flex-col space-y-4">
<li class="flex flex-row bg-white rounded-lg overflow-hidden shadow-md" v-for="(movie, index) in movies"
@click="$emit('onClickMovie', movie, index)">
<movie-poster class="w-3/12 flex-shrink-0" :is-on-plex="movie.plexLink !== null"
:src="movie.posterPath ?? undefined" size="w185"/>
<div class="relative box-border p-4 flex flex-col justify-center w-full">
<div class="flex-grow flex flex-col justify-center">
<h3 class="font-medium text-md sm:text-xl">{{ movie.title }}</h3>
<span v-if="movie.releaseDate" class="opacity-50">{{ movie.releaseDate.slice(0, 4) }}</span>
</div>
<div v-if="movie.plexLink" class="">
<img src="../assets/plex_logo.svg" alt="Added to Plex" class="bg-ui-plexGray px-3 py-1.5 h-5 rounded-full"/>
</div>
<!-- <div class="poster-decal" v-if="isOnPlex">
<img class="w-full" src="../assets/plex_logo.svg" alt="Watchable now on Plex"/>
</div> -->
<!-- <div v-if="movie.plexLink" class="opacity-75 text-sm" style="background: rgb(31, 35. 38)">Added to Plex</div> -->
<div v-if="!movie.plexLink" class="flex flex-row items-end space-x-2">
<div class="w-full rounded-full bg-gray-200 overflow-hidden flex-grow-0">
<div class="w-1/2 bg-red-500 h-1"></div>
</div>
<div class="relative h-1 flex justify-center items-center text-sm">
<span class="h-0 overflow-hidden">100%</span>
<span class="absolute left-0 text-center w-full opacity-75">{{ 50 }}%</span>
</div>
</div>
</div>
</li>
</ul>
</template>


+ 28
- 4
services/request/src/app/components/MoviePoster.vue View File

@ -5,17 +5,28 @@
:src="`/api/tmdb/image/${size}${src}`" @load="onPosterLoad">
<div v-else class="poster-content bg-gray-300 text-gray-500">
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="image" class="svg-inline--fa fa-image fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56zM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48z"></path></svg>
<!-- <img v-else class="w-full h-full object-cover" loading="lazy" :ref="`poster-${index}`"
:src="`/api/tmdb/image/w185${movie.poster_path}`"> -->
</div>
<!-- <div class="poster-decal" v-if="isOnPlex">
<img class="w-full" src="../assets/plex_logo.svg" alt="Watchable now on Plex"/>
</div> -->
<!-- <div class="absolute inset-0 z-20 flex items-end justify-center p-4"> -->
<!-- <progress-ring class="w-1/3" fontSize="25" :ringWidth="10" :progress="0.99"/> -->
<!-- <div class="w-full rounded-full bg-gray-100">
<div class="w-1/2 bg-red-500" style="padding-top: 6%"></div>
</div> -->
<!-- </div> -->
</div>
</template>
<script lang="ts">
import ProgressRing from './ProgressRing.vue'
import { defineComponent } from "vue";
export default defineComponent({
components: {
ProgressRing,
},
data() {
return {
isLoaded: false
@ -28,6 +39,10 @@ export default defineComponent({
}
},
props: {
isOnPlex: {
type: Boolean,
default: false
},
size: {
type: String,
default: "w342"
@ -37,15 +52,24 @@ export default defineComponent({
});
</script>
<style lang="postcss">
<style lang="css">
.poster {
@apply relative overflow-hidden;
}
.poster > .poster-padding {
padding-top: 143.885%;
}
.poster > .poster-decal {
@apply absolute z-10 top-0 w-full shadow-sm;
padding: 4%;
background: rgb(31, 35, 38);
transform: rotate(-45deg) translateX(-28%) translateY(-100%);
}
.poster > .poster-decal > img {
@apply w-1/3 mx-auto;
}
.poster > .poster-content {
@apply absolute inset-0 flex justify-center items-center;
@apply absolute inset-0 flex justify-center items-center z-0 shadow-sm;
}
.poster > .poster-content > svg {
width: 35%;


+ 97
- 0
services/request/src/app/components/ProgressRing.vue View File

@ -0,0 +1,97 @@
<template>
<div class="box-border relative text-white radial-progress">
<svg class="transform -rotate-90" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style="height: 100%; width: 100%">
<circle
class="radial-progress-ring__body"
:r="50"
:cx="50"
:cy="50"/>
<circle
class="radial-progress-ring__progress"
:stroke-width="ringWidth"
:r="50 - ringWidth/2"
:cx="50"
:cy="50"
ref="ring"
/>
<text text-anchor="middle" alignment-baseline="central" x="50" y="50" :font-size="fontSize" transform="rotate(90) translate(0,-100)">{{ progressFormatted }}</text>
</svg>
<!-- <div class="antialiased flex absolute inset-0 items-center justify-center text-xs font-light rounded-full text-white">{{ progressFormatted }}%</div> -->
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export enum RadialProgressState {
Downloading,
Paused,
Halted,
Error
}
export default defineComponent({
computed: {
circumference(): number {
return 2*Math.PI*(50 - this.ringWidth/2);
},
progressFormatted(): number {
return Math.floor(100*(<number>this.progress));
}
},
methods: {
updateProgressBar() {
(<HTMLElement>this.$refs.ring).style.strokeDasharray = `${this.circumference} ${this.circumference}`;
(<HTMLElement>this.$refs.ring).style.strokeDashoffset = `${this.circumference - this.circumference*this.progress}`;
}
},
mounted() {
this.updateProgressBar();
},
props: {
fontSize: {
type: Number,
required: true
},
progress: {
type: Number,
required: true
},
ringWidth: {
type: Number,
default: 1
}
},
watch: {
progress() {
this.updateProgressBar();
}
}
});
</script>
<style lang="css">
.radial-progress text {
fill: white;
}
.radial-progress-ring__body {
fill: #1f2937;
}
.radial-progress-ring__progress {
fill: transparent;
stroke: #34D399;
transition: stroke-dashoffset 0.25s;
}
.bg-modal {
background: rgba(0, 0, 0, 0.35);
opacity: 0.0;
transition: opacity 0.25s;
}
.show-state {
opacity: 1.0;
}
.radial-progress:hover .bg-modal {
opacity: 1.0;
}
</style>

+ 11
- 8
services/request/src/app/views/Search.vue View File

@ -1,12 +1,15 @@
<template>
<form @submit.prevent="() => search()">
<div class="flex rounded-xl overflow-hidden shadow-md mb-8">
<input type="text" v-model="searchValue" :placeholder="exampleTitles[Math.floor(exampleTitles.length*Math.random())]"
class="w-full outline-none p-2 bg-white border border-gray-100 text-gray-800 placeholder-gray-400 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50 disabled:border-gray-400">
<button class="py-3 px-6 bg-indigo-500 text-white" type="submit">Search</button>
</div>
</form>
<movie-list :movies="movies" @onClickMovie="displayMovie"/>
<div class="space-y-4">
<form @submit.prevent="() => search()">
<div class="flex rounded-xl overflow-hidden shadow-md">
<input type="text" v-model="searchValue" :placeholder="exampleTitles[Math.floor(exampleTitles.length*Math.random())]"
class="w-full outline-none p-2 bg-white border border-gray-100 text-gray-800 placeholder-gray-400 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50 disabled:border-gray-400">
<button class="py-3 px-6 bg-indigo-500 text-white" type="submit">Search</button>
</div>
</form>
<div v-if="$route.query['query']" class="text-center">{{ movies.length }} result{{ movies.length != 1 ? 's' : ""}} found</div>
<movie-list :movies="movies" @onClickMovie="displayMovie"/>
</div>
<router-view @onClose="onModalClosed"></router-view>
</template>


+ 11
- 10
services/request/src/server/services/WebServer/routes/auth.ts View File

@ -30,17 +30,18 @@ export default function register(factory: RouteRegisterFactory, app: Application
}
let body = { id: user.id, name: user.name, isAdmin: user.isAdmin };
let token = jwt.sign(body, app.APP_KEY, { expiresIn: 60*60*24 });
// Below code requires SSH to store cookies securely
// Store the header/payload in the client, store the signature in a secure httpOnly cookie
if ((<any>request.query)["use_cookies"] || (<any>request.query)["use_cookies"] === undefined) {
let [header, payload, signature] = token.split('.');
token = `${header}.${payload}`;
reply.setCookie("jwt_signature", signature, {
path: '/',
httpOnly: true,
sameSite: true,
secure: true
});
}
// if ((<any>request.query)["use_cookies"] || (<any>request.query)["use_cookies"] === undefined) {
// let [header, payload, signature] = token.split('.');
// token = `${header}.${payload}`;
// reply.setCookie("jwt_signature", signature, {
// path: '/',
// httpOnly: true,
// sameSite: true,
// secure: true
// });
// }
reply.send({ status: "Success", token });
}));


+ 1
- 2
services/request/tailwind.config.js View File

@ -5,8 +5,7 @@ module.exports = {
extend: {
colors: {
ui: {
bg: "#191726",
fg: "#202332",
plexGray: "rgb(31, 35, 38)"
}
},
},


Loading…
Cancel
Save