1
0
mirror of https://github.com/devfake/flox.git synced 2024-11-24 02:52:28 +01:00

modal, display episodes, few other updates

This commit is contained in:
devfake 2016-11-28 08:50:09 +01:00
parent 5cc5802231
commit 643c71469a
27 changed files with 372 additions and 55 deletions

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Episode;
use App\Http\Requests\ImportRequest;
use App\Item;
use App\Services\Storage;
@ -14,25 +15,29 @@
class SettingController {
private $item;
private $episodes;
private $storage;
public function __construct(Item $item, Storage $storage)
public function __construct(Item $item, Episode $episodes, Storage $storage)
{
$this->item = $item;
$this->episodes = $episodes;
$this->storage = $storage;
}
/**
* Save all movies as json file and return an download response.
* Save all movies and series as json file and return an download response.
*
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function export()
{
$items = json_encode($this->item->all());
$data['items'] = $this->item->all();
$data['episodes'] = $this->episodes->all();
$file = 'flox--' . date('Y-m-d---H-i') . '.json';
$this->storage->saveExport($file, $items);
$this->storage->saveExport($file, json_encode($data));
return response()->download(base_path('../public/exports/' . $file));
}
@ -55,10 +60,15 @@
$data = json_decode(file_get_contents($file));
$this->item->truncate();
foreach($data as $item) {
foreach($data->items as $item) {
$this->item->create((array) $item);
$this->storage->createPosterFile($item->poster);
}
$this->episodes->truncate();
foreach($data->episodes as $episode) {
$this->episodes->create((array) $episode);
}
}
/**

View File

@ -19,9 +19,9 @@
return $this->tmdb->search(Input::get('q'));
}
public function suggestions($tmdbID)
public function suggestions($tmdbID, $mediaType)
{
return $this->tmdb->suggestions($tmdbID);
return $this->tmdb->suggestions($mediaType, $tmdbID);
}
public function trending()

View File

@ -7,6 +7,7 @@ import SiteHeader from './components/Header.vue';
import Search from './components/Search.vue';
import SiteFooter from './components/Footer.vue';
import Login from './components/Login.vue';
import Modal from './components/Modal/Index.vue';
import router from './routes';
import store from './store/index';
@ -26,7 +27,7 @@ const App = new Vue({
},
components: {
SiteHeader, Search, SiteFooter, Login
SiteHeader, Search, SiteFooter, Login, Modal
},
methods: {

View File

@ -55,7 +55,9 @@
...mapActions([ 'loadItems', 'loadMoreItems', 'setSearchTitle' ]),
fetchData() {
this.loadItems(this.userFilter);
let name = this.$route.name;
this.loadItems({name, filter: this.userFilter});
this.setSearchTitle('');
},

View File

@ -58,8 +58,9 @@
initSuggestions() {
const tmdbID = this.$route.query.for;
const type = this.$route.query.type;
http(`${config.api}/suggestions/${tmdbID}`).then(value => {
http(`${config.api}/suggestions/${tmdbID}/${type}`).then(value => {
this.items = value.data;
this.SET_LOADING(false);
});

View File

@ -11,12 +11,15 @@
<span :title="lang('change color')" class="icon-constrast" @click="toggleColorScheme()"><i></i></span>
</span>
<nav class="site-nav">
<ul>
<li><router-link to="/trending">{{ lang('trending') }}</router-link></li>
<li><router-link to="/upcoming">{{ lang('upcoming') }}</router-link></li>
</ul>
</nav>
<ul class="site-nav">
<li><router-link to="/trending">{{ lang('trending') }}</router-link></li>
<li><router-link to="/upcoming">{{ lang('upcoming') }}</router-link></li>
</ul>
<ul class="site-nav-second">
<li><router-link to="/tv">{{ lang('tv') }}</router-link></li>
<li><router-link to="/movies">{{ lang('movies') }}</router-link></li>
</ul>
</div>
</header>
@ -62,10 +65,12 @@
this.SET_USER_FILTER(localStorage.getItem('filter'));
},
setUserFilter(type) {
localStorage.setItem('filter', type);
this.SET_USER_FILTER(type);
this.loadItems(type);
setUserFilter(filter) {
let name = this.$route.name;
localStorage.setItem('filter', filter);
this.SET_USER_FILTER(filter);
this.loadItems({name, filter});
}
}
}

View File

@ -0,0 +1,29 @@
<template>
<div class="all-modals">
<season></season>
<span class="overlay" v-if="overlay" @click="CLOSE_MODAL()"></span>
</div>
</template>
<script>
import Season from './Season.vue';
import { mapState, mapMutations } from 'vuex';
export default {
computed: {
...mapState({
overlay: state => state.overlay
})
},
methods: {
...mapMutations([ 'CLOSE_MODAL' ])
},
components: {
Season
}
}
</script>

View File

@ -0,0 +1,55 @@
<template>
<div class="modal-wrap" v-if="modalType == 'season'">
<div class="modal-header">
<span>{{ modalData.title }}</span>
</div>
<div class="season-tabs">
<span @click="SET_SEASON_ACTIVE_MODAL(index)" v-for="(season, index) in episodes" :class="{active: index == seasonActiveModal}">S{{ addZero(index) }}</span>
</div>
<div class="modal-content" v-if="episodes">
<div @click="setSeen(episode)" class="modal-item" v-for="(episode, index) in episodes[seasonActiveModal]">
<span class="modal-episode no-select">E{{ addZero(episode.episode_number) }}</span>
<span class="modal-name">{{ episode.name }}</span>
<span class="episode-seen" :class="{seen: episode.seen}"><i></i></span>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import http from 'axios';
import Helper from '../../helper';
export default {
mixins: [Helper],
methods: {
...mapMutations([ 'SET_SEASON_ACTIVE_MODAL' ]),
setSeen(episode) {
episode.seen = ! episode.seen;
http.patch(`${config.api}/set-seen/${episode.id}`).catch(error => {
episode.seen = ! episode.seen;
});
}
},
computed: {
...mapState({
modalType: state => state.modalType,
modalData: state => state.modalData,
seasonActiveModal: state => state.seasonActiveModal
}),
episodes() {
return this.modalData.episodes;
}
}
}
</script>

View File

@ -20,6 +20,10 @@ export default {
window.requestAnimationFrame(step);
},
addZero(item) {
return ('0' + item).slice(-2);
},
// Language helper
lang(text) {
const language = JSON.parse(config.language);

View File

@ -14,12 +14,14 @@ export default new Router({
mode: 'history',
base: config.uri,
routes: [
{ path: '/', component: Content },
{ path: '/search', component: SearchContent },
{ path: '/settings', component: Settings },
{ path: '/suggestions', component: TMDBContent },
{ path: '/trending', component: TMDBContent },
{ path: '/upcoming', component: TMDBContent },
{ path: '/', component: Content, name: 'home' },
{ path: '/movies', component: Content, name: 'movie' },
{ path: '/tv', component: Content, name: 'tv' },
{ path: '/search', component: SearchContent, name: 'search' },
{ path: '/settings', component: Settings, name: 'settings' },
{ path: '/suggestions', component: TMDBContent, name: 'suggestions' },
{ path: '/trending', component: TMDBContent, name: 'trending' },
{ path: '/upcoming', component: TMDBContent, name: 'upcoming' },
{ path: '*', component: Content }
]
});

View File

@ -1,8 +1,8 @@
import http from 'axios';
export function loadItems({commit}, filter) {
export function loadItems({commit}, response) {
commit('SET_LOADING', true);
http(`${config.api}/items/${filter}`).then(value => {
http(`${config.api}/items/${response.name}/${response.filter}`).then(value => {
const {data, next_page_url} = value.data;
commit('SET_ITEMS', data);
@ -39,4 +39,13 @@ export function setSearchTitle({commit}, title) {
export function setColorScheme({commit}, color) {
localStorage.setItem('color', color);
commit('SET_COLOR_SCHEME', color);
}
export function fetchEpisodes({commit}, data) {
http(`${config.api}/episodes/${data.tmdb_id}`).then(response => {
commit('SET_MODAL_DATA', {
title: data.title,
episodes: response.data
});
});
}

View File

@ -14,7 +14,11 @@ export default new Vuex.Store({
loading: false,
clickedMoreLoading: false,
paginator: null,
colorScheme: ''
colorScheme: '',
overlay: false,
modalData: {},
seasonActiveModal: 1,
modalType: ''
},
mutations,
actions

View File

@ -31,5 +31,27 @@ export default {
[type.SET_COLOR_SCHEME](state, color) {
state.colorScheme = color;
},
[type.CLOSE_MODAL](state) {
state.modalType = false;
state.overlay = false;
state.seasonActiveModal = 1;
document.body.className = document.body.className.replace('open-modal', '');
},
[type.OPEN_MODAL](state, data) {
state.overlay = true;
state.modalType = data.type;
state.modalData = data.data;
document.body.className += ' open-modal';
},
[type.SET_SEASON_ACTIVE_MODAL](state, season) {
state.seasonActiveModal = season;
},
[type.SET_MODAL_DATA](state, data) {
state.modalData = data;
}
}

View File

@ -5,4 +5,8 @@ export const PUSH_TO_ITEMS = 'PUSH_TO_ITEMS';
export const SET_LOADING = 'SET_LOADING';
export const SET_PAGINATOR = 'SET_PAGINATOR';
export const SET_CLICKED_LOADING = 'SET_CLICKED_LOADING';
export const SET_COLOR_SCHEME = 'SET_COLOR_SCHEME';
export const SET_COLOR_SCHEME = 'SET_COLOR_SCHEME';
export const CLOSE_MODAL = 'CLOSE_MODAL';
export const OPEN_MODAL = 'OPEN_MODAL';
export const SET_SEASON_ACTIVE_MODAL = 'SET_SEASON_ACTIVE_MODAL';
export const SET_MODAL_DATA = 'SET_MODAL_DATA';

View File

@ -24,6 +24,7 @@
@if(Request::is('login'))
<login></login>
@else
<modal></modal>
<site-header></site-header>
<search></search>
<router-view></router-view>

View File

@ -7,13 +7,15 @@
"suggestions": "Vorschläge",
"delete movie": "Löschen",
"confirm delete": "Wirklich löschen?",
"search": "Film suchen",
"search or add": "Film suchen oder hinzufügen",
"search": "Suchen",
"search or add": "Suchen oder hinzufügen",
"load more": "Mehr anzeigen",
"nothing found": "Nichts gefunden",
"trending": "Beliebt",
"upcoming": "Kommend",
"tv": "TV",
"movies": "Filme",
"last seen": "Zuletzt gesehen",
"best rated": "Am besten bewertet",
"change color": "Farbmodus wechseln",
@ -29,11 +31,11 @@
"export button": "Exportieren",
"import button": "Importieren",
"or divider": "ODER",
"update genre": "Genre updaten",
"sync scout": "Sync Laravel Scout",
"update genre": "Genre aktualisieren",
"sync scout": "Synchronisieren Laravel Scout",
"display genre": "Genre anzeigen",
"display date": "Datum anzeigen",
"success import": "Filme wurden erfolgreich importiert",
"import warn": "Durch den Import werden alle Filme ersetzt. Erstelle vorher ein Backup!",
"genre message": "Um eine alte Version von Flox upzudaten"
"genre message": "Um eine alte Version von Flox zu aktualisieren "
}

View File

@ -7,13 +7,15 @@
"suggestions": "Suggestions",
"delete movie": "Delete",
"confirm delete": "Are you sure?",
"search": "Search movie",
"search or add": "Search or add movie",
"search": "Search",
"search or add": "Search or add",
"load more": "Load More",
"nothing found": "Nothing found",
"trending": "Trending",
"upcoming": "Upcoming",
"tv": "TV",
"movies": "Movies",
"last seen": "Last seen",
"best rated": "Best rated",
"change color": "Change colors",

View File

@ -10,6 +10,10 @@ body {
&.dark {
background: #1c1c1c;
}
&.open-modal {
overflow: hidden;
}
}
html {

View File

@ -62,6 +62,10 @@ $rating3: #cd2727;
}
}
.no-select {
user-select: none;
}
.fullsize-loader {
position: absolute;
left: calc(50% - 14px);

View File

@ -10,4 +10,5 @@
'components/search',
'components/content',
'components/login',
'components/footer';
'components/footer',
'components/modal';

View File

@ -7,6 +7,10 @@ main {
.dark & {
background: #1c1c1c;
}
.open-modal & {
padding: 110px 16px 0 0;
}
}
.item-wrap {
@ -41,10 +45,10 @@ main {
.item-new {
display: block;
}
}
.show-episode {
bottom: 24px;
.show-episode {
bottom: 24px;
}
}
.recommend-item,

View File

@ -5,6 +5,10 @@ footer {
background: $main2;
background: linear-gradient(to right, $main1, $main2);
.open-modal & {
padding: 40px 16px 0 0;
}
.dark & {
opacity: .9;
}

View File

@ -11,6 +11,10 @@ header {
.dark & {
opacity: .9;
}
.open-modal & {
padding: 25px 16px 25px 0;
}
}
.logo {
@ -59,11 +63,11 @@ header {
}
.icon-sort-time {
background: url(../../../public/assets/img/sort-time.png);
background: url(../../../public/assets/img/sort-time.png) no-repeat;
}
.icon-sort-star {
background: url(../../../public/assets/img/sort-star.png);
background: url(../../../public/assets/img/sort-star.png) no-repeat;
}
.icon-constrast {
@ -91,23 +95,19 @@ header {
}
}
.site-nav {
float: right;
margin: 7px 60px 0 0;
.site-nav,
.site-nav-second {
float: left;
margin: 7px 0 0 40px;
list-style: none;
padding: 0;
@include media(5) {
@include media(4) {
clear: left;
float: left;
width: 100%;
margin: 20px 0 0 0;
}
ul {
float: left;
list-style: none;
margin: 0;
padding: 0;
}
li {
float: left;
margin: 0 20px 0 0;
@ -136,4 +136,15 @@ header {
font-size: 14px;
}
}
}
.site-nav-second {
float: right;
margin: 7px 60px 0 0;
@include media(4) {
clear: none;
float: left;
margin: 20px 0 0 20px;
}
}

View File

@ -0,0 +1,132 @@
.overlay {
background: rgba(#333, .7);
height: 100vh;
left: 0;
position: fixed;
top: 0;
width: 100%;
z-index: 50;
}
.modal-wrap {
left: 50%;
max-width: 550px;
position: fixed;
top: 10%;
transform: translateX(-50%);
width: 100%;
box-shadow: 0 5px 20px 0 rgba(#000, .6);
z-index: 100;
@include media(4) {
top: 0;
}
}
.modal-header {
background: $main2;
background: linear-gradient(to right, $main1, $main2);
color: #fff;
float: left;
font-size: 20px;
width: 100%;
padding: 10px 15px;
}
.season-tabs {
float: left;
width: 100%;
background: #464646;
span {
float: left;
cursor: pointer;
font-size: 16px;
color: #a9a9a9;
padding: 10px 0;
width: 10%;
text-align: center;
@include media(6) {
width: 20%;
}
@include transition(background);
&.active,
&:hover {
background: #2f2f2f;
}
}
}
.modal-content {
float: left;
width: 100%;
background: #2f2f2f;
max-height: calc(60vh - 100px);
overflow: auto;
@include media(4) {
max-height: calc(100vh - 150px);
}
}
.modal-item {
float: left;
width: 100%;
padding: 10px;
color: #fff;
cursor: pointer;
border-bottom: 1px solid #444;
@include transition(background);
&:hover {
background: darken(#2f2f2f, 5%);
}
&:active {
background: darken(#2f2f2f, 3%);
}
&:last-child {
border: none;
}
}
.modal-episode {
float: left;
width: 35px;
text-align: right;
//color: #a9a9a9;
opacity: .4;
font-size: 15px;
margin: 0 10px 0 0;
}
.modal-name {
float: left;
//color: #d1d1d1;
opacity: .7;
max-width: calc(100% - 100px);
font-size: 15px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.episode-seen {
float: right;
i {
float: left;
width: 22px;
height: 22px;
background: url(../../../public/assets/img/seen.png) no-repeat;
}
&.seen i {
background: url(../../../public/assets/img/seen-active.png) no-repeat;
}
}

View File

@ -10,6 +10,10 @@
background: #2f2f2f;
}
.open-modal & {
padding: 0 16px 0 0;
}
&.sticky {
@include media(sticky) {
position: fixed;

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
public/assets/img/seen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B