mirror of https://github.com/pixelfed/pixelfed
commit
700c7805ce
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\{
|
||||
DB,
|
||||
Storage
|
||||
};
|
||||
use App\{
|
||||
Story,
|
||||
StoryView
|
||||
};
|
||||
|
||||
class StoryGC extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'story:gc';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Clear expired Stories';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$stories = Story::where('expires_at', '<', now())->take(50)->get();
|
||||
|
||||
if($stories->count() == 0) {
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach($stories as $story) {
|
||||
if(Storage::exists($story->path) == true) {
|
||||
Storage::delete($story->path);
|
||||
}
|
||||
DB::transaction(function() use($story) {
|
||||
StoryView::whereStoryId($story->id)->delete();
|
||||
$story->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Keys
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Passport uses encryption keys while generating secure access tokens for
|
||||
| your application. By default, the keys are stored as local files but
|
||||
| can be set via environment variables when that is more convenient.
|
||||
|
|
||||
*/
|
||||
|
||||
'private_key' => env('PASSPORT_PRIVATE_KEY'),
|
||||
|
||||
'public_key' => env('PASSPORT_PUBLIC_KEY'),
|
||||
|
||||
];
|
||||
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateStoriesTable extends Migration
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
|
||||
}
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::dropIfExists('stories');
|
||||
Schema::dropIfExists('story_items');
|
||||
Schema::dropIfExists('story_reactions');
|
||||
Schema::dropIfExists('story_views');
|
||||
|
||||
Schema::create('stories', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('profile_id')->unsigned()->index();
|
||||
$table->string('type')->nullable();
|
||||
$table->unsignedInteger('size')->nullable();
|
||||
$table->string('mime')->nullable();
|
||||
$table->smallInteger('duration')->unsigned();
|
||||
$table->string('path')->nullable();
|
||||
$table->string('cdn_url')->nullable();
|
||||
$table->boolean('public')->default(false)->index();
|
||||
$table->boolean('local')->default(false)->index();
|
||||
$table->unsignedInteger('view_count')->nullable();
|
||||
$table->unsignedInteger('comment_count')->nullable();
|
||||
$table->json('story')->nullable();
|
||||
$table->unique(['profile_id', 'path']);
|
||||
$table->timestamp('expires_at')->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('story_views', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('story_id')->unsigned()->index();
|
||||
$table->bigInteger('profile_id')->unsigned()->index();
|
||||
$table->unique(['profile_id', 'story_id']);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('stories');
|
||||
Schema::dropIfExists('story_views');
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{15:function(e,a,o){e.exports=o("YMO/")},"YMO/":function(e,a,o){(function(e){function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}ace.define("ace/theme/monokai",["require","exports","module","ace/lib/dom"],(function(e,a,o){a.isDark=!0,a.cssClass="ace-monokai",a.cssText=".ace-monokai .ace_gutter {background: #2F3129;color: #8F908A}.ace-monokai .ace_print-margin {width: 1px;background: #555651}.ace-monokai {background-color: #272822;color: #F8F8F2}.ace-monokai .ace_cursor {color: #F8F8F0}.ace-monokai .ace_marker-layer .ace_selection {background: #49483E}.ace-monokai.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #272822;}.ace-monokai .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-monokai .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #49483E}.ace-monokai .ace_marker-layer .ace_active-line {background: #202020}.ace-monokai .ace_gutter-active-line {background-color: #272727}.ace-monokai .ace_marker-layer .ace_selected-word {border: 1px solid #49483E}.ace-monokai .ace_invisible {color: #52524d}.ace-monokai .ace_entity.ace_name.ace_tag,.ace-monokai .ace_keyword,.ace-monokai .ace_meta.ace_tag,.ace-monokai .ace_storage {color: #F92672}.ace-monokai .ace_punctuation,.ace-monokai .ace_punctuation.ace_tag {color: #fff}.ace-monokai .ace_constant.ace_character,.ace-monokai .ace_constant.ace_language,.ace-monokai .ace_constant.ace_numeric,.ace-monokai .ace_constant.ace_other {color: #AE81FF}.ace-monokai .ace_invalid {color: #F8F8F0;background-color: #F92672}.ace-monokai .ace_invalid.ace_deprecated {color: #F8F8F0;background-color: #AE81FF}.ace-monokai .ace_support.ace_constant,.ace-monokai .ace_support.ace_function {color: #66D9EF}.ace-monokai .ace_fold {background-color: #A6E22E;border-color: #F8F8F2}.ace-monokai .ace_storage.ace_type,.ace-monokai .ace_support.ace_class,.ace-monokai .ace_support.ace_type {font-style: italic;color: #66D9EF}.ace-monokai .ace_entity.ace_name.ace_function,.ace-monokai .ace_entity.ace_other,.ace-monokai .ace_entity.ace_other.ace_attribute-name,.ace-monokai .ace_variable {color: #A6E22E}.ace-monokai .ace_variable.ace_parameter {font-style: italic;color: #FD971F}.ace-monokai .ace_string {color: #E6DB74}.ace-monokai .ace_comment {color: #75715E}.ace-monokai .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y}",e("../lib/dom").importCssString(a.cssText,a.cssClass)})),ace.require(["ace/theme/monokai"],(function(c){"object"==o(e)&&"object"==o(a)&&e&&(e.exports=c)}))}).call(this,o("YuTi")(e))},YuTi:function(e,a){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}}},[[15,0]]]);
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{15:function(e,a,o){e.exports=o("YMO/")},"YMO/":function(e,a,o){(function(e){function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}ace.define("ace/theme/monokai",["require","exports","module","ace/lib/dom"],(function(e,a,o){a.isDark=!0,a.cssClass="ace-monokai",a.cssText=".ace-monokai .ace_gutter {background: #2F3129;color: #8F908A}.ace-monokai .ace_print-margin {width: 1px;background: #555651}.ace-monokai {background-color: #272822;color: #F8F8F2}.ace-monokai .ace_cursor {color: #F8F8F0}.ace-monokai .ace_marker-layer .ace_selection {background: #49483E}.ace-monokai.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #272822;}.ace-monokai .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-monokai .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #49483E}.ace-monokai .ace_marker-layer .ace_active-line {background: #202020}.ace-monokai .ace_gutter-active-line {background-color: #272727}.ace-monokai .ace_marker-layer .ace_selected-word {border: 1px solid #49483E}.ace-monokai .ace_invisible {color: #52524d}.ace-monokai .ace_entity.ace_name.ace_tag,.ace-monokai .ace_keyword,.ace-monokai .ace_meta.ace_tag,.ace-monokai .ace_storage {color: #F92672}.ace-monokai .ace_punctuation,.ace-monokai .ace_punctuation.ace_tag {color: #fff}.ace-monokai .ace_constant.ace_character,.ace-monokai .ace_constant.ace_language,.ace-monokai .ace_constant.ace_numeric,.ace-monokai .ace_constant.ace_other {color: #AE81FF}.ace-monokai .ace_invalid {color: #F8F8F0;background-color: #F92672}.ace-monokai .ace_invalid.ace_deprecated {color: #F8F8F0;background-color: #AE81FF}.ace-monokai .ace_support.ace_constant,.ace-monokai .ace_support.ace_function {color: #66D9EF}.ace-monokai .ace_fold {background-color: #A6E22E;border-color: #F8F8F2}.ace-monokai .ace_storage.ace_type,.ace-monokai .ace_support.ace_class,.ace-monokai .ace_support.ace_type {font-style: italic;color: #66D9EF}.ace-monokai .ace_entity.ace_name.ace_function,.ace-monokai .ace_entity.ace_other,.ace-monokai .ace_entity.ace_other.ace_attribute-name,.ace-monokai .ace_variable {color: #A6E22E}.ace-monokai .ace_variable.ace_parameter {font-style: italic;color: #FD971F}.ace-monokai .ace_string {color: #E6DB74}.ace-monokai .ace_comment {color: #75715E}.ace-monokai .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y}",e("../lib/dom").importCssString(a.cssText,a.cssClass)})),ace.require(["ace/theme/monokai"],(function(c){"object"==o(e)&&"object"==o(a)&&e&&(e.exports=c)}))}).call(this,o("YuTi")(e))},YuTi:function(e,a){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}}},[[15,0]]]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,42 +1,268 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<p class="display-4 text-center py-5">Share Your Story</p>
|
||||
<div class="container mt-2 mt-md-5">
|
||||
<input type="file" id="pf-dz" name="media" class="w-100 h-100 d-none file-input" draggable="true" v-bind:accept="config.mimes">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 offset-md-3">
|
||||
|
||||
<div class="d-flex justify-content-center align-item-center">
|
||||
<div class="bg-dark" style="width:400px;height:600px">
|
||||
<p class="text-center text-light font-weight-bold">Add Photo</p>
|
||||
<!-- LANDING -->
|
||||
<div v-if="page == 'landing'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center" style="height: 90vh;">
|
||||
<div class="text-center flex-fill mt-5 pt-5">
|
||||
<img src="/img/pixelfed-icon-grey.svg" width="60px" height="60px">
|
||||
<p class="font-weight-bold lead text-lighter mt-1">Stories</p>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="card w-100 shadow-none">
|
||||
<div class="list-group">
|
||||
<a class="list-group-item text-center lead text-decoration-none text-dark" href="#" @click.prevent="upload()">Add Photo</a>
|
||||
<a v-if="stories.length" class="list-group-item text-center lead text-decoration-none text-dark" href="#" @click.prevent="edit()">Edit Story</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex-fill">
|
||||
<p class="text-lighter small text-uppercase">
|
||||
<a href="/" class="text-muted font-weight-bold">Home</a>
|
||||
<span class="px-2 text-lighter">|</span>
|
||||
<a href="/i/my/story" class="text-muted font-weight-bold">View My Story</a>
|
||||
<span class="px-2 text-lighter">|</span>
|
||||
<a href="/site/help" class="text-muted font-weight-bold">Help</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CROP -->
|
||||
<div v-if="page == 'crop'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center" style="height: 95vh;">
|
||||
<div class="text-center pt-5 mb-3 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<button class="btn btn-outline-lighter btn-sm py-0 px-md-3"><i class="pr-2 fas fa-chevron-left fa-sm"></i> Delete</button>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<img class="d-inline-block mr-2" src="/img/pixelfed-icon-grey.svg" width="30px" height="30px">
|
||||
<span class="font-weight-bold lead text-lighter">Stories</span>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-outline-success btn-sm py-0 px-md-3">Crop <i class="pl-2 fas fa-chevron-right fa-sm"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="card w-100 mt-3">
|
||||
<div class="card-body p-0">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:relativeZoom="cropper.zoom"
|
||||
:aspectRatio="cropper.aspectRatio"
|
||||
:viewMode="cropper.viewMode"
|
||||
:zoomable="cropper.zoomable"
|
||||
:rotatable="true"
|
||||
:src="mediaUrl"
|
||||
>
|
||||
</vue-cropper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center flex-fill">
|
||||
<p class="text-lighter small text-uppercase pt-2">
|
||||
<!-- <a href="#" class="text-muted font-weight-bold">Home</a>
|
||||
<span class="px-2 text-lighter">|</span>
|
||||
<a href="#" class="text-muted font-weight-bold">View My Story</a>
|
||||
<span class="px-2 text-lighter">|</span> -->
|
||||
<a href="/site/help" class="text-muted font-weight-bold mb-0">Help</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ERROR -->
|
||||
<div v-if="page == 'error'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center align-items-center" style="height: 90vh;">
|
||||
<p class="h3 mb-0">Oops!</p>
|
||||
<p class="text-muted lead">An error occurred, please try again later.</p>
|
||||
<p class="text-muted mb-0">
|
||||
<a class="btn btn-outline-secondary py-0 px-5 font-weight-bold" href="/">Go back</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="page == 'edit'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center" style="height: 90vh;">
|
||||
<div class="text-center flex-fill mt-5 pt-5">
|
||||
<img src="/img/pixelfed-icon-grey.svg" width="60px" height="60px">
|
||||
<p class="font-weight-bold lead text-lighter mt-1">Stories</p>
|
||||
</div>
|
||||
<div class="flex-fill py-5">
|
||||
<div class="card w-100 shadow-none" style="max-height: 500px; overflow-y: auto">
|
||||
<div class="list-group">
|
||||
<div v-for="(story, index) in stories" class="list-group-item text-center text-dark" href="#">
|
||||
<div class="media align-items-center">
|
||||
<img :src="story.src" class="img-fluid mr-3 cursor-pointer" width="70px" height="70px" @click="showLightbox(story)">
|
||||
<div class="media-body">
|
||||
<p class="mb-0">Expires</p>
|
||||
<p class="mb-0 text-muted small"><span>{{expiresTimestamp(story.expires_at)}}</span></p>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<button @click="deleteStory(story, index)" class="btn btn-danger btn-sm font-weight-bold text-uppercase">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-fill text-center">
|
||||
<a class="btn btn-outline-secondary py-0 px-5 font-weight-bold" href="/i/stories/new">Go back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<b-modal
|
||||
id="lightbox"
|
||||
ref="lightboxModal"
|
||||
hide-header
|
||||
hide-footer
|
||||
centered
|
||||
size="lg"
|
||||
body-class="p-0"
|
||||
>
|
||||
<div v-if="lightboxMedia" class="w-100 h-100">
|
||||
<img :src="lightboxMedia.url" style="max-height: 100%; max-width: 100%">
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style type="text/css" scoped>
|
||||
.navtab .nav-link {
|
||||
color: #657786;
|
||||
}
|
||||
|
||||
.navtab .nav-link.active {
|
||||
color: #08d;
|
||||
border-bottom: 4px solid #08d;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
import VueTimeago from 'vue-timeago';
|
||||
import VueCropper from 'vue-cropperjs';
|
||||
import 'cropperjs/dist/cropper.css';
|
||||
export default {
|
||||
components: {
|
||||
VueCropper,
|
||||
VueTimeago
|
||||
},
|
||||
|
||||
props: ['profile-id'],
|
||||
data() {
|
||||
return {
|
||||
currentTab: 'upload',
|
||||
config: window.App.config,
|
||||
mimes: [
|
||||
'image/jpeg',
|
||||
'image/png'
|
||||
],
|
||||
page: 'landing',
|
||||
pages: [
|
||||
'landing',
|
||||
'crop',
|
||||
'edit',
|
||||
'confirm',
|
||||
'error'
|
||||
],
|
||||
uploading: false,
|
||||
uploadProgress: 100,
|
||||
cropper: {
|
||||
aspectRatio: 9/16,
|
||||
viewMode: 1,
|
||||
zoomable: true,
|
||||
zoom: null
|
||||
},
|
||||
mediaUrl: null,
|
||||
stories: [],
|
||||
lightboxMedia: false,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.welcomeMessage();
|
||||
this.mediaWatcher();
|
||||
axios.get('/api/stories/v1/fetch/' + this.profileId)
|
||||
.then(res => this.stories = res.data);
|
||||
},
|
||||
|
||||
methods: {
|
||||
welcomeMessage() {
|
||||
|
||||
upload() {
|
||||
let fi = $('.file-input[name="media"]');
|
||||
fi.trigger('click');
|
||||
},
|
||||
|
||||
mediaWatcher() {
|
||||
let self = this;
|
||||
$(document).on('change', '#pf-dz', function(e) {
|
||||
self.triggerUpload();
|
||||
});
|
||||
},
|
||||
|
||||
triggerUpload() {
|
||||
let self = this;
|
||||
self.uploading = true;
|
||||
let io = document.querySelector('#pf-dz');
|
||||
Array.prototype.forEach.call(io.files, function(io, i) {
|
||||
if(self.media && self.media.length + i >= self.config.uploader.album_limit) {
|
||||
swal('Error', 'You can only upload ' + self.config.uploader.album_limit + ' photos per album', 'error');
|
||||
self.uploading = false;
|
||||
self.page = 2;
|
||||
return;
|
||||
}
|
||||
let type = io.type;
|
||||
let validated = $.inArray(type, self.mimes);
|
||||
if(validated == -1) {
|
||||
swal('Invalid File Type', 'The file you are trying to add is not a valid mime type. Please upload a '+self.mimes+' only.', 'error');
|
||||
self.uploading = false;
|
||||
self.page = 'error';
|
||||
return;
|
||||
}
|
||||
|
||||
let form = new FormData();
|
||||
form.append('file', io);
|
||||
|
||||
let xhrConfig = {
|
||||
onUploadProgress: function(e) {
|
||||
let progress = Math.round( (e.loaded * 100) / e.total );
|
||||
self.uploadProgress = progress;
|
||||
}
|
||||
};
|
||||
|
||||
axios.post('/api/stories/v1/add', form, xhrConfig)
|
||||
.then(function(e) {
|
||||
self.uploadProgress = 100;
|
||||
self.uploading = false;
|
||||
window.location.href = '/i/my/story';
|
||||
self.mediaUrl = e.data.media_url;
|
||||
}).catch(function(e) {
|
||||
self.uploading = false;
|
||||
io.value = null;
|
||||
swal('Oops!', e.response.data.message, 'warning');
|
||||
});
|
||||
io.value = null;
|
||||
self.uploadProgress = 0;
|
||||
});
|
||||
},
|
||||
|
||||
expiresTimestamp(ts) {
|
||||
ts = new Date(ts * 1000);
|
||||
return ts.toDateString() + ' ' + ts.toLocaleTimeString();
|
||||
},
|
||||
|
||||
edit() {
|
||||
this.page = 'edit';
|
||||
},
|
||||
|
||||
showLightbox(story) {
|
||||
this.lightboxMedia = {
|
||||
url: story.src
|
||||
}
|
||||
this.$refs.lightboxModal.show();
|
||||
},
|
||||
|
||||
deleteStory(story, index) {
|
||||
if(window.confirm('Are you sure you want to delete this Story?') != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.delete('/api/stories/v1/delete/' + story.id)
|
||||
.then(res => {
|
||||
this.stories.splice(index, 1);
|
||||
if(this.stories.length == 0) {
|
||||
window.location.href = '/i/stories/new';
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div v-if="loading" class="row">
|
||||
<div class="col-12 mt-5 pt-5">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="stories.length != 0">
|
||||
<div id="storyContainer" class="d-none m-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import 'zuck.js/dist/zuck.css';
|
||||
import 'zuck.js/dist/skins/snapgram.css';
|
||||
window.Zuck = require('zuck.js');
|
||||
|
||||
export default {
|
||||
props: ['pid'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
stories: {},
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
this.fetchStories();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchStories() {
|
||||
axios.get('/api/stories/v1/profile/' + this.pid)
|
||||
.then(res => {
|
||||
let data = res.data;
|
||||
if(data.length == 0) {
|
||||
window.location.href = '/';
|
||||
return;
|
||||
}
|
||||
window._storyData = data;
|
||||
window.stories = new Zuck('storyContainer', {
|
||||
stories: data,
|
||||
localStorage: false,
|
||||
callbacks: {
|
||||
onOpen (storyId, callback) {
|
||||
document.body.style.overflow = "hidden";
|
||||
callback()
|
||||
},
|
||||
|
||||
onEnd (storyId, callback) {
|
||||
axios.post('/i/stories/viewed', {
|
||||
id: storyId
|
||||
});
|
||||
callback();
|
||||
},
|
||||
|
||||
onClose (storyId, callback) {
|
||||
document.body.style.overflow = "auto";
|
||||
callback();
|
||||
window.location.href = '/';
|
||||
},
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
|
||||
// todo: refactor this mess
|
||||
document.querySelectorAll('#storyContainer .story')[0].click()
|
||||
})
|
||||
.catch(err => {
|
||||
window.location.href = '/';
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
#storyContainer .story {
|
||||
margin-right: 2rem;
|
||||
width: 100%;
|
||||
max-width: 64px;
|
||||
}
|
||||
.stories.carousel .story > .item-link > .item-preview {
|
||||
height: 64px;
|
||||
}
|
||||
#zuck-modal.with-effects {
|
||||
width: 100%;
|
||||
}
|
||||
.stories.carousel .story > .item-link > .info .name {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
.stories.carousel .story > .item-link > .info {
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,4 @@
|
||||
Vue.component(
|
||||
'story-compose',
|
||||
require('./components/StoryCompose.vue').default
|
||||
);
|
||||
@ -0,0 +1,11 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<story-viewer pid="{{$pid}}"></story-viewer>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript" src="{{mix('js/compose.js')}}"></script>
|
||||
<script type="text/javascript" src="{{mix('js/profile.js')}}"></script>
|
||||
<script type="text/javascript">App.boot();</script>
|
||||
@endpush
|
||||
@ -0,0 +1,11 @@
|
||||
@extends('layouts.blank')
|
||||
|
||||
|
||||
@section('content')
|
||||
<story-compose profile-id="{{auth()->user()->profile_id}}"></story-compose>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript" src="{{ mix('js/story-compose.js') }}"></script>
|
||||
<script type="text/javascript">window.App.boot()</script>
|
||||
@endpush
|
||||
Loading…
Reference in New Issue