1
0
mirror of https://github.com/devfake/flox.git synced 2024-11-14 22:22:39 +01:00

Feature/reminder (#110)

* move refresh in tab, add job for refresh, add new setting

* check for setting in kernel

* prepare frontend and database for reminders

* prepare mail settings

* add timezone config

* add security-advisories package

* set back APP_URL

* send daily reminder

* update headlines for daily reminder

* fix postgres id sequence on import

* some basic responsive behaviour

* add weekly reminder

* fix footer css

* update readme

* change loading logo
This commit is contained in:
Viktor Geringer 2018-12-27 21:35:41 +01:00 committed by GitHub
parent 4b060cfd3b
commit 23ecc26978
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 3143 additions and 560 deletions

View File

@ -66,7 +66,7 @@ If you go to the settings page of your installation, flox will automatically che
### Queue
To import or update any of your entries you need to have at least one worker running.
To import or refresh any of your entries you need to have at least one worker running.
```bash
# spawn a single worker
@ -112,6 +112,16 @@ The import will download all poster images.
After the import, you can refresh the complete data (add missing episodes, update ratings and more) in the settings page.
### Refresh data
To keep your entries up to date (e.g. ratings, episodes, images) you need to refresh them. In the settings there is the possibility to refresh the data manually or via a cron job (you need the queue worker for this). If you want to refresh only a single entry, there is a button on the subpage of this item.
### Reminders
Flox can send you a daily reminder of episodes or movies coming out today via mail. Or a weekly summary of episodes and movies coming out in the last 7 days. There are options in the settings page for this.
Make sure you tweak the `DATE_FORMAT_PATTERN` config in your `.env` file.
### Translation
All titles are in english by default. You can change your language by setting `TRANSLATION` in `backend/.env`. The most commons are `DE`, `IT`, `FR`, `ES` and `RU`. You can try to use your language code.

View File

@ -7,6 +7,12 @@ LOADING_ITEMS=30
# Default 10 minutes (600 seconds)
PHP_TIME_LIMIT=600
# Set your correct timezone
TIMEZONE=UTC
# Date pattern for reminder mails
DATE_FORMAT_PATTERN=d.m.Y
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
@ -14,6 +20,7 @@ DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
APP_URL=http://localhost
APP_ENV=local
APP_KEY=
APP_DEBUG=true
@ -24,3 +31,10 @@ QUEUE_DRIVER=database
FP_HOST=localhost
FP_PORT=3000
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

View File

@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use App\Services\Reminder;
use Illuminate\Console\Command;
class Daily extends Command {
protected $signature = 'flox:daily';
protected $description = 'Send a daily reminder of released episodes and movies';
private $reminder;
public function __construct(Reminder $reminder)
{
parent::__construct();
$this->reminder = $reminder;
}
public function handle()
{
$this->reminder->sendDaily();
}
}

View File

@ -9,11 +9,6 @@
protected $signature = 'flox:init {database?} {username?} {password?}';
protected $description = 'Create .env file, set the app key and fill database credentials';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$this->createENVFile();

View File

@ -0,0 +1,27 @@
<?php
namespace App\Console\Commands;
use App\Services\Models\ItemService;
use Illuminate\Console\Command;
class Refresh extends Command {
protected $signature = 'flox:refresh';
protected $description = 'Refresh informations for all items';
private $itemService;
public function __construct(ItemService $itemService)
{
parent::__construct();
$this->itemService = $itemService;
}
public function handle()
{
$this->callSilent('queue:clear');
$this->itemService->refreshAll();
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use App\Services\Reminder;
use Illuminate\Console\Command;
class Weekly extends Command {
protected $signature = 'flox:weekly';
protected $description = 'Send a weekly summary of released episodes and movies';
private $reminder;
public function __construct(Reminder $reminder)
{
parent::__construct();
$this->reminder = $reminder;
}
public function handle()
{
$this->reminder->sendWeekly();
}
}

View File

@ -2,8 +2,13 @@
namespace App\Console;
use App\Console\Commands\Daily;
use App\Console\Commands\Refresh;
use App\Console\Commands\Weekly;
use App\Setting;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Schema;
class Kernel extends ConsoleKernel
{
@ -15,6 +20,9 @@ class Kernel extends ConsoleKernel
protected $commands = [
Commands\Init::class,
Commands\DB::class,
Refresh::class,
Daily::class,
Weekly::class,
];
/**
@ -25,7 +33,25 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->call("\App\Services\Models\ItemService@refreshAll")->daily();
if(app()->runningUnitTests()) {
return null;
}
if (Schema::hasTable('settings')) {
$settings = Setting::first();
if ($settings->refresh_automatically) {
$schedule->command(Refresh::class)->dailyAt('06:00');
}
if ($settings->daily_reminder) {
$schedule->command(Daily::class)->dailyAt('07:00');
}
if ($settings->weekly_reminder) {
$schedule->command(Weekly::class)->saturdays()->at('18:00');
}
}
}
/**

View File

@ -7,10 +7,8 @@
use App\AlternativeTitle;
use App\Episode;
use App\Item;
use App\Services\Models\ItemService;
use App\Services\Storage;
use App\Setting;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Input;
use Symfony\Component\HttpFoundation\Response;
@ -20,7 +18,6 @@
private $item;
private $episodes;
private $storage;
private $version;
private $alternativeTitles;
private $settings;
@ -31,7 +28,6 @@
$this->alternativeTitles = $alternativeTitles;
$this->storage = $storage;
$this->settings = $settings;
$this->version = config('app.version');
}
/**
@ -90,30 +86,34 @@
ImportItem::dispatch(json_encode($item));
}
}
logInfo("Import Movies done.");
}
private function importEpisodes($data)
{
logInfo("Import Tv Shows");
if(isset($data->episodes)) {
if(isset($data->episodes)) {
$this->episodes->truncate();
foreach(array_chunk($data->episodes, 50) as $chunk) {
ImportEpisode::dispatch(json_encode($chunk));
}
}
logInfo("Import Tv Shows done.");
}
private function importAlternativeTitles($data)
{
if(isset($data->alternative_titles)) {
$this->alternativeTitles->truncate();
foreach($data->alternative_titles as $title) {
$this->alternativeTitles->create((array) $title);
$title = collect($title)->except('id')->toArray();
$this->alternativeTitles->create($title);
}
}
}
@ -125,7 +125,9 @@
$this->settings->truncate();
foreach($data->settings as $setting) {
$this->settings->create((array) $setting);
$setting = collect($setting)->except('id')->toArray();
$this->settings->create($setting);
}
}
}

View File

@ -60,6 +60,10 @@
'version' => $this->version,
'watchlist' => $settings->show_watchlist_everywhere,
'ratings' => $settings->show_ratings,
'refresh' => $settings->refresh_automatically,
'reminders_send_to' => $settings->reminders_send_to,
'daily' => $settings->daily_reminder,
'weekly' => $settings->weekly_reminder,
];
}
@ -88,4 +92,35 @@
'show_ratings' => Input::get('ratings'),
]);
}
/**
* Update refresh check.
*/
public function updateRefresh()
{
$this->setting->first()->update([
'refresh_automatically' => Input::get('refresh'),
]);
}
/**
* Update reminders mail.
*/
public function updateRemindersSendTo()
{
$this->setting->first()->update([
'reminders_send_to' => Input::get('reminders_send_to'),
]);
}
/**
* Update reminder options.
*/
public function updateReminderOptions()
{
$this->setting->first()->update([
'daily_reminder' => Input::get('daily'),
'weekly_reminder' => Input::get('weekly'),
]);
}
}

View File

@ -38,7 +38,9 @@ class ImportEpisode implements ShouldQueue
foreach($this->episodes as $ep) {
logInfo("Importing episode", [$ep->name]);
try {
$episode->create((array) $ep);
$ep = collect($ep)->except('id')->toArray();
$episode->create($ep);
} catch(\Exception $e) {
logInfo("Failed:", [$e]);
throw $e;

View File

@ -2,9 +2,7 @@
namespace App\Jobs;
use App\Item;
use App\Services\Models\ItemService;
use App\Services\Storage;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;

View File

@ -18,7 +18,7 @@ class UpdateItem implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param $itemId
*/
public function __construct($itemId)
{

View File

@ -0,0 +1,51 @@
<?php
namespace App\Mail;
use App\Services\Storage;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class DailyReminder extends Mailable
{
use Queueable, SerializesModels;
private $episodes;
private $movies;
/**
* Create a new message instance.
*
* @param $episodes
* @param $movies
*/
public function __construct($episodes, $movies)
{
$this->episodes = $episodes;
$this->movies = $movies;
}
/**
* Build the message.
*
* @param Storage $storage
*
* @return $this
*/
public function build(Storage $storage)
{
$lang = collect(json_decode($storage->parseLanguage()));
$headline = $lang['daily reminder'];
$date = date(config('app.DATE_FORMAT_PATTERN'));
return $this->view('mails.compiled.daily')->with([
'headline' => $headline,
'episodesHeadline' => $lang['episodes today'],
'moviesHeadline' => $lang['movies today'],
'episodes' => $this->episodes,
'movies' => $this->movies,
'date' => $date,
])->subject("$headline $date");
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use App\Services\Storage;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class WeeklyReminder extends Mailable
{
use Queueable, SerializesModels;
private $episodes;
private $movies;
private $startWeek;
private $endWeek;
/**
* Create a new message instance.
*
* @param $episodes
* @param $movies
* @param $startWeek
* @param $endWeek
*/
public function __construct($episodes, $movies, $startWeek, $endWeek)
{
$this->episodes = $episodes;
$this->movies = $movies;
$this->startWeek = date(config('app.DATE_FORMAT_PATTERN'), $startWeek);
$this->endWeek = date(config('app.DATE_FORMAT_PATTERN'), $endWeek);
}
/**
* Build the message.
*
* @param Storage $storage
*
* @return $this
*/
public function build(Storage $storage)
{
$lang = collect(json_decode($storage->parseLanguage()));
$headline = $lang['weekly reminder'];
$date = $this->startWeek . ' - ' . $this->endWeek;
return $this->view('mails.compiled.weekly')->with([
'headline' => $headline,
'episodes' => $this->episodes,
'movies' => $this->movies,
'episodesHeadline' => $lang['episodes'],
'moviesHeadline' => $lang['movies'],
'date' => $date,
])->subject("$headline $date");
}
}

View File

@ -356,7 +356,9 @@
$this->storage->downloadImages($item['poster'], $item['backdrop']);
}
Item::create((array) $item);
$item = collect($item)->except('id')->toArray();
Item::create($item);
}
/**

View File

@ -0,0 +1,63 @@
<?php
namespace App\Services;
use App\Episode;
use App\Item;
use App\Mail\DailyReminder;
use App\Mail\WeeklyReminder;
use App\Setting;
use Illuminate\Support\Facades\Mail;
class Reminder {
/**
* Send daily reminder via mail.
*/
public function sendDaily()
{
$settings = Setting::first();
$startToday = today()->startOfDay()->timestamp;
$endToday = today()->endOfDay()->timestamp;
$episodes = Episode::whereBetween('release_episode', [$startToday, $endToday])
->with('item')
->orderBy('tmdb_id')
->get();
$movies = Item::where('media_type', 'movie')
->whereBetween('released', [$startToday, $endToday])
->get();
if (count($episodes) || count($movies)) {
Mail::to($settings->reminders_send_to)
->send(new DailyReminder($episodes, $movies));
}
}
/**
* Send a weekly summary.
*/
public function sendWeekly()
{
$settings = Setting::first();
$startWeek = today()->startOfWeek()->timestamp;
$endWeek = today()->endOfWeek()->timestamp;
$episodes = Episode::whereBetween('release_episode', [$startWeek, $endWeek])
->with('item')
->orderBy('tmdb_id')
->get();
$movies = Item::where('media_type', 'movie')
->whereBetween('released', [$startWeek, $endWeek])
->get();
if (count($episodes) || count($movies)) {
Mail::to($settings->reminders_send_to)
->send(new WeeklyReminder($episodes, $movies, $startWeek, $endWeek));
}
}
}

View File

@ -38,5 +38,8 @@
'show_genre' => 'boolean',
'episode_spoiler_protection' => 'boolean',
'show_watchlist_everywhere' => 'boolean',
'refresh_automatically' => 'boolean',
'daily_reminder' => 'boolean',
'weekly_reminder' => 'boolean',
];
}

View File

@ -19,9 +19,12 @@
"guzzlehttp/guzzle": "^6.3",
"imangazaliev/didom": "^1.13",
"laravel/framework": "5.7.*",
"laravel/tinker": "^1.0"
"laravel/tinker": "^1.0",
"morrislaptop/laravel-queue-clear": "^1.1",
"ext-json": "*"
},
"require-dev": {
"roave/security-advisories": "dev-master",
"filp/whoops": "~2.0",
"fzaninotto/faker": "~1.4",
"laravel/telescope": "^0.1.7",

43
backend/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cf5202abb7de87ccf7e8f79f018d72a2",
"content-hash": "7c42445afb3d31be041791569951f35f",
"packages": [
{
"name": "dnoegel/php-xdg-base-dir",
@ -1278,6 +1278,47 @@
],
"time": "2018-11-05T09:00:11+00:00"
},
{
"name": "morrislaptop/laravel-queue-clear",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/morrislaptop/laravel-queue-clear.git",
"reference": "ea814b360322fcdf2e9397b7a77b1ce4e96423a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/morrislaptop/laravel-queue-clear/zipball/ea814b360322fcdf2e9397b7a77b1ce4e96423a4",
"reference": "ea814b360322fcdf2e9397b7a77b1ce4e96423a4",
"shasum": ""
},
"require": {
"illuminate/support": ">=5.0.0",
"php": ">=5.4.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Morrislaptop\\LaravelQueueClear\\LaravelQueueClearServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Morrislaptop\\LaravelQueueClear\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"authors": [
{
"name": "Craig Morris",
"email": "craig.michael.morris@gmail.com"
}
],
"description": "Command for wiping your queues clear",
"time": "2017-09-04T08:02:44+00:00"
},
{
"name": "nesbot/carbon",
"version": "1.34.1",

View File

@ -9,6 +9,7 @@
'LOADING_ITEMS' => env('LOADING_ITEMS'),
'CLIENT_URI' => env('CLIENT_URI'),
'PHP_TIME_LIMIT' => env('PHP_TIME_LIMIT'),
'DATE_FORMAT_PATTERN' => env('DATE_FORMAT_PATTERN'),
/*
|--------------------------------------------------------------------------
@ -72,7 +73,7 @@
|
*/
'timezone' => 'UTC',
'timezone' => env('TIMEZONE', 'UTC'),
/*
|--------------------------------------------------------------------------

View File

@ -57,7 +57,7 @@ return [
'from' => [
'address' => 'hello@example.com',
'name' => 'Example',
'name' => 'Flox',
],
/*

View File

@ -0,0 +1,17 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddRefreshAutomaticallyField extends Migration
{
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->boolean('refresh_automatically')->default(0);
});
}
public function down() {}
}

View File

@ -0,0 +1,17 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddRemindersSendToField extends Migration
{
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->string('reminders_send_to')->nullable();
});
}
public function down() {}
}

View File

@ -0,0 +1,18 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddDailyAndWeeklyFields extends Migration
{
public function up()
{
Schema::table('settings', function (Blueprint $table) {
$table->boolean('daily_reminder')->default(0);
$table->boolean('weekly_reminder')->default(0);
});
}
public function down() {}
}

View File

@ -0,0 +1,2 @@
<?php

View File

@ -26,6 +26,9 @@
Route::middleware('auth')->group(function() {
Route::get('/check-update', 'SettingController@checkForUpdate');
Route::get('/version', 'SettingController@getVersion');
Route::patch('/settings/refresh', 'SettingController@updateRefresh');
Route::patch('/settings/reminders-send-to', 'SettingController@updateRemindersSendTo');
Route::patch('/settings/reminder-options', 'SettingController@updateReminderOptions');
Route::patch('/settings', 'SettingController@updateSettings');
Route::post('/add', 'ItemController@add');

View File

@ -24,8 +24,6 @@
/** @test */
public function user_can_change_settings()
{
$this->getJson('api/settings');
$oldSettings = Setting::first();
$this->actingAs($this->user)->patchJson('api/settings', [
@ -49,4 +47,52 @@
$this->assertEquals(1, $newSettings->show_watchlist_everywhere);
$this->assertEquals('hover', $newSettings->show_ratings);
}
/** @test */
public function user_can_change_refresh()
{
$oldSettings = Setting::first();
$this->actingAs($this->user)->patchJson('api/settings/refresh', [
'refresh' => 1,
]);
$newSettings = Setting::first();
$this->assertEquals(0, $oldSettings->refresh_automatically);
$this->assertEquals(1, $newSettings->refresh_automatically);
}
/** @test */
public function user_can_change_reminders_send_to()
{
$oldSettings = Setting::first();
$this->actingAs($this->user)->patchJson('api/settings/reminders-send-to', [
'reminders_send_to' => 'jon@snow.io',
]);
$newSettings = Setting::first();
$this->assertNull($oldSettings->reminders_send_to);
$this->assertEquals('jon@snow.io', $newSettings->reminders_send_to);
}
/** @test */
public function user_can_change_reminder_options()
{
$oldSettings = Setting::first();
$this->actingAs($this->user)->patchJson('api/settings/reminder-options', [
'daily' => 1,
'weekly' => 1,
]);
$newSettings = Setting::first();
$this->assertEquals(0, $oldSettings->daily_reminder);
$this->assertEquals(0, $oldSettings->weekly_reminder);
$this->assertEquals(1, $newSettings->daily_reminder);
$this->assertEquals(1, $newSettings->weekly_reminder);
}
}

View File

@ -7,6 +7,8 @@
<span :class="{active: activeTab == 'user'}" @click="changeActiveTab('user')">{{ lang('tab user') }}</span>
<span :class="{active: activeTab == 'options'}" @click="changeActiveTab('options')">{{ lang('tab options') }}</span>
<span :class="{active: activeTab == 'backup'}" @click="changeActiveTab('backup')">{{ lang('tab backup') }}</span>
<span :class="{active: activeTab == 'refresh'}" @click="changeActiveTab('refresh')">{{ lang('refresh') }}</span>
<span :class="{active: activeTab == 'reminders'}" @click="changeActiveTab('reminders')">{{ lang('reminders') }}</span>
</div>
<span class="loader fullsize-loader" v-if="loading"><i></i></span>
@ -15,6 +17,8 @@
<options v-if="activeTab == 'options'"></options>
<backup v-if="activeTab == 'backup'"></backup>
<misc v-if="activeTab == 'misc'"></misc>
<refresh v-if="activeTab == 'refresh'"></refresh>
<reminders v-if="activeTab == 'reminders'"></reminders>
</div>
</main>
@ -25,6 +29,8 @@
import Options from './Options.vue';
import Backup from './Backup.vue';
import Misc from './Misc.vue';
import Refresh from './Refresh.vue';
import Reminders from './Reminders.vue';
import { mapState, mapActions } from 'vuex';
import MiscHelper from '../../../helpers/misc';
@ -37,7 +43,7 @@
},
components: {
User, Options, Backup, Misc
User, Options, Backup, Misc, Refresh, Reminders
},
data() {

View File

@ -10,11 +10,9 @@
<span class="update-check">{{ lang('feedback') }} <a href="https://github.com/devfake/flox/issues" target="_blank">GitHub</a></span>
</div>
<div class="misc-btn-wrap">
<button @click="fetchFiles()" class="setting-btn">{{ lang('call file-parser') }}</button>
<button v-show=" ! refreshAllClicked" @click="refreshAll()" class="setting-btn">{{ lang('refresh all infos') }}</button>
<span v-show="showRefreshAllMessage" class="update-check">{{ lang('refresh all triggered') }}</span>
</div>
<!--<div class="misc-btn-wrap">-->
<!--<button @click="fetchFiles()" class="setting-btn">{{ lang('call file-parser') }}</button>-->
<!--</div>-->
</div>
</template>
@ -37,8 +35,6 @@
return {
version: '',
isUpdate: null,
refreshAllClicked: false,
showRefreshAllMessage: false,
}
},
@ -83,17 +79,6 @@
this.SET_LOADING(false);
this.version = response.data.version;
});
},
refreshAll() {
this.refreshAllClicked = true;
http.patch(`${config.api}/refresh-all`).then(() => {
this.showRefreshAllMessage = true;
}).catch(error => {
this.refreshAllClicked = false;
alert(error.response.data);
});
}
}
}

View File

@ -65,7 +65,7 @@
const data = response.data;
this.SET_LOADING(false);
console.log(data)
this.genre = data.genre;
this.date = data.date;
this.spoiler = data.spoiler;

View File

@ -0,0 +1,80 @@
<template>
<div class="settings-box element-ui-checkbox no-select" v-if=" ! loading">
<div class="setting-box">
<el-checkbox v-model="refresh" @change="updateRefresh">{{ lang('refresh automatically') }}</el-checkbox>
</div>
<div class="misc-btn-wrap">
<button v-show=" ! refreshAllClicked" @click="refreshAll()" class="setting-btn">{{ lang('refresh all infos') }}</button>
<span v-show="showRefreshAllMessage" class="update-check">{{ lang('refresh all triggered') }}</span>
</div>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import MiscHelper from '../../../helpers/misc';
import http from 'axios';
export default {
mixins: [MiscHelper],
created() {
this.fetchOptions();
},
data() {
return {
refresh: false,
refreshAllClicked: false,
showRefreshAllMessage: false,
}
},
computed: {
...mapState({
loading: state => state.loading
})
},
methods: {
...mapMutations([ 'SET_LOADING' ]),
fetchOptions() {
this.SET_LOADING(true);
http(`${config.api}/settings`).then(response => {
this.refresh = response.data.refresh;
this.SET_LOADING(false);
});
},
updateRefresh() {
this.SET_LOADING(true);
http.patch(`${config.api}/settings/refresh`, {refresh: this.refresh}).then(() => {
this.SET_LOADING(false);
}, error => {
alert(error.message);
})
},
refreshAll() {
this.refreshAllClicked = true;
http.patch(`${config.api}/refresh-all`).then(() => {
this.showRefreshAllMessage = true;
}).catch(error => {
this.refreshAllClicked = false;
alert(error.response.data);
});
}
}
}
</script>

View File

@ -0,0 +1,97 @@
<template>
<div class="settings-box element-ui-checkbox no-select" v-if=" ! loading">
<form class="login-form" @submit.prevent="editSetting()">
<span class="update-check">{{ lang('reminders send to') }}</span>
<input type="email" placeholder="E-Mail" v-model="reminders_send_to">
<span class="userdata-changed"><span v-if="success">{{ lang('success message') }}</span></span>
<input type="submit" :value="lang('save button')">
</form>
<div class="setting-box">
<el-checkbox v-model="daily" @change="updateReminders">{{ lang('daily reminder') }}</el-checkbox>
</div>
<div class="setting-box">
<el-checkbox v-model="weekly" @change="updateReminders">{{ lang('weekly reminder') }}</el-checkbox>
</div>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import MiscHelper from '../../../helpers/misc';
import http from 'axios';
import debounce from 'debounce';
const debounceMilliseconds = 2000;
export default {
mixins: [MiscHelper],
created() {
this.fetchUserData();
this.clearSuccessMessage = debounce(this.clearSuccessMessage, debounceMilliseconds);
},
data() {
return {
reminders_send_to: '',
success: false,
daily: false,
weekly: false,
}
},
computed: {
...mapState({
loading: state => state.loading
})
},
methods: {
...mapMutations([ 'SET_LOADING' ]),
fetchUserData() {
this.SET_LOADING(true);
http(`${config.api}/settings`).then(response => {
const data = response.data;
this.reminders_send_to = data.reminders_send_to;
this.daily = data.daily;
this.weekly = data.weekly;
this.SET_LOADING(false);
});
},
editSetting() {
http.patch(`${config.api}/settings/reminders-send-to`, {reminders_send_to: this.reminders_send_to})
.then(() => {
this.success = true;
this.clearSuccessMessage();
});
},
updateReminders() {
this.SET_LOADING(true);
const daily = this.daily;
const weekly = this.weekly;
http.patch(`${config.api}/settings/reminder-options`, {daily, weekly}).then(() => {
this.SET_LOADING(false);
}, error => {
alert(error.message);
})
},
clearSuccessMessage() {
this.success = false;
}
}
}
</script>

View File

@ -1,6 +1,5 @@
<template>
<main>
<!-- todo: make header position absolute, and float teaser and content correct -->
<div class="bigsize-header">
<section class="big-teaser-wrap" :class="{active: itemLoadedSubpage}" v-show=" ! loading">

View File

@ -2,7 +2,7 @@
<footer v-show=" ! loading && ! hideFooter">
<div class="wrap">
<span class="attribution">
<a href="https://www.themoviedb.org/" target="_blank">
<a class="tmdb-logo" href="https://www.themoviedb.org/" target="_blank">
<i class="icon-tmdb"></i>
</a>
This product uses the TMDb API but is not endorsed or certified by TMDb

View File

@ -1,12 +1,14 @@
<template>
<div class="header-wrap" :class="{active: displayHeader, sticky: sticky}">
<div class="header-wrap" :class="{active: displayHeader, sticky: sticky, 'mobile-open': mobileNavigationOpen}">
<header>
<div class="wrap">
<router-link to="/" @click.native="refresh('home')" class="logo">
<img src="../../../public/assets/img/logo.png" alt="Flox" width="108" height="32">
</router-link>
<ul class="site-nav">
<i @click="toggleMobileNavigation()" class="icon-hamburger"></i>
<ul class="site-nav site-nav-first">
<li>
<router-link to="/trending" @click.native="refresh('trending')">{{ lang('trending') }}</router-link>
</li>
@ -44,7 +46,6 @@
<script>
import Search from './Search.vue';
import MiscHelper from '../helpers/misc';
import store from '../store';
import {mapActions, mapState} from 'vuex'
@ -55,7 +56,8 @@
return {
sticky: false,
enableStickyOn: 100,
latestRoute: ''
latestRoute: '',
mobileNavigationOpen: false
}
},
@ -83,7 +85,12 @@
};
},
toggleMobileNavigation() {
this.mobileNavigationOpen = !this.mobileNavigationOpen;
},
refresh(route) {
this.mobileNavigationOpen = false;
let name = this.$route.name;
// Reload only if the page is the same.

1792
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,8 @@
"private": true,
"scripts": {
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"dev": "webpack -w --progress --hide-modules"
"dev": "webpack -w --progress --hide-modules",
"mail": "./node_modules/.bin/mjml ./resources/mails/templates/*.mjml.blade.php -o ./resources/mails/compiled"
},
"dependencies": {
"axios": "^0.17.0",
@ -26,6 +27,7 @@
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"cross-env": "^5.1.1",
"mjml": "^4.2.0",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.5",

View File

@ -20,6 +20,7 @@
"upcoming": "المقبلة",
"now-playing": "Now Playing",
"tv": "تلفاز",
"episodes": "Episodes",
"movies": "أفلام",
"movie": "Movie",
"last seen": "آخر المشاهدة",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "متنوع",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "حفظ",
"password message": "أترك الحقل كلمة المرور فارغا إذا كنت لا ترغب في تغييرها",
"success message": "تم التغيير بنجاح",

View File

@ -20,6 +20,7 @@
"upcoming": "Kommende",
"now-playing": "Now Playing",
"tv": "TV",
"episodes": "Episodes",
"movies": "Movies",
"movie": "Movie",
"last seen": "Sidst set",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Diverse",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Gem",
"password message": "Efterlad dette adgangkodefelt blankt hvis du ikke vil ændre det",
"success message": "Succesfuldt ændret",

View File

@ -20,6 +20,7 @@
"upcoming": "Kommend",
"now-playing": "Aktuell",
"tv": "TV",
"episodes": "Episoden",
"movies": "Filme",
"movie": "Film",
"last seen": "Zuletzt gesehen",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Sonstiges",
"episodes today": "Episoden Heute",
"movies today": "Filme Heute",
"daily reminder": "Tägliche Erinnerung",
"weekly reminder": "Wöchentliche Zusammenfassung",
"reminders send to": "Sende Erinnerungen an",
"reminders": "Erinnerungen",
"refresh": "Aktualisieren",
"refresh automatically": "Infos automatisch aktualisieren",
"save button": "Speichern",
"password message": "Lasse das Passwort-Feld leer, wenn du es nicht ändern willst",
"success message": "Erfolgreich gespeichert",

View File

@ -20,6 +20,7 @@
"upcoming": "Προσεχείς",
"now-playing": "Now Playing",
"tv": "TV",
"episodes": "Episodes",
"movies": "Ταινίες",
"movie": "Movie",
"last seen": "Τελευταία προβολή",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Διάφορα",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Αποθήκευση",
"password message": "Άφησε το πεδίο του κωδικού κενό αν δεν θέλεις να το αλλάξεις",
"success message": "Επιτυχής αλλαγή",

View File

@ -20,6 +20,7 @@
"upcoming": "Upcoming",
"now-playing": "Now Playing",
"tv": "TV",
"episodes": "Episodes",
"movies": "Movies",
"movie": "Movie",
"last seen": "Last seen",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Misc",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Save",
"password message": "Leave the password field blank if you don't want to change it",
"success message": "Successful changed",

View File

@ -19,6 +19,7 @@
"upcoming": "Próximamente",
"now-playing": "Now Playing",
"tv": "TV",
"episodes": "Episodes",
"movies": "Películas",
"movie": "Movie",
"last seen": "Última vista",
@ -34,6 +35,15 @@
"headline user": "Usuario",
"headline export import": "Exportar / Importar",
"headline misc": "Misc",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Guardar",
"password message": "Deja el campo de la contraseña en blanco si no deseas cambiarla",
"success message": "Cambio sastifecho",

View File

@ -20,6 +20,7 @@
"upcoming": "Prochainement",
"now-playing": "Now Playing",
"tv": "Séries TV",
"episodes": "Episodes",
"movies": "Films",
"movie": "Movie",
"last seen": "Dernier film/épisode vu",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Divers",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Sauvegarder",
"password message": "Laisser vide le champ du mot de passe sil ne faut pas le changer",
"success message": "Sauvegarde réussie",

View File

@ -20,6 +20,7 @@
"upcoming": "Aankomende",
"now-playing": "Now Playing",
"tv": "TV",
"episodes": "Episodes",
"movies": "Films",
"movie": "Movie",
"last seen": "Laatst gezien",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Diversen",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Sla op",
"password message": "Laat het wachtwoord vak leeg als je het niet wil veranderen",
"success message": "Successvol veranderd",

View File

@ -20,6 +20,7 @@
"upcoming": "Ожидаемые",
"now-playing": "Now Playing",
"tv": "Сериалы",
"episodes": "Episodes",
"movies": "Фильмы",
"movie": "Movie",
"last seen": "Последние просмотренные",
@ -37,6 +38,14 @@
"tab backup": "Backup",
"tab misc": "Diversen",
"episodes today": "Episoden today",
"movies today": "Movies today",
"daily reminder": "Daily",
"weekly reminder": "Weekly summary",
"reminders send to": "Send reminders to",
"reminders": "Reminders",
"refresh": "Refresh",
"refresh automatically": "Refresh items automatically",
"save button": "Сохранить",
"password message": "Оставьте поле пустым, если не хотите менять его",
"success message": "Успешно изменено",

View File

@ -0,0 +1,322 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<title> Flox </title>
<!--[if !mso]><!-- -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
#outlook a {
padding: 0;
}
.ReadMsgBody {
width: 100%;
}
.ExternalClass {
width: 100%;
}
.ExternalClass * {
line-height: 100%;
}
body {
margin: 0;
padding: 0;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
table,
td {
border-collapse: collapse;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
img {
border: 0;
height: auto;
line-height: 100%;
outline: none;
text-decoration: none;
-ms-interpolation-mode: bicubic;
}
p {
display: block;
margin: 13px 0;
}
</style>
<!--[if !mso]><!-->
<style type="text/css">
@media only screen and (max-width:480px) {
@-ms-viewport {
width: 320px;
}
@viewport {
width: 320px;
}
}
</style>
<!--<![endif]-->
<!--[if mso]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!--[if lte mso 11]>
<style type="text/css">
.outlook-group-fix { width:100% !important; }
</style>
<![endif]-->
<style type="text/css">
@media only screen and (min-width:480px) {
.mj-column-per-100 {
width: 100% !important;
max-width: 100%;
}
}
</style>
<style type="text/css">
@media only screen and (max-width:480px) {
table.full-width-mobile {
width: 100% !important;
}
td.full-width-mobile {
width: auto !important;
}
}
</style>
<style type="text/css">
.link {
color: #895bff;
}
.link-title {
color: #484848;
text-decoration: none;
float: left;
clear: both;
margin: 0 0 20px 0;
}
.link-title:last-child {
margin: 0;
}
.link-title:hover {
color: #f1309a;
}
.link-title span {
color: gray;
}
.headline {
font-size: 14px;
float: left;
text-transform: uppercase;
color: #f1309a;
font-weight: bold;
margin: 0 0 20px 0;
}
</style>
</head>
<body style="background-color:#ebecee;">
<div style="background-color:#ebecee;">
<!--[if mso | IE]>
<table
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
>
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="Margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tr>
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody>
<tr>
<td style="width:108px;">
<a href="{{ url('/') }}" target="_blank">
<img height="32" src="{{ url('/assets/img/logo-login.png') }}" style="border:0;display:block;outline:none;text-decoration:none;height:32px;width:100%;" width="108" />
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</div>
<!--[if mso | IE]>
</td>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tr>
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">
<h2>{{ $headline }}</h2>
<span>{{ $date }}</span>
</div>
</td>
</tr>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<table
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
>
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="background:#ffffff;background-color:#ffffff;Margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"> @if(count($episodes)) <tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:14px;line-height:20px;text-align:left;color:#000000;">
<span class="headline">{{ $episodesHeadline }}</span>
<br> @foreach($episodes as $episode) <a href="{{ url("/tv/{$episode->tmdb_id}") }}" class="link-title" target="_blank">
{{ $episode->item->title }}
<br>
<span>S{{ $episode->season_number }}E{{ $episode->episode_number }}</span>
</a> @endforeach </div>
</td>
</tr> @endif @if(count($movies)) <tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
<span class="headline">{{ $moviesHeadline }}</span>
<br> @foreach($movies as $movie) <a href="{{ url("/movie/{$movie->tmdb_id}") }}" class="link-title" target="_blank">
{{ $movie->title }}
</a> @endforeach </div>
</td>
</tr> @endif </table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<table
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
>
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="Margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:gray;"> No longer want to receive these emails? Change your <a class="link" target="_blank" href="{{ url('/settings') }}">settings</a> in flox. </div>
</td>
</tr>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</div>
</body>
</html>

View File

@ -0,0 +1,322 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<title> Flox </title>
<!--[if !mso]><!-- -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
#outlook a {
padding: 0;
}
.ReadMsgBody {
width: 100%;
}
.ExternalClass {
width: 100%;
}
.ExternalClass * {
line-height: 100%;
}
body {
margin: 0;
padding: 0;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
table,
td {
border-collapse: collapse;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
img {
border: 0;
height: auto;
line-height: 100%;
outline: none;
text-decoration: none;
-ms-interpolation-mode: bicubic;
}
p {
display: block;
margin: 13px 0;
}
</style>
<!--[if !mso]><!-->
<style type="text/css">
@media only screen and (max-width:480px) {
@-ms-viewport {
width: 320px;
}
@viewport {
width: 320px;
}
}
</style>
<!--<![endif]-->
<!--[if mso]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!--[if lte mso 11]>
<style type="text/css">
.outlook-group-fix { width:100% !important; }
</style>
<![endif]-->
<style type="text/css">
@media only screen and (min-width:480px) {
.mj-column-per-100 {
width: 100% !important;
max-width: 100%;
}
}
</style>
<style type="text/css">
@media only screen and (max-width:480px) {
table.full-width-mobile {
width: 100% !important;
}
td.full-width-mobile {
width: auto !important;
}
}
</style>
<style type="text/css">
.link {
color: #895bff;
}
.link-title {
color: #484848;
text-decoration: none;
float: left;
clear: both;
margin: 0 0 20px 0;
}
.link-title:last-child {
margin: 0;
}
.link-title:hover {
color: #f1309a;
}
.link-title span {
color: gray;
}
.headline {
font-size: 14px;
float: left;
text-transform: uppercase;
color: #f1309a;
font-weight: bold;
margin: 0 0 20px 0;
}
</style>
</head>
<body style="background-color:#ebecee;">
<div style="background-color:#ebecee;">
<!--[if mso | IE]>
<table
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
>
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="Margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tr>
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody>
<tr>
<td style="width:108px;">
<a href="{{ url('/') }}" target="_blank">
<img height="32" src="{{ url('/assets/img/logo-login.png') }}" style="border:0;display:block;outline:none;text-decoration:none;height:32px;width:100%;" width="108" />
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</div>
<!--[if mso | IE]>
</td>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tr>
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000;">
<h2>{{ $headline }}</h2>
<span>{{ $date }}</span>
</div>
</td>
</tr>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<table
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
>
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="background:#ffffff;background-color:#ffffff;Margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%"> @if(count($episodes)) <tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:14px;line-height:20px;text-align:left;color:#000000;">
<span class="headline">{{ $episodesHeadline }}</span>
<br> @foreach($episodes as $episode) <a href="{{ url("/tv/{$episode->tmdb_id}") }}" class="link-title" target="_blank">
{{ $episode->item->title }}
<br>
<span>S{{ $episode->season_number }}E{{ $episode->episode_number }}</span>
</a> @endforeach </div>
</td>
</tr> @endif @if(count($movies)) <tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
<span class="headline">{{ $moviesHeadline }}</span>
<br> @foreach($movies as $movie) <a href="{{ url("/movie/{$movie->tmdb_id}") }}" class="link-title" target="_blank">
{{ $movie->title }}
</a> @endforeach </div>
</td>
</tr> @endif </table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<table
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
>
<tr>
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
<![endif]-->
<div style="Margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td
class="" style="vertical-align:top;width:600px;"
>
<![endif]-->
<div class="mj-column-per-100 outlook-group-fix" style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:gray;"> No longer want to receive these emails? Change your <a class="link" target="_blank" href="{{ url('/settings') }}">settings</a> in flox. </div>
</td>
</tr>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</div>
</body>
</html>

View File

@ -0,0 +1,92 @@
<mjml>
<mj-head>
<mj-style>
.link {
color: #895bff;
}
.link-title {
color: #484848;
text-decoration: none;
float: left;
clear: both;
margin: 0 0 20px 0;
}
.link-title:last-child {
margin: 0;
}
.link-title:hover {
color: #f1309a;
}
.link-title span {
color: gray;
}
.headline {
font-size: 14px;
float: left;
text-transform: uppercase;
color: #f1309a;
font-weight: bold;
margin: 0 0 20px 0;
}
</mj-style>
<mj-title>Flox</mj-title>
<mj-attributes>
<mj-all font-family="Arial, sans-serif"></mj-all>
</mj-attributes>
</mj-head>
<mj-body background-color="#ebecee">
<mj-section>
<mj-column width="100%">
<mj-image href="{{ url('/') }}" width="108px" height="32px" src="{{ url('/assets/img/logo-login.png') }}" />
</mj-column>
<mj-column width="100%">
<mj-text align="center">
<h2>{{ $headline }}</h2>
<span>{{ $date }}</span>
</mj-text>
</mj-column>
</mj-section>
<mj-section background-color="#fff">
<mj-column>
<mj-raw>@if(count($episodes))</mj-raw>
<mj-text font-size="14px" line-height="20px">
<span class="headline">{{ $episodesHeadline }}</span>
<br>
@foreach($episodes as $episode)
<a href="{{ url("/tv/{$episode->tmdb_id}") }}" class="link-title" target="_blank">
{{ $episode->item->title }}
<br>
<span>S{{ $episode->season_number }}E{{ $episode->episode_number }}</span>
</a>
@endforeach
</mj-text>
<mj-raw>
@endif
@if(count($movies))
</mj-raw>
<mj-text>
<span class="headline">{{ $moviesHeadline }}</span>
<br>
@foreach($movies as $movie)
<a href="{{ url("/movie/{$movie->tmdb_id}") }}" class="link-title" target="_blank">
{{ $movie->title }}
</a>
@endforeach
</mj-text>
<mj-raw>@endif</mj-raw>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text color="gray">
No longer want to receive these emails? Change your <a class="link" target="_blank" href="{{ url('/settings') }}">settings</a> in flox.
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>

View File

@ -0,0 +1,92 @@
<mjml>
<mj-head>
<mj-style>
.link {
color: #895bff;
}
.link-title {
color: #484848;
text-decoration: none;
float: left;
clear: both;
margin: 0 0 20px 0;
}
.link-title:last-child {
margin: 0;
}
.link-title:hover {
color: #f1309a;
}
.link-title span {
color: gray;
}
.headline {
font-size: 14px;
float: left;
text-transform: uppercase;
color: #f1309a;
font-weight: bold;
margin: 0 0 20px 0;
}
</mj-style>
<mj-title>Flox</mj-title>
<mj-attributes>
<mj-all font-family="Arial, sans-serif"></mj-all>
</mj-attributes>
</mj-head>
<mj-body background-color="#ebecee">
<mj-section>
<mj-column width="100%">
<mj-image href="{{ url('/') }}" width="108px" height="32px" src="{{ url('/assets/img/logo-login.png') }}" />
</mj-column>
<mj-column width="100%">
<mj-text align="center">
<h2>{{ $headline }}</h2>
<span>{{ $date }}</span>
</mj-text>
</mj-column>
</mj-section>
<mj-section background-color="#fff">
<mj-column>
<mj-raw>@if(count($episodes))</mj-raw>
<mj-text font-size="14px" line-height="20px">
<span class="headline">{{ $episodesHeadline }}</span>
<br>
@foreach($episodes as $episode)
<a href="{{ url("/tv/{$episode->tmdb_id}") }}" class="link-title" target="_blank">
{{ $episode->item->title }}
<br>
<span>S{{ $episode->season_number }}E{{ $episode->episode_number }}</span>
</a>
@endforeach
</mj-text>
<mj-raw>
@endif
@if(count($movies))
</mj-raw>
<mj-text>
<span class="headline">{{ $moviesHeadline }}</span>
<br>
@foreach($movies as $movie)
<a href="{{ url("/movie/{$movie->tmdb_id}") }}" class="link-title" target="_blank">
{{ $movie->title }}
</a>
@endforeach
</mj-text>
<mj-raw>@endif</mj-raw>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text color="gray">
No longer want to receive these emails? Change your <a class="link" target="_blank" href="{{ url('/settings') }}">settings</a> in flox.
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>

View File

@ -51,3 +51,9 @@ a {
background: rgba($main1, .99);
color: #fff;
}
@keyframes blink {
0% { opacity: 1; }
50% { opacity: .3; }
100% { opacity: 1; }
}

View File

@ -45,6 +45,16 @@ $gradient: linear-gradient(to right, $main1, $main2);
}
.loader {
background: url(../../../public/assets/img/logo-small.png);
width: 32px;
height: 25px;
@include transition(opacity);
animation: blink 1s infinite;
}
.loader-old {
width: 29px;
height: 29px;
display: block;

View File

@ -16,8 +16,12 @@ main {
padding: 200px 17px 0 0;
}
@include media(3) {
padding: 230px 0 0 0;
}
@include media(5) {
padding: 80px 0 0 0;
padding: 190px 0 0 0;
}
.subpage-open & {

View File

@ -17,10 +17,27 @@ footer {
.attribution {
color: #fff;
float: left;
@include media(4) {
font-size: 14px;
}
}
.tmdb-logo {
@include media(4) {
width: 100%;
float: left;
}
}
.footer-actions {
float: right;
@include media(3) {
float: left;
clear: both;
margin: 20px 0 0 0;
}
}
.icon-tmdb {
@ -43,8 +60,8 @@ footer {
@include media(3) {
float: left;
clear: both;
margin: 30px 0 0 0;
//clear: both;
//margin: 30px 0 0 0;
}
&:active {

View File

@ -17,10 +17,6 @@ header {
z-index: 20;
opacity: 0;
@include media(5) {
//padding: 15px 0;
}
.active & {
transition: opacity .7s ease .1s, padding-top .2s ease 0s, padding-bottom .2s ease 0s;
opacity: 1;
@ -36,6 +32,10 @@ header {
padding: 25px 0;
}
.mobile-open & {
background: $gradient;
}
.open-modal & {
padding-right: 17px;
}
@ -45,6 +45,7 @@ header {
float: left;
opacity: .9;
cursor: pointer;
user-select: none;
img {
@include transition(width, height);
@ -56,13 +57,13 @@ header {
}
@include media(5) {
width: 80px;
height: auto;
margin: 7px 0 0 0;
//width: 80px;
//height: auto;
//margin: 7px 0 0 0;
img {
width: 100%;
height: auto;
//width: 100%;
//height: auto;
}
}
}
@ -75,12 +76,22 @@ header {
padding: 0;
opacity: .9;
@include media(4) {
@include media(3) {
clear: left;
float: left;
margin: 20px 0 0 0;
}
@include media(5) {
display: none;
margin: 0;
width: 100%;
.mobile-open & {
display: block;
}
}
li {
float: left;
margin: 0 20px 0 0;
@ -88,6 +99,15 @@ header {
&:last-child {
margin: 0;
}
@include media(5) {
width: 33%;
margin: 0;
}
@include media(6) {
width: 100%;
}
}
a {
@ -95,6 +115,7 @@ header {
text-decoration: none;
text-transform: uppercase;
font-size: 16px;
float: left;
border-bottom: 2px solid transparent;
&.router-link-active {
@ -105,9 +126,19 @@ header {
opacity: .6;
}
@include media(5) {
@include media(3) {
font-size: 14px;
}
@include media(5) {
padding: 8px 0;
}
}
}
.site-nav-first {
@include media(5) {
margin: 15px 0 0 0;
}
}
@ -115,9 +146,31 @@ header {
float: right;
margin: 7px 0 0 0;
@include media(4) {
@include media(3) {
clear: none;
float: left;
margin: 20px 0 0 15px;
}
@include media(5) {
margin: 0;
}
}
.icon-hamburger {
background: url(../../../public/assets/img/hamburger.png);
width: 31px;
height: 23px;
float: right;
display: none;
margin: 6px 0 0 0;
cursor: pointer;
&:active {
opacity: .6;
}
@include media(5) {
display: block;
}
}

View File

@ -25,6 +25,7 @@
width: 100%;
input[type="text"],
input[type="email"],
input[type="password"] {
float: left;
width: 100%;

View File

@ -11,11 +11,11 @@
}
@include media(4) {
height: 450px;
height: 590px;
}
@include media(6) {
height: 550px;
height: 650px;
}
}
@ -108,9 +108,9 @@
width: 7px;
height: 8px;
float: left;
margin: 9px 7px 0 0;
margin: 0 7px 0 0;
@include media(4) {
@include media(6) {
display: none;
}
}
@ -120,9 +120,9 @@
width: 14px;
height: 11px;
float: left;
margin: 7px 7px 0 0;
margin: 0 7px 0 0;
@include media(4) {
@include media(6) {
display: none;
}
}
@ -132,9 +132,9 @@
width: 14px;
height: 11px;
float: left;
margin: 7px 7px 0 0;
margin: 0 7px 0 0;
@include media(4) {
@include media(6) {
display: none;
}
}
@ -143,6 +143,7 @@
float: left;
clear: both;
margin: 0 0 0 272px;
display: flex;
span,
a {
@ -151,19 +152,26 @@
font-size: 17px;
cursor: pointer;
text-decoration: none;
display: flex;
align-items: center;
flex: auto;
@include transition(background);
@include media(4) {
padding: 5px 8px;
//padding: 5px 8px;
font-size: 15px;
//width: 50%;
//text-align: center;
}
@include media(5) {
clear: both;
// clear: both;
//width: 50%;
}
@include media(6) {
width: auto;
//width: 50%;
//overflow: hidden;
//text-overflow: ellipsis;
@ -176,8 +184,13 @@
margin: 0 0 0 200px;
}
@include media(5) {
@include media(4) {
margin: 0 0 0 -20px;
width: 100%;
}
@include media(6) {
flex-direction: column;
}
}
@ -212,7 +225,8 @@
background: rgba($dark, .5);
font-size: 14px;
padding: 3px 7px;
margin: 0 5px 0 0;
margin: 0 5px 5px 0;
float: left;
@include transition(background);
@ -233,18 +247,15 @@
font-style: normal;
text-transform: uppercase;
}
@include media(4) {
//font-size: 14px;
}
}
@include media(3) {
margin: 0 0 40px 230px;
}
@include media(5) {
margin: 0 0 20px 0;
@include media(4) {
clear: both;
margin: 30px 0 40px 0;
}
}
@ -278,6 +289,10 @@
.big-teaser-content {
position: absolute;
bottom: 0;
@include media(4) {
width: 100%;
}
}
.subpage-content {
@ -303,18 +318,14 @@
z-index: 50;
@include media(3) {
margin: -360px 0 0 0;
}
@include media(4) {
margin: -450px 0 0 0;
margin: -250px 0 0 0;
}
@include media(5) {
margin: 0;
}
@include media(6) {
@include media(4) {
width: 100%;
margin: 0 0 30px 0;
}
@ -325,7 +336,7 @@
position: relative;
display: none;
@include media(5) {
@include media(4) {
display: block;
}
@ -381,7 +392,7 @@
}
}
@include media(5) {
@include media(4) {
//max-width: 100px;
//height: auto;
display: none;
@ -462,7 +473,11 @@
//margin: 0 0 0 30px;
padding: 0 0 30px 0;
@include media(6) {
@include media(3) {
width: calc(100% - 240px);
}
@include media(4) {
margin: 0;
width: 100%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B