Merge pull request #2738 from pixelfed/staging

Staging
This commit is contained in:
daniel 2021-04-29 23:55:10 -06:00 committed by GitHub
commit 1b7ef61a4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 907 additions and 457 deletions

View File

@ -67,6 +67,11 @@
- Updated AdminInstanceController, invalidate banned domain cache when updated. ([35393edf](https://github.com/pixelfed/pixelfed/commit/35393edf))
- Updated AP Helpers, use instance filtering. ([66b4f8c7](https://github.com/pixelfed/pixelfed/commit/66b4f8c7))
- Updated ApiV1Controller, add missing instance api attributes. ([64b86546](https://github.com/pixelfed/pixelfed/commit/64b86546))
- Updated story garbage collection, handle non active stories and new ephemeral story media directory. ([c43f8bcc](https://github.com/pixelfed/pixelfed/commit/c43f8bcc))
- Updated Stories, add crop and duration settings to composer. ([c8edca69](https://github.com/pixelfed/pixelfed/commit/c8edca69))
- Updated instance endpoint, add custom description. ([668e936e](https://github.com/pixelfed/pixelfed/commit/668e936e))
- Updated StoryCompose component, improve full screen preview. ([39a76103](https://github.com/pixelfed/pixelfed/commit/39a76103))
- Updated Helpers, fix broken tests. ([22dddaa0](https://github.com/pixelfed/pixelfed/commit/22dddaa0))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.10.10 (2021-01-28)](https://github.com/pixelfed/pixelfed/compare/v0.10.9...v0.10.10)

View File

@ -3,103 +3,127 @@
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\{
DB,
Storage
};
use App\{
Story,
StoryView
};
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use App\Story;
use App\StoryView;
class StoryGC extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'story:gc';
/**
* 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';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear expired Stories';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->directoryScan();
$this->deleteViews();
$this->deleteStories();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->directoryScan();
$this->deleteViews();
$this->deleteStories();
}
protected function directoryScan()
{
$day = now()->day;
protected function directoryScan()
{
$hour = now()->hour;
if($day != 3) {
return;
}
if($hour !== 1) {
return;
}
$monthHash = substr(hash('sha1', date('Y').date('m')), 0, 12);
$monthHash = substr(hash('sha1', date('Y').date('m')), 0, 12);
$t1 = Storage::directories('public/_esm.t1');
$t2 = Storage::directories('public/_esm.t2');
$t1 = Storage::directories('public/_esm.t1');
$t2 = Storage::directories('public/_esm.t2');
$dirs = array_merge($t1, $t2);
$dirs = array_merge($t1, $t2);
foreach($dirs as $dir) {
$hash = last(explode('/', $dir));
if($hash != $monthHash) {
$this->info('Found directory to delete: ' . $dir);
$this->deleteDirectory($dir);
}
}
}
foreach($dirs as $dir) {
$hash = last(explode('/', $dir));
if($hash != $monthHash) {
$this->info('Found directory to delete: ' . $dir);
$this->deleteDirectory($dir);
}
}
protected function deleteDirectory($path)
{
Storage::deleteDirectory($path);
}
$mh = hash('sha256', date('Y').'-.-'.date('m'));
$monthHash = date('Y').date('m').substr($mh, 0, 6).substr($mh, 58, 6);
$dirs = Storage::directories('public/_esm.t3');
protected function deleteViews()
{
StoryView::where('created_at', '<', now()->subMinutes(1441))->delete();
}
foreach($dirs as $dir) {
$hash = last(explode('/', $dir));
if($hash != $monthHash) {
$this->info('Found directory to delete: ' . $dir);
$this->deleteDirectory($dir);
}
}
}
protected function deleteStories()
{
$stories = Story::where('created_at', '<', now()->subMinutes(1441))->take(50)->get();
protected function deleteDirectory($path)
{
Storage::deleteDirectory($path);
}
if($stories->count() == 0) {
exit;
}
protected function deleteViews()
{
StoryView::where('created_at', '<', now()->subMinutes(1441))->delete();
}
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();
});
}
}
protected function deleteStories()
{
$stories = Story::where('created_at', '>', now()->subMinutes(30))
->whereNull('active')
->get();
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();
});
}
$stories = Story::where('created_at', '<', now()
->subMinutes(1441))
->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();
});
}
}
}

View File

@ -48,6 +48,11 @@ class Handler extends ExceptionHandler
*/
public function render($request, Throwable $exception)
{
if ($request->wantsJson())
return response()->json(
['error' => $exception->getMessage()],
method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : 500
);
return parent::render($request, $exception);
}
}

View File

@ -960,7 +960,7 @@ class ApiV1Controller extends Controller
$res = [
'approval_required' => false,
'contact_account' => null,
'description' => 'Pixelfed - Photo sharing for everyone',
'description' => config('instance.description'),
'email' => config('instance.email'),
'invites_enabled' => false,
'rules' => [],

View File

@ -12,7 +12,7 @@ use App\Services\StoryService;
use Cache, Storage;
use Image as Intervention;
use App\Services\FollowerService;
use App\Services\MediaPathService;
class StoryController extends Controller
{
@ -37,7 +37,7 @@ class StoryController extends Controller
}
$photo = $request->file('file');
$path = $this->storePhoto($photo);
$path = $this->storePhoto($photo, $user);
$story = new Story();
$story->duration = 3;
@ -47,21 +47,18 @@ class StoryController extends Controller
$story->path = $path;
$story->local = true;
$story->size = $photo->getSize();
$story->expires_at = now()->addHours(24);
$story->save();
return [
'code' => 200,
'msg' => 'Successfully added',
'media_id' => (string) $story->id,
'media_url' => url(Storage::url($story->path))
];
}
protected function storePhoto($photo)
protected function storePhoto($photo, $user)
{
$monthHash = substr(hash('sha1', date('Y').date('m')), 0, 12);
$sid = (string) Str::uuid();
$rid = Str::random(9).'.'.Str::random(9);
$mimes = explode(',', config('pixelfed.media_types'));
if(in_array($photo->getMimeType(), [
'image/jpeg',
@ -72,9 +69,9 @@ class StoryController extends Controller
return;
}
$storagePath = "public/_esm.t2/{$monthHash}/{$sid}/{$rid}";
$storagePath = MediaPathService::story($user->profile);
$path = $photo->store($storagePath);
if(in_array($photo->getMimeType(), ['image/jpeg','image/png',])) {
if(in_array($photo->getMimeType(), ['image/jpeg','image/png'])) {
$fpath = storage_path('app/' . $path);
$img = Intervention::make($fpath);
$img->orientate();
@ -84,6 +81,68 @@ class StoryController extends Controller
return $path;
}
public function cropPhoto(Request $request)
{
abort_if(!config('instance.stories.enabled') || !$request->user(), 404);
$this->validate($request, [
'media_id' => 'required|integer|min:1',
'width' => 'required',
'height' => 'required',
'x' => 'required',
'y' => 'required'
]);
$user = $request->user();
$id = $request->input('media_id');
$width = round($request->input('width'));
$height = round($request->input('height'));
$x = round($request->input('x'));
$y = round($request->input('y'));
$story = Story::whereProfileId($user->profile_id)->findOrFail($id);
$path = storage_path('app/' . $story->path);
if(!is_file($path)) {
abort(400, 'Invalid or missing media.');
}
$img = Intervention::make($path);
$img->crop($width, $height, $x, $y);
$img->save($path, config('pixelfed.image_quality'));
return [
'code' => 200,
'msg' => 'Successfully cropped',
];
}
public function publishStory(Request $request)
{
abort_if(!config('instance.stories.enabled') || !$request->user(), 404);
$this->validate($request, [
'media_id' => 'required',
'duration' => 'required|integer|min:3|max:10'
]);
$id = $request->input('media_id');
$user = $request->user();
$story = Story::whereProfileId($user->profile_id)
->findOrFail($id);
$story->active = true;
$story->duration = $request->input('duration', 10);
$story->expires_at = now()->addHours(24);
$story->save();
return [
'code' => 200,
'msg' => 'Successfully published',
];
}
public function apiV1Delete(Request $request, $id)
{
abort_if(!config('instance.stories.enabled') || !$request->user(), 404);
@ -91,7 +150,7 @@ class StoryController extends Controller
$user = $request->user();
$story = Story::whereProfileId($user->profile_id)
->findOrFail($id);
->findOrFail($id);
if(Storage::exists($story->path) == true) {
Storage::delete($story->path);
@ -114,6 +173,7 @@ class StoryController extends Controller
if(config('database.default') == 'pgsql') {
$db = Story::with('profile')
->whereActive(true)
->whereIn('profile_id', $following)
->where('expires_at', '>', now())
->distinct('profile_id')
@ -121,8 +181,9 @@ class StoryController extends Controller
->get();
} else {
$db = Story::with('profile')
->whereActive(true)
->whereIn('profile_id', $following)
->where('expires_at', '>', now())
->where('created_at', '>', now()->subDay())
->orderByDesc('expires_at')
->groupBy('profile_id')
->take(9)
@ -158,6 +219,7 @@ class StoryController extends Controller
}
$stories = Story::whereProfileId($profile->id)
->whereActive(true)
->orderBy('expires_at', 'desc')
->where('expires_at', '>', now())
->when(!$publicOnly, function($query, $publicOnly) {
@ -187,6 +249,7 @@ class StoryController extends Controller
$authed = $request->user()->profile;
$story = Story::with('profile')
->whereActive(true)
->where('expires_at', '>', now())
->findOrFail($id);
@ -198,11 +261,11 @@ class StoryController extends Controller
}
abort_if(!$publicOnly, 403);
$res = [
'id' => (string) $story->id,
'type' => Str::endsWith($story->path, '.mp4') ? 'video' :'photo',
'length' => 3,
'length' => 10,
'src' => url(Storage::url($story->path)),
'preview' => null,
'link' => null,
@ -227,6 +290,7 @@ class StoryController extends Controller
}
$stories = Story::whereProfileId($profile->id)
->whereActive(true)
->orderBy('expires_at')
->where('expires_at', '>', now())
->when(!$publicOnly, function($query, $publicOnly) {
@ -237,7 +301,7 @@ class StoryController extends Controller
return [
'id' => $s->id,
'type' => Str::endsWith($s->path, '.mp4') ? 'video' :'photo',
'length' => 3,
'length' => 10,
'src' => url(Storage::url($s->path)),
'preview' => null,
'link' => null,
@ -272,19 +336,21 @@ class StoryController extends Controller
'id' => 'required|integer|min:1|exists:stories',
]);
$id = $request->input('id');
$authed = $request->user()->profile;
$story = Story::with('profile')
->where('expires_at', '>', now())
->orderByDesc('expires_at')
->findOrFail($id);
$profile = $story->profile;
if($story->profile_id == $authed->id) {
$publicOnly = true;
} else {
$publicOnly = (bool) $profile->followedBy($authed);
return [];
}
$publicOnly = (bool) $profile->followedBy($authed);
abort_if(!$publicOnly, 403);
StoryView::firstOrCreate([
@ -292,6 +358,9 @@ class StoryController extends Controller
'profile_id' => $authed->id
]);
$story->view_count = $story->view_count + 1;
$story->save();
return ['code' => 200];
}
@ -300,6 +369,7 @@ class StoryController extends Controller
abort_if(!config('instance.stories.enabled') || !$request->user(), 404);
$res = (bool) Story::whereProfileId($id)
->whereActive(true)
->where('expires_at', '>', now())
->count();
@ -312,6 +382,7 @@ class StoryController extends Controller
$profile = $request->user()->profile;
$stories = Story::whereProfileId($profile->id)
->whereActive(true)
->orderBy('expires_at')
->where('expires_at', '>', now())
->get()
@ -346,7 +417,7 @@ class StoryController extends Controller
public function compose(Request $request)
{
abort_if(!config('instance.stories.enabled') || !$request->user(), 404);
return view('stories.compose');
}

View File

@ -12,116 +12,120 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
use App\{
ImportJob,
ImportData,
Media,
Profile,
Status,
ImportJob,
ImportData,
Media,
Profile,
Status,
};
class ImportInstagram implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $import;
/**
* Delete the job if its models no longer exist.
*
* @var bool
*/
public $deleteWhenMissingModels = true;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(ImportJob $import)
{
$this->import = $import;
}
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if(config('pixelfed.import.instagram.enabled') != true) {
return;
}
protected $import;
$job = ImportJob::findOrFail($this->import->id);
$profile = Profile::findOrFail($job->profile_id);
$user = $profile->user;
$json = $job->mediaJson();
$collection = array_reverse($json['photos']);
$files = $job->files;
$monthHash = hash('sha1', date('Y').date('m'));
$userHash = hash('sha1', $user->id . (string) $user->created_at);
$fs = new Filesystem;
/**
* Delete the job if its models no longer exist.
*
* @var bool
*/
public $deleteWhenMissingModels = true;
foreach($collection as $import)
{
$caption = $import['caption'];
try {
$min = Carbon::create(2010, 10, 6, 0, 0, 0);
$taken_at = Carbon::parse($import['taken_at']);
if(!$min->lt($taken_at)) {
$taken_at = Carbon::now();
}
} catch (Exception $e) {
}
$filename = last( explode('/', $import['path']) );
$importData = ImportData::whereJobId($job->id)
->whereOriginalName($filename)
->first();
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(ImportJob $import)
{
$this->import = $import;
}
if(empty($importData) || is_file(storage_path("app/$importData->path")) == false) {
continue;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if(config('pixelfed.import.instagram.enabled') != true) {
return;
}
DB::transaction(function() use(
$fs, $job, $profile, $caption, $taken_at, $filename,
$monthHash, $userHash, $importData
) {
$status = new Status();
$status->profile_id = $profile->id;
$status->caption = strip_tags($caption);
$status->is_nsfw = false;
$status->type = 'photo';
$status->scope = 'unlisted';
$status->visibility = 'unlisted';
$status->created_at = $taken_at;
$status->save();
$job = ImportJob::findOrFail($this->import->id);
$profile = Profile::findOrFail($job->profile_id);
$user = $profile->user;
$json = $job->mediaJson();
$collection = array_reverse($json['photos']);
$files = $job->files;
$monthHash = hash('sha1', date('Y').date('m'));
$userHash = hash('sha1', $user->id . (string) $user->created_at);
$fs = new Filesystem;
foreach($collection as $import)
{
$caption = $import['caption'];
try {
$min = Carbon::create(2010, 10, 6, 0, 0, 0);
$taken_at = Carbon::parse($import['taken_at']);
if(!$min->lt($taken_at)) {
$taken_at = Carbon::now();
}
} catch (Exception $e) {
}
$filename = last( explode('/', $import['path']) );
$importData = ImportData::whereJobId($job->id)
->whereOriginalName($filename)
->first();
if(empty($importData) || is_file(storage_path("app/$importData->path")) == false) {
continue;
}
DB::transaction(function() use(
$fs, $job, $profile, $caption, $taken_at, $filename,
$monthHash, $userHash, $importData
) {
$status = new Status();
$status->profile_id = $profile->id;
$status->caption = strip_tags($caption);
$status->is_nsfw = false;
$status->type = 'photo';
$status->scope = 'unlisted';
$status->visibility = 'unlisted';
$status->created_at = $taken_at;
$status->save();
$path = storage_path("app/$importData->path");
$storagePath = "public/m/{$monthHash}/{$userHash}";
$newPath = "app/$storagePath/$filename";
$fs->move($path,storage_path($newPath));
$path = $newPath;
$hash = \hash_file('sha256', storage_path($path));
$media = new Media();
$media->status_id = $status->id;
$media->profile_id = $profile->id;
$media->user_id = $profile->user->id;
$media->media_path = "$storagePath/$filename";
$media->original_sha256 = $hash;
$media->size = $fs->size(storage_path($path));
$media->mime = $fs->mimeType(storage_path($path));
$media->filter_class = null;
$media->filter_name = null;
$media->order = 1;
$media->save();
ImageOptimize::dispatch($media);
});
}
$path = storage_path("app/$importData->path");
$storagePath = "public/m/{$monthHash}/{$userHash}";
$dir = "app/$storagePath";
if(!is_dir(storage_path($dir))) {
mkdir(storage_path($dir), 0755, true);
}
$newPath = "$dir/$filename";
$fs->move($path,storage_path($newPath));
$path = $newPath;
$hash = \hash_file('sha256', storage_path($path));
$media = new Media();
$media->status_id = $status->id;
$media->profile_id = $profile->id;
$media->user_id = $profile->user->id;
$media->media_path = "$storagePath/$filename";
$media->original_sha256 = $hash;
$media->size = $fs->size(storage_path($path));
$media->mime = $fs->mimeType(storage_path($path));
$media->filter_class = null;
$media->filter_name = null;
$media->order = 1;
$media->save();
ImageOptimize::dispatch($media);
});
}
$job->completed_at = Carbon::now();
$job->save();
}
$job->completed_at = Carbon::now();
$job->save();
}
}

View File

@ -176,12 +176,14 @@ class Helpers {
}
}
$bannedInstances = InstanceService::getBannedDomains();
if(in_array($host, $bannedInstances)) {
return false;
if(app()->environment() === 'production') {
$bannedInstances = InstanceService::getBannedDomains();
if(in_array($host, $bannedInstances)) {
return false;
}
}
if(in_array($host, $localhosts)) {
return false;
}

193
composer.lock generated
View File

@ -130,16 +130,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.178.6",
"version": "3.179.1",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "0aa83b522d5ffa794c02e7411af87a0e241a3082"
"reference": "f4f2c01b53f71379a1ed8ccccf4305949a69ccbe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0aa83b522d5ffa794c02e7411af87a0e241a3082",
"reference": "0aa83b522d5ffa794c02e7411af87a0e241a3082",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/f4f2c01b53f71379a1ed8ccccf4305949a69ccbe",
"reference": "f4f2c01b53f71379a1ed8ccccf4305949a69ccbe",
"shasum": ""
},
"require": {
@ -214,9 +214,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.178.6"
"source": "https://github.com/aws/aws-sdk-php/tree/3.179.1"
},
"time": "2021-04-19T18:13:17+00:00"
"time": "2021-04-29T18:17:25+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -656,40 +656,39 @@
},
{
"name": "doctrine/cache",
"version": "1.10.2",
"version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "13e3381b25847283a91948d04640543941309727"
"reference": "a9c1b59eba5a08ca2770a76eddb88922f504e8e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
"reference": "13e3381b25847283a91948d04640543941309727",
"url": "https://api.github.com/repos/doctrine/cache/zipball/a9c1b59eba5a08ca2770a76eddb88922f504e8e0",
"reference": "a9c1b59eba5a08ca2770a76eddb88922f504e8e0",
"shasum": ""
},
"require": {
"php": "~7.1 || ^8.0"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
"doctrine/common": ">2.2,<2.4",
"psr/cache": ">=3"
},
"require-dev": {
"alcaeus/mongo-php-adapter": "^1.1",
"doctrine/coding-standard": "^6.0",
"cache/integration-tests": "dev-master",
"doctrine/coding-standard": "^8.0",
"mongodb/mongodb": "^1.1",
"phpunit/phpunit": "^7.0",
"predis/predis": "~1.0"
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"predis/predis": "~1.0",
"psr/cache": "^1.0 || ^2.0",
"symfony/cache": "^4.4 || ^5.2"
},
"suggest": {
"alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
@ -736,7 +735,7 @@
],
"support": {
"issues": "https://github.com/doctrine/cache/issues",
"source": "https://github.com/doctrine/cache/tree/1.10.x"
"source": "https://github.com/doctrine/cache/tree/1.11.0"
},
"funding": [
{
@ -752,7 +751,7 @@
"type": "tidelift"
}
],
"time": "2020-07-07T18:54:01+00:00"
"time": "2021-04-13T14:46:17+00:00"
},
{
"name": "doctrine/dbal",
@ -1517,16 +1516,16 @@
},
{
"name": "fruitcake/laravel-cors",
"version": "v2.0.3",
"version": "v2.0.4",
"source": {
"type": "git",
"url": "https://github.com/fruitcake/laravel-cors.git",
"reference": "01de0fe5f71c70d1930ee9a80385f9cc28e0f63a"
"reference": "a8ccedc7ca95189ead0e407c43b530dc17791d6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fruitcake/laravel-cors/zipball/01de0fe5f71c70d1930ee9a80385f9cc28e0f63a",
"reference": "01de0fe5f71c70d1930ee9a80385f9cc28e0f63a",
"url": "https://api.github.com/repos/fruitcake/laravel-cors/zipball/a8ccedc7ca95189ead0e407c43b530dc17791d6a",
"reference": "a8ccedc7ca95189ead0e407c43b530dc17791d6a",
"shasum": ""
},
"require": {
@ -1539,8 +1538,8 @@
},
"require-dev": {
"laravel/framework": "^6|^7|^8",
"orchestra/testbench-dusk": "^4|^5|^6",
"phpunit/phpunit": "^6|^7|^8",
"orchestra/testbench-dusk": "^4|^5|^6|^7",
"phpunit/phpunit": "^6|^7|^8|^9",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
@ -1582,7 +1581,7 @@
],
"support": {
"issues": "https://github.com/fruitcake/laravel-cors/issues",
"source": "https://github.com/fruitcake/laravel-cors/tree/v2.0.3"
"source": "https://github.com/fruitcake/laravel-cors/tree/v2.0.4"
},
"funding": [
{
@ -1590,7 +1589,7 @@
"type": "github"
}
],
"time": "2020-10-22T13:57:20+00:00"
"time": "2021-04-26T11:24:25+00:00"
},
{
"name": "geerlingguy/ping",
@ -1823,16 +1822,16 @@
},
{
"name": "guzzlehttp/psr7",
"version": "1.8.1",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1"
"reference": "dc960a912984efb74d0a90222870c72c87f10c91"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1",
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
"reference": "dc960a912984efb74d0a90222870c72c87f10c91",
"shasum": ""
},
"require": {
@ -1892,9 +1891,9 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/1.8.1"
"source": "https://github.com/guzzle/psr7/tree/1.8.2"
},
"time": "2021-03-21T16:25:00+00:00"
"time": "2021-04-26T09:17:50+00:00"
},
{
"name": "intervention/image",
@ -2107,16 +2106,16 @@
},
{
"name": "laravel/framework",
"version": "v8.38.0",
"version": "v8.40.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "26a73532c54d2c090692bf2e3e64e449669053ba"
"reference": "a654897ad7f97aea9d7ef292803939798c4a02a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/26a73532c54d2c090692bf2e3e64e449669053ba",
"reference": "26a73532c54d2c090692bf2e3e64e449669053ba",
"url": "https://api.github.com/repos/laravel/framework/zipball/a654897ad7f97aea9d7ef292803939798c4a02a4",
"reference": "a654897ad7f97aea9d7ef292803939798c4a02a4",
"shasum": ""
},
"require": {
@ -2271,7 +2270,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2021-04-20T13:50:21+00:00"
"time": "2021-04-28T14:38:56+00:00"
},
{
"name": "laravel/helpers",
@ -2331,16 +2330,16 @@
},
{
"name": "laravel/horizon",
"version": "v5.7.5",
"version": "v5.7.6",
"source": {
"type": "git",
"url": "https://github.com/laravel/horizon.git",
"reference": "9bf0ef4873d9e52f58a9cd1de69dcdd98a5c4fe8"
"reference": "24ffd819df749ef11a4eb20e14150b671d4f5717"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/horizon/zipball/9bf0ef4873d9e52f58a9cd1de69dcdd98a5c4fe8",
"reference": "9bf0ef4873d9e52f58a9cd1de69dcdd98a5c4fe8",
"url": "https://api.github.com/repos/laravel/horizon/zipball/24ffd819df749ef11a4eb20e14150b671d4f5717",
"reference": "24ffd819df749ef11a4eb20e14150b671d4f5717",
"shasum": ""
},
"require": {
@ -2402,9 +2401,9 @@
],
"support": {
"issues": "https://github.com/laravel/horizon/issues",
"source": "https://github.com/laravel/horizon/tree/v5.7.5"
"source": "https://github.com/laravel/horizon/tree/v5.7.6"
},
"time": "2021-04-06T14:17:52+00:00"
"time": "2021-04-27T18:00:46+00:00"
},
{
"name": "laravel/passport",
@ -3913,16 +3912,16 @@
},
{
"name": "pbmedia/laravel-ffmpeg",
"version": "7.5.10",
"version": "7.5.11",
"source": {
"type": "git",
"url": "https://github.com/protonemedia/laravel-ffmpeg.git",
"reference": "d3c3b77e5de08d4038ebcb9e9d405d51ec8ce2f9"
"reference": "95b75f41edce12f513df12928b10b8f6949ffe56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/protonemedia/laravel-ffmpeg/zipball/d3c3b77e5de08d4038ebcb9e9d405d51ec8ce2f9",
"reference": "d3c3b77e5de08d4038ebcb9e9d405d51ec8ce2f9",
"url": "https://api.github.com/repos/protonemedia/laravel-ffmpeg/zipball/95b75f41edce12f513df12928b10b8f6949ffe56",
"reference": "95b75f41edce12f513df12928b10b8f6949ffe56",
"shasum": ""
},
"require": {
@ -3934,7 +3933,7 @@
"illuminate/support": "^6.0|^7.0|^8.0",
"league/flysystem": "^1.0.34",
"php": "^7.3|^8.0",
"php-ffmpeg/php-ffmpeg": "^0.17.0|^0.18.0"
"php-ffmpeg/php-ffmpeg": "^0.18.0"
},
"require-dev": {
"league/flysystem-memory": "^1.0",
@ -3986,7 +3985,7 @@
],
"support": {
"issues": "https://github.com/protonemedia/laravel-ffmpeg/issues",
"source": "https://github.com/protonemedia/laravel-ffmpeg/tree/7.5.10"
"source": "https://github.com/protonemedia/laravel-ffmpeg/tree/7.5.11"
},
"funding": [
{
@ -3994,7 +3993,7 @@
"type": "github"
}
],
"time": "2021-03-31T14:18:52+00:00"
"time": "2021-04-25T20:47:01+00:00"
},
{
"name": "php-ffmpeg/php-ffmpeg",
@ -5315,16 +5314,16 @@
},
{
"name": "spatie/image-optimizer",
"version": "1.3.2",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/image-optimizer.git",
"reference": "6aa170eb292758553d332efee5e0c3977341080c"
"reference": "c22202fdd57856ed18a79cfab522653291a6e96a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/image-optimizer/zipball/6aa170eb292758553d332efee5e0c3977341080c",
"reference": "6aa170eb292758553d332efee5e0c3977341080c",
"url": "https://api.github.com/repos/spatie/image-optimizer/zipball/c22202fdd57856ed18a79cfab522653291a6e96a",
"reference": "c22202fdd57856ed18a79cfab522653291a6e96a",
"shasum": ""
},
"require": {
@ -5363,9 +5362,9 @@
],
"support": {
"issues": "https://github.com/spatie/image-optimizer/issues",
"source": "https://github.com/spatie/image-optimizer/tree/1.3.2"
"source": "https://github.com/spatie/image-optimizer/tree/1.4.0"
},
"time": "2020-11-28T12:37:58+00:00"
"time": "2021-04-22T06:17:27+00:00"
},
{
"name": "spatie/laravel-backup",
@ -8287,16 +8286,16 @@
"packages-dev": [
{
"name": "brianium/paratest",
"version": "v6.2.0",
"version": "v6.3.0",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "9a94366983ce32c7724fc92e3b544327d4adb9be"
"reference": "268d5b2b4237c0abf76c4aa9633ad8580be01e1e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/9a94366983ce32c7724fc92e3b544327d4adb9be",
"reference": "9a94366983ce32c7724fc92e3b544327d4adb9be",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/268d5b2b4237c0abf76c4aa9633ad8580be01e1e",
"reference": "268d5b2b4237c0abf76c4aa9633ad8580be01e1e",
"shasum": ""
},
"require": {
@ -8305,28 +8304,28 @@
"ext-reflection": "*",
"ext-simplexml": "*",
"php": "^7.3 || ^8.0",
"phpunit/php-code-coverage": "^9.2.5",
"phpunit/php-code-coverage": "^9.2.6",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-timer": "^5.0.3",
"phpunit/phpunit": "^9.5.1",
"phpunit/phpunit": "^9.5.4",
"sebastian/environment": "^5.1.3",
"symfony/console": "^4.4 || ^5.2",
"symfony/process": "^4.4 || ^5.2"
"symfony/console": "^4.4.21 || ^5.2.6",
"symfony/process": "^4.4.21 || ^5.2.4"
},
"require-dev": {
"doctrine/coding-standard": "^8.2.0",
"ekino/phpstan-banned-code": "^0.3.1",
"doctrine/coding-standard": "^9.0.0",
"ekino/phpstan-banned-code": "^0.4.0",
"ergebnis/phpstan-rules": "^0.15.3",
"ext-posix": "*",
"infection/infection": "^0.20.2",
"phpstan/phpstan": "^0.12.70",
"infection/infection": "^0.21.5",
"phpstan/phpstan": "^0.12.84",
"phpstan/phpstan-deprecation-rules": "^0.12.6",
"phpstan/phpstan-phpunit": "^0.12.17",
"phpstan/phpstan-phpunit": "^0.12.18",
"phpstan/phpstan-strict-rules": "^0.12.9",
"squizlabs/php_codesniffer": "^3.5.8",
"symfony/filesystem": "^5.2.2",
"squizlabs/php_codesniffer": "^3.6.0",
"symfony/filesystem": "^5.2.6",
"thecodingmachine/phpstan-strict-rules": "^0.12.1",
"vimeo/psalm": "^4.4.1"
"vimeo/psalm": "^4.7.1"
},
"bin": [
"bin/paratest"
@ -8347,8 +8346,12 @@
{
"name": "Brian Scaturro",
"email": "scaturrob@gmail.com",
"homepage": "http://brianscaturro.com",
"role": "Lead"
"role": "Developer"
},
{
"name": "Filippo Tessarotto",
"email": "zoeslam@gmail.com",
"role": "Developer"
}
],
"description": "Parallel testing for PHP",
@ -8361,9 +8364,19 @@
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
"source": "https://github.com/paratestphp/paratest/tree/v6.2.0"
"source": "https://github.com/paratestphp/paratest/tree/v6.3.0"
},
"time": "2021-01-29T15:25:31+00:00"
"funding": [
{
"url": "https://github.com/sponsors/Slamdunk",
"type": "github"
},
{
"url": "https://paypal.me/filippotessarotto",
"type": "paypal"
}
],
"time": "2021-04-27T09:24:27+00:00"
},
{
"name": "doctrine/instantiator",
@ -8501,16 +8514,16 @@
},
{
"name": "facade/ignition",
"version": "2.8.3",
"version": "2.8.4",
"source": {
"type": "git",
"url": "https://github.com/facade/ignition.git",
"reference": "a8201d51aae83addceaef9344592a3b068b5d64d"
"reference": "87fb348dab0ae1a7c206c3e902a5a44ba541742f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facade/ignition/zipball/a8201d51aae83addceaef9344592a3b068b5d64d",
"reference": "a8201d51aae83addceaef9344592a3b068b5d64d",
"url": "https://api.github.com/repos/facade/ignition/zipball/87fb348dab0ae1a7c206c3e902a5a44ba541742f",
"reference": "87fb348dab0ae1a7c206c3e902a5a44ba541742f",
"shasum": ""
},
"require": {
@ -8574,7 +8587,7 @@
"issues": "https://github.com/facade/ignition/issues",
"source": "https://github.com/facade/ignition"
},
"time": "2021-04-09T20:45:59+00:00"
"time": "2021-04-29T13:55:26+00:00"
},
{
"name": "facade/ignition-contracts",
@ -8631,16 +8644,16 @@
},
{
"name": "filp/whoops",
"version": "2.12.0",
"version": "2.12.1",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "d501fd2658d55491a2295ff600ae5978eaad7403"
"reference": "c13c0be93cff50f88bbd70827d993026821914dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/d501fd2658d55491a2295ff600ae5978eaad7403",
"reference": "d501fd2658d55491a2295ff600ae5978eaad7403",
"url": "https://api.github.com/repos/filp/whoops/zipball/c13c0be93cff50f88bbd70827d993026821914dd",
"reference": "c13c0be93cff50f88bbd70827d993026821914dd",
"shasum": ""
},
"require": {
@ -8690,7 +8703,7 @@
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.12.0"
"source": "https://github.com/filp/whoops/tree/2.12.1"
},
"funding": [
{
@ -8698,7 +8711,7 @@
"type": "github"
}
],
"time": "2021-03-30T12:00:00+00:00"
"time": "2021-04-25T12:00:00+00:00"
},
{
"name": "fzaninotto/faker",

View File

@ -2,7 +2,7 @@
return [
'description' => env('INSTANCE_DESCRIPTION', null),
'description' => env('INSTANCE_DESCRIPTION', 'Pixelfed - Photo sharing for everyone'),
'contact' => [
'enabled' => env('INSTANCE_CONTACT_FORM', false),
@ -18,7 +18,7 @@ return [
'is_public' => env('INSTANCE_PUBLIC_HASHTAGS', false)
],
],
'email' => env('INSTANCE_CONTACT_EMAIL'),
'timeline' => [
@ -72,4 +72,4 @@ return [
'org' => env('COVID_LABEL_ORG', 'visit the WHO website')
]
],
];
];

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddActiveToStoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('stories', function (Blueprint $table) {
$table->boolean('active')->nullable()->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('stories', function (Blueprint $table) {
$table->dropColumn('active');
});
}
}

4
public/css/app.css vendored

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/direct.js vendored

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

2
public/js/rempos.js vendored

File diff suppressed because one or more lines are too long

2
public/js/rempro.js vendored

File diff suppressed because one or more lines are too long

2
public/js/search.js vendored

File diff suppressed because one or more lines are too long

2
public/js/status.js vendored

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

2
public/js/vendor.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,33 +1,33 @@
{
"/js/manifest.js": "/js/manifest.js?id=7db827d654313dce4250",
"/js/vendor.js": "/js/vendor.js?id=1b5b575aa98fd0fc24cf",
"/js/vendor.js": "/js/vendor.js?id=4736aa0fc51d85c921c6",
"/js/ace.js": "/js/ace.js?id=11e5550a450fece75c33",
"/js/activity.js": "/js/activity.js?id=64252d7f9c17e958b8d2",
"/js/activity.js": "/js/activity.js?id=78cf5dd05ca4893a4fdf",
"/js/app.js": "/js/app.js?id=fdbdd51482b98e1324e8",
"/css/app.css": "/css/app.css?id=b5c4655696840986ee34",
"/css/appdark.css": "/css/appdark.css?id=490a8d77398d21d0dc73",
"/css/landing.css": "/css/landing.css?id=7c8976b687f37b0882ef",
"/css/app.css": "/css/app.css?id=b8654ac6bc3bc7bdc2b0",
"/css/appdark.css": "/css/appdark.css?id=896508476c4e64509930",
"/css/landing.css": "/css/landing.css?id=037442572722dee7e8f5",
"/css/quill.css": "/css/quill.css?id=711b2150d518816d6112",
"/js/collectioncompose.js": "/js/collectioncompose.js?id=37ac6f2e9cbcd035704f",
"/js/collections.js": "/js/collections.js?id=be6208c4ab7909ad8ebe",
"/js/collections.js": "/js/collections.js?id=334ce3168b07728da0ae",
"/js/components.js": "/js/components.js?id=56aa48f8042553148a78",
"/js/compose.js": "/js/compose.js?id=d629fc34a60b65f031ee",
"/js/compose.js": "/js/compose.js?id=bff6a5e310f64f3ad91d",
"/js/compose-classic.js": "/js/compose-classic.js?id=ee4ad4759a55261c429c",
"/js/developers.js": "/js/developers.js?id=f8efa9cb9101d403d6c2",
"/js/direct.js": "/js/direct.js?id=735c52376bc4f3ec102e",
"/js/direct.js": "/js/direct.js?id=3f9e2504bf670d3d7961",
"/js/discover.js": "/js/discover.js?id=b954935be21c33a6bafa",
"/js/hashtag.js": "/js/hashtag.js?id=2238ded8e17551cea303",
"/js/hashtag.js": "/js/hashtag.js?id=656b11ab6b4d1dd489ae",
"/js/loops.js": "/js/loops.js?id=4ae79e81965c4fd1ae66",
"/js/mode-dot.js": "/js/mode-dot.js?id=d54ad862baf30ee756f8",
"/js/network-timeline.js": "/js/network-timeline.js?id=82884f73719c6b3abd80",
"/js/profile.js": "/js/profile.js?id=f0e108062a688a576b64",
"/js/network-timeline.js": "/js/network-timeline.js?id=c153107849c4485772d0",
"/js/profile.js": "/js/profile.js?id=4ebaa85964697a2fac1a",
"/js/profile-directory.js": "/js/profile-directory.js?id=886f17ad7ab4b2e08019",
"/js/quill.js": "/js/quill.js?id=866b31b9b9540305751d",
"/js/rempos.js": "/js/rempos.js?id=4f815ad22faac9af33ac",
"/js/rempro.js": "/js/rempro.js?id=0497d022decb1f1a4ac6",
"/js/search.js": "/js/search.js?id=f5f6a6ee32db30d29c2d",
"/js/status.js": "/js/status.js?id=fe385b6698a7a6638964",
"/js/story-compose.js": "/js/story-compose.js?id=5e184aae1ae4cde04361",
"/js/rempos.js": "/js/rempos.js?id=26c37eace801046187bf",
"/js/rempro.js": "/js/rempro.js?id=99242973aa5063217944",
"/js/search.js": "/js/search.js?id=3ba14b0584cb9d408a3f",
"/js/status.js": "/js/status.js?id=ca27a10a8fb2d95f0956",
"/js/story-compose.js": "/js/story-compose.js?id=583a8c85bc7611ca48de",
"/js/theme-monokai.js": "/js/theme-monokai.js?id=8842103833ba4861bcfa",
"/js/timeline.js": "/js/timeline.js?id=a163af153c2c1cae34b7"
"/js/timeline.js": "/js/timeline.js?id=a71588fab24087b45576"
}

View File

@ -89,6 +89,7 @@
<a v-if="!pageLoading && (page > 1 && page <= 2) || (page == 1 && ids.length != 0) || page == 'cropPhoto'" class="font-weight-bold text-decoration-none" href="#" @click.prevent="nextPage">Next</a>
<a v-if="!pageLoading && page == 3" class="font-weight-bold text-decoration-none" href="#" @click.prevent="compose()">Post</a>
<a v-if="!pageLoading && page == 'addText'" class="font-weight-bold text-decoration-none" href="#" @click.prevent="composeTextPost()">Post</a>
<a v-if="!pageLoading && page == 'video-2'" class="font-weight-bold text-decoration-none" href="#" @click.prevent="compose()">Post</a>
</span>
</div>
</div>
@ -97,18 +98,20 @@
<div class="list-group list-group-flush">
<div
v-for="(item, index) in availableLicenses"
class="list-group-item cursor-pointer"
:class="{
'text-primary': licenseIndex === index,
class="list-group-item cursor-pointer"
:class="{
'text-primary': licenseIndex === index,
'font-weight-bold': licenseIndex === index
}"
}"
@click="toggleLicense(index)">
{{item.name}}
</div>
</div>
</div>
<div v-if="page == 'textOptions'" class="w-100 h-100" style="min-height: 280px;">
</div>
<div v-if="page == 'addText'" class="w-100 h-100" style="min-height: 280px;">
<div class="mt-2">
<div class="media px-3">
@ -148,10 +151,10 @@
<div class="media">
<div class="mr-3 align-items-center justify-content-center" style="display:inline-flex;width:40px;height:40px;border-radius: 100%;background-color: #008DF5">
<i class="fas fa-bolt text-white fa-lg"></i>
</div>
</div>
<div class="media-body text-left">
<p class="mb-0">
<span class="h5 mt-0 font-weight-bold text-primary">New Post</span>
<span class="h5 mt-0 font-weight-bold text-primary">New Post</span>
</p>
<p class="mb-0 text-muted">Share up to {{config.uploader.album_limit}} photos or videos</p>
</div>
@ -164,7 +167,7 @@
<div class="media">
<div class="mr-3 align-items-center justify-content-center" style="display:inline-flex;width:40px;height:40px;border-radius: 100%;border: 2px solid #008DF5">
<i class="far fa-edit text-primary fa-lg"></i>
</div>
</div>
<div class="media-body text-left">
<p class="mb-0">
<span class="h5 mt-0 font-weight-bold text-primary">New Text Post</span>
@ -183,10 +186,10 @@
<div class="media">
<div class="mr-3 align-items-center justify-content-center" style="display:inline-flex;width:40px;height:40px;border-radius: 100%;border: 2px solid #008DF5">
<i class="fas fa-history text-primary fa-lg"></i>
</div>
</div>
<div class="media-body text-left">
<p class="mb-0">
<span class="h5 mt-0 font-weight-bold text-primary">New Story</span>
<span class="h5 mt-0 font-weight-bold text-primary">New Story</span>
<sup class="float-right mt-2">
<span class="btn btn-outline-lighter p-1 btn-sm font-weight-bold py-0" style="font-size:10px;line-height: 0.6">BETA</span>
</sup>
@ -202,10 +205,10 @@
<div class="media">
<div class="mr-3 align-items-center justify-content-center" style="display:inline-flex;width:40px;height:40px;border-radius: 100%;border: 2px solid #008DF5">
<i class="fas fa-images text-primary fa-lg"></i>
</div>
</div>
<div class="media-body text-left">
<p class="mb-0">
<span class="h5 mt-0 font-weight-bold text-primary">New Collection</span>
<span class="h5 mt-0 font-weight-bold text-primary">New Collection</span>
<sup class="float-right mt-2">
<span class="btn btn-outline-lighter p-1 btn-sm font-weight-bold py-0" style="font-size:10px;line-height: 0.6">BETA</span>
</sup>
@ -216,7 +219,7 @@
</div>
</a>
<p class="py-3">
<a class="font-weight-bold" href="/site/help">Help</a>
</p>
@ -268,7 +271,7 @@
<div class="nav-link" style="display:block;width:300px;height:300px;" @click="carouselCursor = i">
<!-- <img :class="'d-block img-fluid w-100 ' + [m.filter_class?m.filter_class:'']" :src="m.url" :alt="m.description" :title="m.description"> -->
<span :class="[m.filter_class?m.filter_class:'']">
<span :class="'rounded border ' + [i == carouselCursor ? ' border-primary shadow':'']" :style="'display:block;padding:5px;width:100%;height:100%;background-image: url(' + m.url + ');background-size:cover;border-width:3px !important;'"></span>
</span>
</div>
@ -372,7 +375,7 @@
</div>
<div v-if="page == 'tagPeople'" class="w-100 h-100 p-3">
<autocomplete
<autocomplete
v-show="taggedUsernames.length < 10"
:search="tagSearch"
placeholder="@pixelfed"
@ -413,7 +416,7 @@
<div v-if="page == 'addLocation'" class="w-100 h-100 p-3">
<p class="mb-0">Add Location</p>
<autocomplete
<autocomplete
:search="locationSearch"
placeholder="Search locations ..."
aria-label="Search locations ..."
@ -501,21 +504,21 @@
<div class="list-group list-group-flush">
<div
v-if="!profile.locked"
class="list-group-item lead cursor-pointer"
:class="{ 'text-primary': visibility == 'public' }"
class="list-group-item lead cursor-pointer"
:class="{ 'text-primary': visibility == 'public' }"
@click="toggleVisibility('public')">
Public
</div>
<div
v-if="!profile.locked"
class="list-group-item lead cursor-pointer"
:class="{ 'text-primary': visibility == 'unlisted' }"
class="list-group-item lead cursor-pointer"
:class="{ 'text-primary': visibility == 'unlisted' }"
@click="toggleVisibility('unlisted')">
Unlisted
</div>
<div
class="list-group-item lead cursor-pointer"
:class="{ 'text-primary': visibility == 'private' }"
<div
class="list-group-item lead cursor-pointer"
:class="{ 'text-primary': visibility == 'private' }"
@click="toggleVisibility('private')">
Followers Only
</div>
@ -589,7 +592,7 @@
<span>{{media[carouselCursor].license ? media[carouselCursor].license.length : 0}}/140</span>
</p> -->
<select class="form-control" v-model="licenseIndex">
<option
<option
v-for="(item, index) in availableLicenses"
:value="index"
:selected="index === licenseIndex">
@ -606,6 +609,69 @@
</p>
</div>
<div v-if="page == 'video-2'" class="w-100 h-100">
<div v-if="video.title.length" class="border-bottom">
<div class="media p-3">
<img :src="media[0].url" width="100px" height="70px" :class="[media[0].filter_class?'mr-2 ' + media[0].filter_class:'mr-2']">
<div class="media-body">
<p class="font-weight-bold mb-1">{{video.title ? video.title.slice(0,70) : 'Untitled'}}</p>
<p class="mb-0 text-muted small">{{video.description ? video.description.slice(0,90) : 'No description'}}</p>
</div>
</div>
</div>
<div class="border-bottom d-flex justify-content-between px-4 mb-0 py-2 ">
<div>
<div class="text-dark ">Contains NSFW Media</div>
</div>
<div>
<div class="custom-control custom-switch" style="z-index: 9999;">
<input type="checkbox" class="custom-control-input" id="asnsfw" v-model="nsfw">
<label class="custom-control-label" for="asnsfw"></label>
</div>
</div>
</div>
<div class="border-bottom">
<p class="px-4 mb-0 py-2 cursor-pointer" @click="showLicenseCard()">Add license</p>
</div>
<div class="border-bottom">
<p class="px-4 mb-0 py-2">
<span>Audience</span>
<span class="float-right">
<a href="#" @click.prevent="showVisibilityCard()" class="btn btn-outline-secondary btn-sm small mr-3 mt-n1 disabled" style="font-size:10px;padding:3px;text-transform: uppercase" disabled>{{visibilityTag}}</a>
<a href="#" @click.prevent="showVisibilityCard()" class="text-decoration-none"><i class="fas fa-chevron-right fa-lg text-lighter"></i></a>
</span>
</p>
</div>
<div class="p-3">
<!-- <div class="card card-body shadow-none border d-flex justify-content-center align-items-center mb-3 p-5">
<div class="d-flex align-items-center">
<p class="mb-0 text-center">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</p>
<p class="ml-3 mb-0 text-center font-weight-bold">
Processing video
</p>
</div>
</div> -->
<div class="form-group">
<p class="small font-weight-bold text-muted mb-0">Title</p>
<input class="form-control" v-model="video.title" placeholder="Add a good title">
<p class="help-text mb-0 small text-muted">{{video.title.length}}/70</p>
</div>
<div class="form-group mb-0">
<p class="small font-weight-bold text-muted mb-0">Description</p>
<textarea class="form-control" v-model="video.description" placeholder="Add an optional description" maxlength="5000" rows="5"></textarea>
<p class="help-text mb-0 small text-muted">{{video.description.length}}/5000</p>
</div>
</div>
</div>
</div>
<!-- card-footers -->
@ -679,7 +745,7 @@ import VueTribute from 'vue-tribute'
export default {
components: {
components: {
VueCropper,
Autocomplete,
VueTribute
@ -700,12 +766,16 @@ export default {
carouselCursor: 0,
uploading: false,
uploadProgress: 100,
composeType: false,
mode: 'photo',
modes: [
'photo',
'video',
'plain'
],
page: 1,
composeState: 'publish',
visibility: 'public',
visibilityTag: 'Public',
nsfw: false,
place: false,
commentsDisabled: false,
optimizeMedia: true,
@ -810,15 +880,16 @@ export default {
name: "Attribution-NonCommercial-NoDerivs"
}
],
licenseIndex: 0
licenseIndex: 0,
video: {
title: '',
description: ''
}
}
},
beforeMount() {
this.fetchProfile();
if(this.config.uploader.media_types.includes('video/mp4') == false) {
this.composeType = 'post'
}
this.filters = window.App.util.filters;
},
@ -860,6 +931,7 @@ export default {
this.pageTitle = 'New Text Post';
this.page = 'addText';
this.textMode = true;
this.mode = 'text';
},
mediaWatcher() {
@ -910,7 +982,14 @@ export default {
self.media.push(e.data);
self.uploading = false;
setTimeout(function() {
self.page = 2;
// if(type === 'video/mp4') {
// self.pageTitle = 'Edit Video Details';
// self.mode = 'video';
// self.page = 'video-2';
// } else {
// self.page = 2;
// }
self.page = 3;
}, 300);
}).catch(function(e) {
switch(e.response.status) {
@ -951,7 +1030,7 @@ export default {
return;
}
let id = this.media[this.carouselCursor].id;
axios.delete('/api/compose/v0/media/delete', {
params: {
id: id
@ -1001,7 +1080,8 @@ export default {
place: this.place,
tagged: this.taggedUsernames,
optimize_media: this.optimizeMedia,
license: this.availableLicenses[this.licenseIndex].id
license: this.availableLicenses[this.licenseIndex].id,
video: this.video
};
axios.post('/api/compose/v0/publish', data)
.then(res => {
@ -1068,41 +1148,99 @@ export default {
},
closeModal() {
this.composeType = '';
$('#composeModal').modal('hide');
},
goBack() {
this.pageTitle = '';
switch(this.page) {
case 'addText':
this.page = 1;
switch(this.mode) {
case 'photo':
switch(this.page) {
case 'addText':
this.page = 1;
break;
case 'textOptions':
this.page = 'addText';
break;
case 'cropPhoto':
case 'editMedia':
this.page = 2;
break;
case 'tagPeopleHelp':
this.showTagCard();
break;
case 'licensePicker':
this.page = 3;
break;
case 'video-2':
this.page = 1;
break;
default:
this.namedPages.indexOf(this.page) != -1 ?
this.page = 3 : this.page--;
break;
}
break;
case 'textOptions':
this.page = 'addText';
break;
case 'video':
switch(this.page) {
case 'licensePicker':
this.page = 'video-2';
break;
case 'cropPhoto':
case 'editMedia':
this.page = 2;
break;
case 'video-2':
this.page = 'video-2';
break;
case 'tagPeopleHelp':
this.showTagCard();
break;
case 'licensePicker':
this.page = 3;
default:
this.page = 'video-2';
break;
}
break;
default:
this.namedPages.indexOf(this.page) != -1 ?
this.page = (this.textMode ? 'addText' : 3) :
(this.textMode ? 'addText' : this.page--);
switch(this.page) {
case 'addText':
this.page = 1;
break;
case 'textOptions':
this.page = 'addText';
break;
case 'cropPhoto':
case 'editMedia':
this.page = 2;
break;
case 'tagPeopleHelp':
this.showTagCard();
break;
case 'licensePicker':
this.page = 3;
break;
case 'video-2':
this.page = 1;
break;
default:
this.namedPages.indexOf(this.page) != -1 ?
this.page = (this.mode == 'text' ? 'addText' : 3) :
(this.mode == 'text' ? 'addText' : this.page--);
break;
}
break;
}
},
nextPage() {
@ -1115,7 +1253,7 @@ export default {
case 'cropPhoto':
this.pageLoading = true;
let self = this;
this.$refs.cropper.getCroppedCanvas({
this.$refs.cropper.getCroppedCanvas({
maxWidth: 4096,
maxHeight: 4096,
fillColor: '#fff',
@ -1199,8 +1337,22 @@ export default {
onSubmitLocation(result) {
this.place = result;
this.pageTitle = this.textMode ? 'New Text Post' : '';
this.page = (this.textMode ? 'addText' : 3);
switch(this.mode) {
case 'photo':
this.pageTitle = '';
this.page = 3;
break;
case 'video':
this.pageTitle = 'Edit Video Details';
this.page = 'video-2';
break;
case 'text':
this.pageTitle = 'New Text Post';
this.page = 'addText';
break;
}
return;
},
@ -1227,8 +1379,23 @@ export default {
}
this.visibility = state;
this.visibilityTag = tags[state];
this.pageTitle = '';
this.page = this.textMode ? 'addText' : 3;
switch(this.mode) {
case 'photo':
this.pageTitle = '';
this.page = 3;
break;
case 'video':
this.pageTitle = 'Edit Video Details';
this.page = 'video-2';
break;
case 'text':
this.pageTitle = 'New Text Post';
this.page = 'addText';
break;
}
},
showMediaDescriptionsCard() {
@ -1350,9 +1517,24 @@ export default {
toggleLicense(index) {
this.licenseIndex = index;
this.pageTitle = '';
this.page = 3;
switch(this.mode) {
case 'photo':
this.pageTitle = '';
this.page = 3;
break;
case 'video':
this.pageTitle = 'Edit Video Details';
this.page = 'video-2';
break;
case 'text':
this.pageTitle = 'New Text Post';
this.page = 'addText';
break;
}
},
}
}
</script>
</script>

View File

@ -1,60 +1,92 @@
<template>
<div class="container mt-2 mt-md-5">
<div class="container mt-2 mt-md-5 bg-black">
<input type="file" id="pf-dz" name="media" class="d-none file-input" v-bind:accept="config.mimes">
<span class="fixed-top text-right m-3 cursor-pointer" @click="navigateTo()">
<i class="fas fa-times fa-lg text-white"></i>
</span>
<div v-if="loaded" class="row">
<div class="col-12 col-md-6 offset-md-3">
<div class="col-12 col-md-6 offset-md-3 bg-dark rounded-lg">
<!-- 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>
<!-- <p v-if="loaded" class="font-weight-bold small text-uppercase text-muted">
<span>{{stories.length}} Active</span>
<span class="px-2">|</span>
<span>30K Views</span>
</p> -->
<div class="text-center flex-fill pt-3">
<p class="text-muted font-weight-light mb-1">
<i class="fas fa-history fa-5x"></i>
</p>
<p class="text-muted font-weight-bold mb-0">STORIES</p>
</div>
<div class="flex-fill py-4">
<div class="card w-100 shadow-none">
<div class="list-group">
<div class="card w-100 shadow-none bg-transparent">
<div class="list-group bg-transparent">
<!-- <a class="list-group-item text-center lead text-decoration-none text-dark" href="#">Camera</a> -->
<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</a>
<a class="list-group-item bg-transparent lead text-decoration-none text-light font-weight-bold border-light" href="#" @click.prevent="upload()">
<i class="fas fa-plus-square mr-2"></i>
Add to Story
</a>
<a v-if="stories.length" class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="#" @click.prevent="edit()">
<i class="far fa-clone mr-2"></i>
My Story
</a>
<a v-if="stories.length" class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="#" @click.prevent="viewMyStory()">
<i class="fas fa-history mr-2"></i>
View My Story
</a>
<!-- <a v-if="stories.length" class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="#" @click.prevent="edit()">
<i class="fas fa-network-wired mr-1"></i>
Audience
</a> -->
<!-- <a v-if="stories.length" class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="#" @click.prevent="edit()">
<i class="far fa-chart-bar mr-2"></i>
Stats
</a> -->
<!-- <a class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="#" @click.prevent="edit()">
<i class="far fa-folder mr-2"></i>
Archived
</a> -->
<!-- <a class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="#" @click.prevent="edit()">
<i class="far fa-question-circle mr-2"></i>
Help
</a> -->
<a class="list-group-item bg-transparent lead text-decoration-none text-lighter font-weight-bold border-muted" href="/">
<i class="fas fa-arrow-left mr-2"></i>
Go back
</a>
<!-- <a class="list-group-item text-center lead text-decoration-none text-dark" href="#">Options</a> -->
</div>
</div>
</div>
<div class="text-center flex-fill">
<p class="text-lighter small text-uppercase">
<!-- <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>
</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 v-if="page == 'crop'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center" style="height: 90vh;">
<div class="text-center py-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>
<button class="btn btn-outline-lighter btn-sm py-1 px-md-3" @click="deleteCurrentStory()"><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 class="">
<p class="text-muted font-weight-light mb-1">
<i class="fas fa-history fa-5x"></i>
</p>
<p class="text-muted font-weight-bold mb-0">STORIES</p>
</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>
<button class="btn btn-primary btn-sm py-1 px-md-3" @click="performCrop()">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"
ref="croppa"
:relativeZoom="cropper.zoom"
:aspectRatio="cropper.aspectRatio"
:viewMode="cropper.viewMode"
@ -66,20 +98,11 @@
</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="h3 mb-0 text-light">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>
@ -88,30 +111,63 @@
<!-- UPLOADING -->
<div v-if="page == 'uploading'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center align-items-center" style="height: 90vh;">
<p v-if="uploadProgress != 100" class="display-4 mb-0">Uploading {{uploadProgress}}%</p>
<p v-else class="display-4 mb-0">Publishing Story</p>
<p v-if="uploadProgress != 100" class="display-4 mb-0 text-muted">Uploading {{uploadProgress}}%</p>
<p v-else class="display-4 mb-0 text-muted">Processing ...</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>
<!-- CROPPING -->
<div v-if="page == 'cropping'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center align-items-center" style="height: 90vh;">
<p class="display-4 mb-0 text-muted">Cropping ...</p>
</div>
<!-- PREVIEW -->
<div v-if="page == 'preview'" class="card card-body bg-transparent border-0 shadow-none d-flex justify-content-center align-items-center" style="height: 90vh;">
<div>
<div class="form-group">
<label for="durationSlider" class="text-light lead font-weight-bold">Story Duration</label>
<input type="range" class="custom-range" min="3" max="10" id="durationSlider" v-model="duration">
<p class="help-text text-center">
<span class="text-light">{{duration}} seconds</span>
</p>
</div>
<hr class="my-3">
<a class="btn btn-primary btn-block px-5 font-weight-bold my-3" href="#" @click.prevent="shareStoryToFollowers()">
Share Story with followers
</a>
<a class="btn btn-outline-muted btn-block px-5 font-weight-bold" href="/" @click.prevent="deleteCurrentStory()">
Cancel
</a>
</div>
<div class="flex-fill py-5">
<div class="card w-100 shadow-none" style="max-height: 500px; overflow-y: auto">
<!-- <a class="btn btn-outline-secondary btn-block px-5 font-weight-bold" href="#">
Share Story with everyone
</a> -->
</div>
<!-- EDIT -->
<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">
<p class="text-muted font-weight-light mb-1">
<i class="fas fa-history fa-5x"></i>
</p>
<p class="text-muted font-weight-bold mb-0">STORIES</p>
</div>
<div class="flex-fill py-4">
<p class="lead font-weight-bold text-lighter">My Stories</p>
<div class="card w-100 shadow-none bg-transparent" style="max-height: 50vh; overflow-y: scroll">
<div class="list-group">
<div v-for="(story, index) in stories" class="list-group-item text-center text-dark" href="#">
<div v-for="(story, index) in stories" class="list-group-item bg-transparent text-center border-muted text-lighter" href="#">
<div class="media align-items-center">
<div class="mr-3 cursor-pointer" @click="showLightbox(story)">
<img :src="story.src" class="img-fluid" width="70px" height="70px">
<p class="small text-muted text-center mb-0">(expand)</p>
<img :src="story.src" class="rounded-circle border" width="40px" height="40px" style="object-fit: cover;">
</div>
<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 class="media-body text-left">
<p class="mb-0 text-muted font-weight-bold"><span>{{story.created_ago}} ago</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 class="flex-grow-1 text-right">
<button @click="deleteStory(story, index)" class="btn btn-link btn-sm">
<i class="fas fa-trash-alt fa-lg text-muted"></i>
</button>
</div>
</div>
</div>
@ -119,7 +175,7 @@
</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>
<a class="btn btn-outline-secondary btn-block px-5 font-weight-bold" href="/i/stories/new" @click.prevent="goBack()">Go back</a>
</div>
</div>
</div>
@ -130,18 +186,24 @@
hide-header
hide-footer
centered
size="lg"
body-class="p-0"
size="md"
class="bg-transparent"
body-class="p-0 bg-transparent"
>
<div v-if="lightboxMedia" class="w-100 h-100">
<img :src="lightboxMedia.url" style="max-height: 100%; max-width: 100%">
<div v-if="lightboxMedia" class="w-100 h-100 bg-transparent">
<img :src="lightboxMedia.url" style="max-height: 90vh; width: 100%; object-fit: contain;">
</div>
</b-modal>
</div>
</template>
<style type="text/css" scoped>
<style type="text/css">
.bg-black {
background-color: #262626;
}
#lightbox .modal-content {
background: transparent;
}
</style>
<script type="text/javascript">
@ -149,7 +211,7 @@
import VueCropper from 'vue-cropperjs';
import 'cropperjs/dist/cropper.css';
export default {
components: {
components: {
VueCropper,
VueTimeago
},
@ -182,12 +244,15 @@
zoom: null
},
mediaUrl: null,
mediaId: null,
stories: [],
lightboxMedia: false,
duration: 3
};
},
mounted() {
$('body').addClass('bg-black');
this.mediaWatcher();
axios.get('/api/stories/v0/fetch/' + this.profileId)
.then(res => {
@ -241,19 +306,22 @@
}
};
io.value = null;
axios.post('/api/stories/v0/add', form, xhrConfig)
.then(function(e) {
self.uploadProgress = 100;
self.uploading = false;
window.location.href = '/i/my/story';
self.mediaUrl = e.data.media_url;
self.mediaId = e.data.media_id;
self.page = e.data.media_type === 'video' ? 'preview' : 'crop';
// window.location.href = '/i/my/story';
}).catch(function(e) {
self.uploading = false;
io.value = null;
let msg = e.response.data.message ? e.response.data.message : 'Something went wrong.'
swal('Oops!', msg, 'warning');
self.page = 'error';
});
io.value = null;
self.uploadProgress = 0;
});
},
@ -286,8 +354,50 @@
window.location.href = '/i/stories/new';
}
});
},
navigateTo(path = '/') {
window.location.href = path;
},
goBack() {
this.page = 'landing';
},
performCrop() {
this.page = 'cropping';
let data = this.$refs.croppa.getData();
axios.post('/api/stories/v0/crop', {
media_id: this.mediaId,
width: data.width,
height: data.height,
x: data.x,
y: data.y
}).then(res => {
this.page = 'preview';
});
},
deleteCurrentStory() {
let story = {
id: this.mediaId
};
this.deleteStory(story);
this.page = 'landing';
},
shareStoryToFollowers() {
axios.post('/api/stories/v0/publish', {
media_id: this.mediaId,
duration: this.duration
}).then(res => {
window.location.href = '/i/my/story?id=' + this.mediaId;
})
},
viewMyStory() {
window.location.href = '/i/my/story';
}
}
}
</script>
</script>

View File

@ -230,6 +230,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::delete('v0/delete/{id}', 'StoryController@apiV1Delete')->middleware('throttle:maxStoryDeletePerDay,1440');
Route::get('v0/me', 'StoryController@apiV1Me');
Route::get('v0/item/{id}', 'StoryController@apiV1Item');
Route::post('v0/crop', 'StoryController@cropPhoto');
Route::post('v0/publish', 'StoryController@publishStory');
});
});