|
@@ -4,337 +4,106 @@ namespace App\Http\Controllers;
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
use Illuminate\Support\Str;
|
|
|
+use App\DirectMessage;
|
|
|
+use App\Follower;
|
|
|
+use App\Notification;
|
|
|
use App\Media;
|
|
|
use App\Profile;
|
|
|
+use App\Status;
|
|
|
use App\Story;
|
|
|
use App\StoryView;
|
|
|
+use App\Services\PollService;
|
|
|
+use App\Services\ProfileService;
|
|
|
use App\Services\StoryService;
|
|
|
use Cache, Storage;
|
|
|
use Image as Intervention;
|
|
|
+use App\Services\AccountService;
|
|
|
use App\Services\FollowerService;
|
|
|
use App\Services\MediaPathService;
|
|
|
use FFMpeg;
|
|
|
use FFMpeg\Coordinate\Dimension;
|
|
|
use FFMpeg\Format\Video\X264;
|
|
|
+use League\Fractal\Manager;
|
|
|
+use League\Fractal\Serializer\ArraySerializer;
|
|
|
+use League\Fractal\Resource\Item;
|
|
|
+use App\Transformer\ActivityPub\Verb\StoryVerb;
|
|
|
+use App\Jobs\StoryPipeline\StoryViewDeliver;
|
|
|
|
|
|
-class StoryController extends Controller
|
|
|
+class StoryController extends StoryComposeController
|
|
|
{
|
|
|
- public function apiV1Add(Request $request)
|
|
|
+ public function recent(Request $request)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $this->validate($request, [
|
|
|
- 'file' => function() {
|
|
|
- return [
|
|
|
- 'required',
|
|
|
- 'mimes:image/jpeg,image/png,video/mp4',
|
|
|
- 'max:' . config_cache('pixelfed.max_photo_size'),
|
|
|
- ];
|
|
|
- },
|
|
|
- ]);
|
|
|
-
|
|
|
- $user = $request->user();
|
|
|
-
|
|
|
- if(Story::whereProfileId($user->profile_id)->where('expires_at', '>', now())->count() >= Story::MAX_PER_DAY) {
|
|
|
- abort(400, 'You have reached your limit for new Stories today.');
|
|
|
- }
|
|
|
-
|
|
|
- $photo = $request->file('file');
|
|
|
- $path = $this->storePhoto($photo, $user);
|
|
|
-
|
|
|
- $story = new Story();
|
|
|
- $story->duration = 3;
|
|
|
- $story->profile_id = $user->profile_id;
|
|
|
- $story->type = Str::endsWith($photo->getMimeType(), 'mp4') ? 'video' :'photo';
|
|
|
- $story->mime = $photo->getMimeType();
|
|
|
- $story->path = $path;
|
|
|
- $story->local = true;
|
|
|
- $story->size = $photo->getSize();
|
|
|
- $story->save();
|
|
|
-
|
|
|
- $url = $story->path;
|
|
|
-
|
|
|
- if($story->type === 'video') {
|
|
|
- $video = FFMpeg::open($path);
|
|
|
- $width = $video->getVideoStream()->get('width');
|
|
|
- $height = $video->getVideoStream()->get('height');
|
|
|
-
|
|
|
-
|
|
|
- if($width !== 1080 || $height !== 1920) {
|
|
|
- Storage::delete($story->path);
|
|
|
- $story->delete();
|
|
|
- abort(422, 'Invalid video dimensions, must be 1080x1920');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully added',
|
|
|
- 'media_id' => (string) $story->id,
|
|
|
- 'media_url' => url(Storage::url($url)) . '?v=' . time(),
|
|
|
- 'media_type' => $story->type
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- protected function storePhoto($photo, $user)
|
|
|
- {
|
|
|
- $mimes = explode(',', config_cache('pixelfed.media_types'));
|
|
|
- if(in_array($photo->getMimeType(), [
|
|
|
- 'image/jpeg',
|
|
|
- 'image/png',
|
|
|
- 'video/mp4'
|
|
|
- ]) == false) {
|
|
|
- abort(400, 'Invalid media type');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- $storagePath = MediaPathService::story($user->profile);
|
|
|
- $path = $photo->store($storagePath);
|
|
|
- if(in_array($photo->getMimeType(), ['image/jpeg','image/png'])) {
|
|
|
- $fpath = storage_path('app/' . $path);
|
|
|
- $img = Intervention::make($fpath);
|
|
|
- $img->orientate();
|
|
|
- $img->save($fpath, config_cache('pixelfed.image_quality'));
|
|
|
- $img->destroy();
|
|
|
- }
|
|
|
- return $path;
|
|
|
- }
|
|
|
-
|
|
|
- public function cropPhoto(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('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.');
|
|
|
- }
|
|
|
-
|
|
|
- if($story->type === 'photo') {
|
|
|
- $img = Intervention::make($path);
|
|
|
- $img->crop($width, $height, $x, $y);
|
|
|
- $img->resize(1080, 1920, function ($constraint) {
|
|
|
- $constraint->aspectRatio();
|
|
|
- });
|
|
|
- $img->save($path, config_cache('pixelfed.image_quality'));
|
|
|
- }
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully cropped',
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function publishStory(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('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()->addMinutes(1450);
|
|
|
- $story->save();
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully published',
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function apiV1Delete(Request $request, $id)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $user = $request->user();
|
|
|
-
|
|
|
- $story = Story::whereProfileId($user->profile_id)
|
|
|
- ->findOrFail($id);
|
|
|
-
|
|
|
- if(Storage::exists($story->path) == true) {
|
|
|
- Storage::delete($story->path);
|
|
|
- }
|
|
|
-
|
|
|
- $story->delete();
|
|
|
-
|
|
|
- return [
|
|
|
- 'code' => 200,
|
|
|
- 'msg' => 'Successfully deleted'
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- public function apiV1Recent(Request $request)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $profile = $request->user()->profile;
|
|
|
- $following = $profile->following->pluck('id')->toArray();
|
|
|
-
|
|
|
- if(config('database.default') == 'pgsql') {
|
|
|
- $db = Story::with('profile')
|
|
|
- ->whereActive(true)
|
|
|
- ->whereIn('profile_id', $following)
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->distinct('profile_id')
|
|
|
- ->take(9)
|
|
|
- ->get();
|
|
|
- } else {
|
|
|
- $db = Story::with('profile')
|
|
|
- ->whereActive(true)
|
|
|
- ->whereIn('profile_id', $following)
|
|
|
- ->where('created_at', '>', now()->subDay())
|
|
|
- ->orderByDesc('expires_at')
|
|
|
- ->groupBy('profile_id')
|
|
|
- ->take(9)
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+
|
|
|
+ $s = Story::select('stories.*', 'followers.following_id')
|
|
|
+ ->leftJoin('followers', 'followers.following_id', 'stories.profile_id')
|
|
|
+ ->where('followers.profile_id', $pid)
|
|
|
+ ->where('stories.active', true)
|
|
|
+ ->groupBy('followers.following_id')
|
|
|
+ ->orderByDesc('id')
|
|
|
->get();
|
|
|
- }
|
|
|
|
|
|
- $stories = $db->map(function($s, $k) {
|
|
|
+ $res = $s->map(function($s) use($pid) {
|
|
|
+ $profile = AccountService::get($s->profile_id);
|
|
|
+ $url = $profile['local'] ? url("/stories/{$profile['username']}") :
|
|
|
+ url("/i/rs/{$profile['id']}");
|
|
|
return [
|
|
|
- 'id' => (string) $s->id,
|
|
|
- 'photo' => $s->profile->avatarUrl(),
|
|
|
- 'name' => $s->profile->username,
|
|
|
- 'link' => $s->profile->url(),
|
|
|
- 'lastUpdated' => (int) $s->created_at->format('U'),
|
|
|
- 'seen' => $s->seen(),
|
|
|
- 'items' => [],
|
|
|
- 'pid' => (string) $s->profile->id
|
|
|
+ 'pid' => $profile['id'],
|
|
|
+ 'avatar' => $profile['avatar'],
|
|
|
+ 'local' => $profile['local'],
|
|
|
+ 'username' => $profile['acct'],
|
|
|
+ 'url' => $url,
|
|
|
+ 'seen' => StoryService::hasSeen($pid, StoryService::latest($s->profile_id)),
|
|
|
+ 'sid' => $s->id
|
|
|
];
|
|
|
- });
|
|
|
-
|
|
|
- return response()->json($stories, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
- }
|
|
|
-
|
|
|
- public function apiV1Fetch(Request $request, $id)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $authed = $request->user()->profile;
|
|
|
- $profile = Profile::findOrFail($id);
|
|
|
- if($id == $authed->id) {
|
|
|
- $publicOnly = true;
|
|
|
- } else {
|
|
|
- $publicOnly = (bool) $profile->followedBy($authed);
|
|
|
- }
|
|
|
-
|
|
|
- $stories = Story::whereProfileId($profile->id)
|
|
|
- ->whereActive(true)
|
|
|
- ->orderBy('expires_at', 'desc')
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->when(!$publicOnly, function($query, $publicOnly) {
|
|
|
- return $query->wherePublic(true);
|
|
|
})
|
|
|
- ->get()
|
|
|
- ->map(function($s, $k) {
|
|
|
- return [
|
|
|
- 'id' => (string) $s->id,
|
|
|
- 'type' => Str::endsWith($s->path, '.mp4') ? 'video' :'photo',
|
|
|
- 'length' => 3,
|
|
|
- 'src' => url(Storage::url($s->path)),
|
|
|
- 'preview' => null,
|
|
|
- 'link' => null,
|
|
|
- 'linkText' => null,
|
|
|
- 'time' => $s->created_at->format('U'),
|
|
|
- 'expires_at' => (int) $s->expires_at->format('U'),
|
|
|
- 'created_ago' => $s->created_at->diffForHumans(null, true, true),
|
|
|
- 'seen' => $s->seen()
|
|
|
- ];
|
|
|
- })->toArray();
|
|
|
- return response()->json($stories, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
- }
|
|
|
-
|
|
|
- public function apiV1Item(Request $request, $id)
|
|
|
- {
|
|
|
- abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
-
|
|
|
- $authed = $request->user()->profile;
|
|
|
- $story = Story::with('profile')
|
|
|
- ->whereActive(true)
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->findOrFail($id);
|
|
|
-
|
|
|
- $profile = $story->profile;
|
|
|
- if($story->profile_id == $authed->id) {
|
|
|
- $publicOnly = true;
|
|
|
- } else {
|
|
|
- $publicOnly = (bool) $profile->followedBy($authed);
|
|
|
- }
|
|
|
-
|
|
|
- abort_if(!$publicOnly, 403);
|
|
|
-
|
|
|
- $res = [
|
|
|
- 'id' => (string) $story->id,
|
|
|
- 'type' => Str::endsWith($story->path, '.mp4') ? 'video' :'photo',
|
|
|
- 'length' => 10,
|
|
|
- 'src' => url(Storage::url($story->path)),
|
|
|
- 'preview' => null,
|
|
|
- 'link' => null,
|
|
|
- 'linkText' => null,
|
|
|
- 'time' => $story->created_at->format('U'),
|
|
|
- 'expires_at' => (int) $story->expires_at->format('U'),
|
|
|
- 'seen' => $story->seen()
|
|
|
- ];
|
|
|
+ ->sortBy('seen')
|
|
|
+ ->values();
|
|
|
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
}
|
|
|
|
|
|
- public function apiV1Profile(Request $request, $id)
|
|
|
+ public function profile(Request $request, $id)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
- $authed = $request->user()->profile;
|
|
|
+ $authed = $request->user()->profile_id;
|
|
|
$profile = Profile::findOrFail($id);
|
|
|
- if($id == $authed->id) {
|
|
|
- $publicOnly = true;
|
|
|
- } else {
|
|
|
- $publicOnly = (bool) $profile->followedBy($authed);
|
|
|
+
|
|
|
+ if($authed != $profile->id && !FollowerService::follows($authed, $profile->id)) {
|
|
|
+ return [];
|
|
|
}
|
|
|
|
|
|
$stories = Story::whereProfileId($profile->id)
|
|
|
->whereActive(true)
|
|
|
->orderBy('expires_at')
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->when(!$publicOnly, function($query, $publicOnly) {
|
|
|
- return $query->wherePublic(true);
|
|
|
- })
|
|
|
->get()
|
|
|
- ->map(function($s, $k) {
|
|
|
- return [
|
|
|
- 'id' => $s->id,
|
|
|
- 'type' => Str::endsWith($s->path, '.mp4') ? 'video' :'photo',
|
|
|
- 'length' => 10,
|
|
|
+ ->map(function($s, $k) use($authed) {
|
|
|
+ $seen = StoryService::hasSeen($authed, $s->id);
|
|
|
+ $res = [
|
|
|
+ 'id' => (string) $s->id,
|
|
|
+ 'type' => $s->type,
|
|
|
+ 'duration' => $s->duration,
|
|
|
'src' => url(Storage::url($s->path)),
|
|
|
- 'preview' => null,
|
|
|
- 'link' => null,
|
|
|
- 'linkText' => null,
|
|
|
- 'time' => $s->created_at->format('U'),
|
|
|
- 'expires_at' => (int) $s->expires_at->format('U'),
|
|
|
- 'seen' => $s->seen()
|
|
|
+ 'created_at' => $s->created_at->toAtomString(),
|
|
|
+ 'expires_at' => $s->expires_at->toAtomString(),
|
|
|
+ 'view_count' => ($authed == $s->profile_id) ? ($s->view_count ?? 0) : null,
|
|
|
+ 'seen' => $seen,
|
|
|
+ 'progress' => $seen ? 100 : 0,
|
|
|
+ 'can_reply' => (bool) $s->can_reply,
|
|
|
+ 'can_react' => (bool) $s->can_react
|
|
|
];
|
|
|
+
|
|
|
+ if($s->type == 'poll') {
|
|
|
+ $res['question'] = json_decode($s->story, true)['question'];
|
|
|
+ $res['options'] = json_decode($s->story, true)['options'];
|
|
|
+ $res['voted'] = PollService::votedStory($s->id, $authed);
|
|
|
+ if($res['voted']) {
|
|
|
+ $res['voted_index'] = PollService::storyChoice($s->id, $authed);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $res;
|
|
|
})->toArray();
|
|
|
if(count($stories) == 0) {
|
|
|
return [];
|
|
@@ -342,32 +111,27 @@ class StoryController extends Controller
|
|
|
$cursor = count($stories) - 1;
|
|
|
$stories = [[
|
|
|
'id' => (string) $stories[$cursor]['id'],
|
|
|
- 'photo' => $profile->avatarUrl(),
|
|
|
- 'name' => $profile->username,
|
|
|
- 'link' => $profile->url(),
|
|
|
- 'lastUpdated' => (int) now()->format('U'),
|
|
|
- 'seen' => null,
|
|
|
- 'items' => $stories,
|
|
|
+ 'nodes' => $stories,
|
|
|
+ 'account' => AccountService::get($profile->id),
|
|
|
'pid' => (string) $profile->id
|
|
|
]];
|
|
|
return response()->json($stories, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
}
|
|
|
|
|
|
- public function apiV1Viewed(Request $request)
|
|
|
+ public function viewed(Request $request)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
$this->validate($request, [
|
|
|
- 'id' => 'required|integer|min:1|exists:stories',
|
|
|
+ 'id' => 'required|min:1',
|
|
|
]);
|
|
|
$id = $request->input('id');
|
|
|
|
|
|
$authed = $request->user()->profile;
|
|
|
|
|
|
$story = Story::with('profile')
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->orderByDesc('expires_at')
|
|
|
->findOrFail($id);
|
|
|
+ $exp = $story->expires_at;
|
|
|
|
|
|
$profile = $story->profile;
|
|
|
|
|
@@ -378,81 +142,128 @@ class StoryController extends Controller
|
|
|
$publicOnly = (bool) $profile->followedBy($authed);
|
|
|
abort_if(!$publicOnly, 403);
|
|
|
|
|
|
- StoryView::firstOrCreate([
|
|
|
+
|
|
|
+ $v = StoryView::firstOrCreate([
|
|
|
'story_id' => $id,
|
|
|
'profile_id' => $authed->id
|
|
|
]);
|
|
|
|
|
|
- $story->view_count = $story->view_count + 1;
|
|
|
- $story->save();
|
|
|
+ if($v->wasRecentlyCreated) {
|
|
|
+ Story::findOrFail($story->id)->increment('view_count');
|
|
|
|
|
|
+ if($story->local == false) {
|
|
|
+ StoryViewDeliver::dispatch($story, $authed)->onQueue('story');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Cache::forget('stories:recent:by_id:' . $authed->id);
|
|
|
+ StoryService::addSeen($authed->id, $story->id);
|
|
|
return ['code' => 200];
|
|
|
}
|
|
|
|
|
|
- public function apiV1Exists(Request $request, $id)
|
|
|
+ public function exists(Request $request, $id)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
- $res = (bool) Story::whereProfileId($id)
|
|
|
+ return response()->json(Story::whereProfileId($id)
|
|
|
->whereActive(true)
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->count();
|
|
|
+ ->exists());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function iRedirect(Request $request)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
- return response()->json($res);
|
|
|
+ $user = $request->user();
|
|
|
+ abort_if(!$user, 404);
|
|
|
+ $username = $user->username;
|
|
|
+ return redirect("/stories/{$username}");
|
|
|
}
|
|
|
|
|
|
- public function apiV1Me(Request $request)
|
|
|
+ public function viewers(Request $request)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
- $profile = $request->user()->profile;
|
|
|
- $stories = Story::whereProfileId($profile->id)
|
|
|
+ $this->validate($request, [
|
|
|
+ 'sid' => 'required|string'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+ $sid = $request->input('sid');
|
|
|
+
|
|
|
+ $story = Story::whereProfileId($pid)
|
|
|
->whereActive(true)
|
|
|
- ->orderBy('expires_at')
|
|
|
- ->where('expires_at', '>', now())
|
|
|
- ->get()
|
|
|
- ->map(function($s, $k) {
|
|
|
- return [
|
|
|
- 'id' => $s->id,
|
|
|
- 'type' => Str::endsWith($s->path, '.mp4') ? 'video' :'photo',
|
|
|
- 'length' => 3,
|
|
|
- 'src' => url(Storage::url($s->path)),
|
|
|
- 'preview' => null,
|
|
|
- 'link' => null,
|
|
|
- 'linkText' => null,
|
|
|
- 'time' => $s->created_at->format('U'),
|
|
|
- 'expires_at' => (int) $s->expires_at->format('U'),
|
|
|
- 'seen' => true
|
|
|
- ];
|
|
|
- })->toArray();
|
|
|
- $ts = count($stories) ? last($stories)['time'] : null;
|
|
|
- $res = [
|
|
|
- 'id' => (string) $profile->id,
|
|
|
- 'photo' => $profile->avatarUrl(),
|
|
|
- 'name' => $profile->username,
|
|
|
- 'link' => $profile->url(),
|
|
|
- 'lastUpdated' => $ts,
|
|
|
- 'seen' => true,
|
|
|
- 'items' => $stories
|
|
|
- ];
|
|
|
+ ->findOrFail($sid);
|
|
|
|
|
|
- return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
+ $viewers = StoryView::whereStoryId($story->id)
|
|
|
+ ->latest()
|
|
|
+ ->simplePaginate(10)
|
|
|
+ ->map(function($view) {
|
|
|
+ return AccountService::get($view->profile_id);
|
|
|
+ })
|
|
|
+ ->values();
|
|
|
+
|
|
|
+ return response()->json($viewers, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
}
|
|
|
|
|
|
- public function compose(Request $request)
|
|
|
+ public function remoteStory(Request $request, $id)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
- return view('stories.compose');
|
|
|
+ $profile = Profile::findOrFail($id);
|
|
|
+ if($profile->user_id != null || $profile->domain == null) {
|
|
|
+ return redirect('/stories/' . $profile->username);
|
|
|
+ }
|
|
|
+ $pid = $profile->id;
|
|
|
+ return view('stories.show_remote', compact('pid'));
|
|
|
}
|
|
|
|
|
|
- public function iRedirect(Request $request)
|
|
|
+ public function pollResults(Request $request)
|
|
|
{
|
|
|
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
|
|
|
|
|
|
- $user = $request->user();
|
|
|
- abort_if(!$user, 404);
|
|
|
- $username = $user->username;
|
|
|
- return redirect("/stories/{$username}");
|
|
|
+ $this->validate($request, [
|
|
|
+ 'sid' => 'required|string'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $pid = $request->user()->profile_id;
|
|
|
+ $sid = $request->input('sid');
|
|
|
+
|
|
|
+ $story = Story::whereProfileId($pid)
|
|
|
+ ->whereActive(true)
|
|
|
+ ->findOrFail($sid);
|
|
|
+
|
|
|
+ return PollService::storyResults($sid);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getActivityObject(Request $request, $username, $id)
|
|
|
+ {
|
|
|
+ abort_if(!config_cache('instance.stories.enabled'), 404);
|
|
|
+
|
|
|
+ if(!$request->wantsJson()) {
|
|
|
+ return redirect('/stories/' . $username);
|
|
|
+ }
|
|
|
+
|
|
|
+ abort_if(!$request->hasHeader('Authorization'), 404);
|
|
|
+
|
|
|
+ $profile = Profile::whereUsername($username)->whereNull('domain')->firstOrFail();
|
|
|
+ $story = Story::whereActive(true)->whereProfileId($profile->id)->findOrFail($id);
|
|
|
+
|
|
|
+ abort_if($story->bearcap_token == null, 404);
|
|
|
+ abort_if(now()->gt($story->expires_at), 404);
|
|
|
+ $token = substr($request->header('Authorization'), 7);
|
|
|
+ abort_if(hash_equals($story->bearcap_token, $token) === false, 404);
|
|
|
+ abort_if($story->created_at->lt(now()->subMinutes(20)), 404);
|
|
|
+
|
|
|
+ $fractal = new Manager();
|
|
|
+ $fractal->setSerializer(new ArraySerializer());
|
|
|
+ $resource = new Item($story, new StoryVerb());
|
|
|
+ $res = $fractal->createData($resource)->toArray();
|
|
|
+ return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function showSystemStory()
|
|
|
+ {
|
|
|
+ // return view('stories.system');
|
|
|
}
|
|
|
}
|