diff --git a/CHANGELOG.md b/CHANGELOG.md
index d077f71c3..ee2a962ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.7...dev)
+### Added
+- Post edits ([#4416](https://github.com/pixelfed/pixelfed/pull/4416)) ([98cf8f3](https://github.com/pixelfed/pixelfed/commit/98cf8f3))
+
### Updates
- Update StatusService, fix bug in getFull method ([4d8b4dcf](https://github.com/pixelfed/pixelfed/commit/4d8b4dcf))
- ([](https://github.com/pixelfed/pixelfed/commit/))
diff --git a/app/Http/Controllers/StatusEditController.php b/app/Http/Controllers/StatusEditController.php
new file mode 100644
index 000000000..1d0a22396
--- /dev/null
+++ b/app/Http/Controllers/StatusEditController.php
@@ -0,0 +1,59 @@
+middleware('auth');
+ abort_if(!config('exp.pue'), 404, 'Post editing is not enabled on this server.');
+ }
+
+ public function store(StoreStatusEditRequest $request, $id)
+ {
+ $validated = $request->validated();
+
+ $status = Status::findOrFail($id);
+ abort_if(StatusEdit::whereStatusId($status->id)->count() >= 10, 400, 'You cannot edit your post more than 10 times.');
+ $res = UpdateStatusService::call($status, $validated);
+
+ $status = Status::findOrFail($id);
+ StatusLocalUpdateActivityPubDeliverPipeline::dispatch($status)->delay(now()->addMinutes(1));
+ return $res;
+ }
+
+ public function history(Request $request, $id)
+ {
+ abort_if(!$request->user(), 403);
+ $status = Status::whereNull('reblog_of_id')->findOrFail($id);
+ abort_if(!in_array($status->scope, ['public', 'unlisted']), 403);
+ if(!$status->edits()->count()) {
+ return [];
+ }
+ $cached = StatusService::get($status->id, false);
+
+ $res = $status->edits->map(function($edit) use($cached) {
+ return [
+ 'content' => Autolink::create()->autolink($edit->caption),
+ 'spoiler_text' => $edit->spoiler_text,
+ 'sensitive' => (bool) $edit->is_nsfw,
+ 'created_at' => str_replace('+00:00', 'Z', $edit->created_at->format(DATE_RFC3339_EXTENDED)),
+ 'account' => $cached['account'],
+ 'media_attachments' => $cached['media_attachments'],
+ 'emojis' => $cached['emojis'],
+ ];
+ })->reverse()->values()->toArray();
+ return $res;
+ }
+}
diff --git a/app/Http/Requests/Status/StoreStatusEditRequest.php b/app/Http/Requests/Status/StoreStatusEditRequest.php
new file mode 100644
index 000000000..aa9364ca6
--- /dev/null
+++ b/app/Http/Requests/Status/StoreStatusEditRequest.php
@@ -0,0 +1,69 @@
+user()->profile;
+ if($profile->status != null) {
+ return false;
+ }
+ if($profile->unlisted == true && $profile->cw == true) {
+ return false;
+ }
+ $types = [
+ "photo",
+ "photo:album",
+ "photo:video:album",
+ "reply",
+ "text",
+ "video",
+ "video:album"
+ ];
+ $scopes = ['public', 'unlisted', 'private'];
+ $status = Status::whereNull('reblog_of_id')->whereIn('type', $types)->whereIn('scope', $scopes)->find($this->route('id'));
+ return $status && $this->user()->profile_id === $status->profile_id;
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules(): array
+ {
+ return [
+ 'status' => 'sometimes|max:'.config('pixelfed.max_caption_length', 500),
+ 'spoiler_text' => 'nullable|string|max:140',
+ 'sensitive' => 'sometimes|boolean',
+ 'media_ids' => [
+ 'nullable',
+ 'required_without:status',
+ 'array',
+ 'max:' . config('pixelfed.max_album_length'),
+ function (string $attribute, mixed $value, Closure $fail) {
+ Media::whereProfileId($this->user()->profile_id)
+ ->where(function($query) {
+ return $query->whereNull('status_id')
+ ->orWhere('status_id', '=', $this->route('id'));
+ })
+ ->findOrFail($value);
+ },
+ ],
+ 'location' => 'sometimes|nullable',
+ 'location.id' => 'sometimes|integer|min:1|max:128769',
+ 'location.country' => 'required_with:location.id',
+ 'location.name' => 'required_with:location.id',
+ ];
+ }
+}
diff --git a/app/Jobs/StatusPipeline/StatusLocalUpdateActivityPubDeliverPipeline.php b/app/Jobs/StatusPipeline/StatusLocalUpdateActivityPubDeliverPipeline.php
new file mode 100644
index 000000000..745f5f5ff
--- /dev/null
+++ b/app/Jobs/StatusPipeline/StatusLocalUpdateActivityPubDeliverPipeline.php
@@ -0,0 +1,129 @@
+status = $status;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ $status = $this->status;
+ $profile = $status->profile;
+
+ // ignore group posts
+ // if($status->group_id != null) {
+ // return;
+ // }
+
+ if($status->local == false || $status->url || $status->uri) {
+ return;
+ }
+
+ $audience = $status->profile->getAudienceInbox();
+
+ if(empty($audience) || !in_array($status->scope, ['public', 'unlisted', 'private'])) {
+ // Return on profiles with no remote followers
+ return;
+ }
+
+ switch($status->type) {
+ case 'poll':
+ // Polls not yet supported
+ return;
+ break;
+
+ default:
+ $activitypubObject = new UpdateNote();
+ break;
+ }
+
+
+ $fractal = new Fractal\Manager();
+ $fractal->setSerializer(new ArraySerializer());
+ $resource = new Fractal\Resource\Item($status, $activitypubObject);
+ $activity = $fractal->createData($resource)->toArray();
+
+ $payload = json_encode($activity);
+
+ $client = new Client([
+ 'timeout' => config('federation.activitypub.delivery.timeout')
+ ]);
+
+ $version = config('pixelfed.version');
+ $appUrl = config('app.url');
+ $userAgent = "(Pixelfed/{$version}; +{$appUrl})";
+
+ $requests = function($audience) use ($client, $activity, $profile, $payload, $userAgent) {
+ foreach($audience as $url) {
+ $headers = HttpSignature::sign($profile, $url, $activity, [
+ 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
+ 'User-Agent' => $userAgent,
+ ]);
+ yield function() use ($client, $url, $headers, $payload) {
+ return $client->postAsync($url, [
+ 'curl' => [
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => $payload,
+ CURLOPT_HEADER => true,
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_SSL_VERIFYHOST => false
+ ]
+ ]);
+ };
+ }
+ };
+
+ $pool = new Pool($client, $requests($audience), [
+ 'concurrency' => config('federation.activitypub.delivery.concurrency'),
+ 'fulfilled' => function ($response, $index) {
+ },
+ 'rejected' => function ($reason, $index) {
+ }
+ ]);
+
+ $promise = $pool->promise();
+
+ $promise->wait();
+ }
+}
diff --git a/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php b/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php
new file mode 100644
index 000000000..b79f54cce
--- /dev/null
+++ b/app/Jobs/StatusPipeline/StatusRemoteUpdatePipeline.php
@@ -0,0 +1,159 @@
+activity = $activity;
+ }
+
+ /**
+ * Execute the job.
+ */
+ public function handle(): void
+ {
+ $activity = $this->activity;
+ $status = Status::with('media')->whereObjectUrl($activity['id'])->first();
+ if(!$status) {
+ return;
+ }
+ $this->createPreviousEdit($status);
+ $this->updateMedia($status, $activity);
+ $this->updateImmediateAttributes($status, $activity);
+ $this->createEdit($status, $activity);
+ }
+
+ protected function createPreviousEdit($status)
+ {
+ if(!$status->edits()->count()) {
+ StatusEdit::create([
+ 'status_id' => $status->id,
+ 'profile_id' => $status->profile_id,
+ 'caption' => $status->caption,
+ 'spoiler_text' => $status->cw_summary,
+ 'is_nsfw' => $status->is_nsfw,
+ 'ordered_media_attachment_ids' => $status->media()->orderBy('order')->pluck('id')->toArray(),
+ 'created_at' => $status->created_at
+ ]);
+ }
+ }
+
+ protected function updateMedia($status, $activity)
+ {
+ if(!isset($activity['attachment'])) {
+ return;
+ }
+ $ogm = $status->media->count() ? $status->media()->orderBy('order')->get() : collect([]);
+ $nm = collect($activity['attachment'])->filter(function($nm) {
+ return isset(
+ $nm['type'],
+ $nm['mediaType'],
+ $nm['url']
+ ) &&
+ in_array($nm['type'], ['Document', 'Image', 'Video']) &&
+ in_array($nm['mediaType'], explode(',', config('pixelfed.media_types')));
+ });
+
+ // Skip when no media
+ if(!$ogm->count() && !$nm->count()) {
+ return;
+ }
+
+ Media::whereProfileId($status->profile_id)
+ ->whereStatusId($status->id)
+ ->update([
+ 'status_id' => null
+ ]);
+
+ $nm->each(function($n, $key) use($status) {
+ $m = new Media;
+ $m->status_id = $status->id;
+ $m->profile_id = $status->profile_id;
+ $m->remote_media = true;
+ $m->media_path = $n['url'];
+ $m->caption = isset($n['name']) && !empty($n['name']) ? Purify::clean($n['name']) : null;
+ $m->remote_url = $n['url'];
+ $m->width = isset($n['width']) && !empty($n['width']) ? $n['width'] : null;
+ $m->height = isset($n['height']) && !empty($n['height']) ? $n['height'] : null;
+ $m->skip_optimize = true;
+ $m->order = $key + 1;
+ $m->save();
+ });
+ }
+
+ protected function updateImmediateAttributes($status, $activity)
+ {
+ if(isset($activity['content'])) {
+ $status->caption = strip_tags($activity['content']);
+ $status->rendered = Purify::clean($activity['content']);
+ }
+
+ if(isset($activity['sensitive'])) {
+ if((bool) $activity['sensitive'] == false) {
+ $status->is_nsfw = false;
+ $exists = ModLog::whereObjectType('App\Status::class')
+ ->whereObjectId($status->id)
+ ->whereAction('admin.status.moderate')
+ ->exists();
+ if($exists == true) {
+ $status->is_nsfw = true;
+ }
+ $profile = Profile::find($status->profile_id);
+ if(!$profile || $profile->cw == true) {
+ $status->is_nsfw = true;
+ }
+ } else {
+ $status->is_nsfw = true;
+ }
+ }
+
+ if(isset($activity['summary'])) {
+ $status->cw_summary = Purify::clean($activity['summary']);
+ } else {
+ $status->cw_summary = null;
+ }
+
+ $status->edited_at = now();
+ $status->save();
+ StatusService::del($status->id);
+ }
+
+ protected function createEdit($status, $activity)
+ {
+ $cleaned = isset($activity['content']) ? Purify::clean($activity['content']) : null;
+ $spoiler_text = isset($activity['summary']) ? Purify::clean($attributes['summary']) : null;
+ $sensitive = isset($activity['sensitive']) ? $activity['sensitive'] : null;
+ $mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null;
+ StatusEdit::create([
+ 'status_id' => $status->id,
+ 'profile_id' => $status->profile_id,
+ 'caption' => $cleaned,
+ 'spoiler_text' => $spoiler_text,
+ 'is_nsfw' => $sensitive,
+ 'ordered_media_attachment_ids' => $mids
+ ]);
+ }
+}
diff --git a/app/Models/StatusEdit.php b/app/Models/StatusEdit.php
new file mode 100644
index 000000000..5c9ec5695
--- /dev/null
+++ b/app/Models/StatusEdit.php
@@ -0,0 +1,19 @@
+ 'array',
+ 'media_descriptions' => 'array',
+ 'poll_options' => 'array'
+ ];
+
+ protected $guarded = [];
+}
diff --git a/app/Services/Status/UpdateStatusService.php b/app/Services/Status/UpdateStatusService.php
new file mode 100644
index 000000000..be50bf70f
--- /dev/null
+++ b/app/Services/Status/UpdateStatusService.php
@@ -0,0 +1,137 @@
+id);
+ }
+
+ public static function updateMediaAttachements(Status $status, $attributes)
+ {
+ $count = $status->media()->count();
+ if($count === 0 || $count === 1) {
+ return;
+ }
+
+ $oids = $status->media()->orderBy('order')->pluck('id')->map(function($m) { return (string) $m; });
+ $nids = collect($attributes['media_ids']);
+
+ if($oids->toArray() === $nids->toArray()) {
+ return;
+ }
+
+ foreach($oids->diff($nids)->values()->toArray() as $mid) {
+ $media = Media::find($mid);
+ if(!$media) {
+ continue;
+ }
+ $media->status_id = null;
+ $media->save();
+ MediaStorageService::delete($media, true);
+ }
+
+ $nids->each(function($nid, $idx) {
+ $media = Media::find($nid);
+ if(!$media) {
+ return;
+ }
+ $media->order = $idx;
+ $media->save();
+ });
+ MediaService::del($status->id);
+ }
+
+ public static function handleImmediateAttributes(Status $status, $attributes)
+ {
+ if(isset($attributes['status'])) {
+ $cleaned = Purify::clean($attributes['status']);
+ $status->caption = $cleaned;
+ $status->rendered = Autolink::create()->autolink($cleaned);
+ } else {
+ $status->caption = null;
+ $status->rendered = null;
+ }
+ if(isset($attributes['sensitive'])) {
+ if($status->is_nsfw != (bool) $attributes['sensitive'] &&
+ (bool) $attributes['sensitive'] == false)
+ {
+ $exists = ModLog::whereObjectType('App\Status::class')
+ ->whereObjectId($status->id)
+ ->whereAction('admin.status.moderate')
+ ->exists();
+ if(!$exists) {
+ $status->is_nsfw = (bool) $attributes['sensitive'];
+ }
+ } else {
+ $status->is_nsfw = (bool) $attributes['sensitive'];
+ }
+ }
+ if(isset($attributes['spoiler_text'])) {
+ $status->cw_summary = Purify::clean($attributes['spoiler_text']);
+ } else {
+ $status->cw_summary = null;
+ }
+ if(isset($attributes['location'])) {
+ if (isset($attributes['location']['id'])) {
+ $status->place_id = $attributes['location']['id'];
+ } else {
+ $status->place_id = null;
+ }
+ }
+ if($status->cw_summary && !$status->is_nsfw) {
+ $status->cw_summary = null;
+ }
+ $status->edited_at = now();
+ $status->save();
+ StatusService::del($status->id);
+ }
+
+ public static function createPreviousEdit(Status $status)
+ {
+ if(!$status->edits()->count()) {
+ StatusEdit::create([
+ 'status_id' => $status->id,
+ 'profile_id' => $status->profile_id,
+ 'caption' => $status->caption,
+ 'spoiler_text' => $status->cw_summary,
+ 'is_nsfw' => $status->is_nsfw,
+ 'ordered_media_attachment_ids' => $status->media()->orderBy('order')->pluck('id')->toArray(),
+ 'created_at' => $status->created_at
+ ]);
+ }
+ }
+
+ public static function createEdit(Status $status, $attributes)
+ {
+ $cleaned = isset($attributes['status']) ? Purify::clean($attributes['status']) : null;
+ $spoiler_text = isset($attributes['spoiler_text']) ? Purify::clean($attributes['spoiler_text']) : null;
+ $sensitive = isset($attributes['sensitive']) ? $attributes['sensitive'] : null;
+ $mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null;
+ StatusEdit::create([
+ 'status_id' => $status->id,
+ 'profile_id' => $status->profile_id,
+ 'caption' => $cleaned,
+ 'spoiler_text' => $spoiler_text,
+ 'is_nsfw' => $sensitive,
+ 'ordered_media_attachment_ids' => $mids
+ ]);
+ }
+}
diff --git a/app/Status.php b/app/Status.php
index 183c18023..4148a4f99 100644
--- a/app/Status.php
+++ b/app/Status.php
@@ -9,6 +9,7 @@ use App\Http\Controllers\StatusController;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\Poll;
use App\Services\AccountService;
+use App\Models\StatusEdit;
class Status extends Model
{
@@ -27,7 +28,8 @@ class Status extends Model
* @var array
*/
protected $casts = [
- 'deleted_at' => 'datetime'
+ 'deleted_at' => 'datetime',
+ 'edited_at' => 'datetime'
];
protected $guarded = [];
@@ -393,4 +395,9 @@ class Status extends Model
{
return $this->hasOne(Poll::class);
}
+
+ public function edits()
+ {
+ return $this->hasMany(StatusEdit::class);
+ }
}
diff --git a/app/Transformer/ActivityPub/Verb/UpdateNote.php b/app/Transformer/ActivityPub/Verb/UpdateNote.php
new file mode 100644
index 000000000..bdbb20c45
--- /dev/null
+++ b/app/Transformer/ActivityPub/Verb/UpdateNote.php
@@ -0,0 +1,133 @@
+mentions->map(function ($mention) {
+ $webfinger = $mention->emailUrl();
+ $name = Str::startsWith($webfinger, '@') ?
+ $webfinger :
+ '@' . $webfinger;
+ return [
+ 'type' => 'Mention',
+ 'href' => $mention->permalink(),
+ 'name' => $name
+ ];
+ })->toArray();
+
+ if($status->in_reply_to_id != null) {
+ $parent = $status->parent()->profile;
+ if($parent) {
+ $webfinger = $parent->emailUrl();
+ $name = Str::startsWith($webfinger, '@') ?
+ $webfinger :
+ '@' . $webfinger;
+ $reply = [
+ 'type' => 'Mention',
+ 'href' => $parent->permalink(),
+ 'name' => $name
+ ];
+ $mentions = array_merge($reply, $mentions);
+ }
+ }
+
+ $hashtags = $status->hashtags->map(function ($hashtag) {
+ return [
+ 'type' => 'Hashtag',
+ 'href' => $hashtag->url(),
+ 'name' => "#{$hashtag->name}",
+ ];
+ })->toArray();
+
+ $emojis = CustomEmoji::scan($status->caption, true) ?? [];
+ $emoji = array_merge($emojis, $mentions);
+ $tags = array_merge($emoji, $hashtags);
+
+ $latestEdit = $status->edits()->latest()->first();
+
+ return [
+ '@context' => [
+ 'https://w3id.org/security/v1',
+ 'https://www.w3.org/ns/activitystreams',
+ [
+ 'Hashtag' => 'as:Hashtag',
+ 'sensitive' => 'as:sensitive',
+ 'schema' => 'http://schema.org/',
+ 'pixelfed' => 'http://pixelfed.org/ns#',
+ 'commentsEnabled' => [
+ '@id' => 'pixelfed:commentsEnabled',
+ '@type' => 'schema:Boolean'
+ ],
+ 'capabilities' => [
+ '@id' => 'pixelfed:capabilities',
+ '@container' => '@set'
+ ],
+ 'announce' => [
+ '@id' => 'pixelfed:canAnnounce',
+ '@type' => '@id'
+ ],
+ 'like' => [
+ '@id' => 'pixelfed:canLike',
+ '@type' => '@id'
+ ],
+ 'reply' => [
+ '@id' => 'pixelfed:canReply',
+ '@type' => '@id'
+ ],
+ 'toot' => 'http://joinmastodon.org/ns#',
+ 'Emoji' => 'toot:Emoji'
+ ]
+ ],
+ 'id' => $status->permalink('#updates/' . $latestEdit->id),
+ 'type' => 'Update',
+ 'actor' => $status->profile->permalink(),
+ 'published' => $latestEdit->created_at->toAtomString(),
+ 'to' => $status->scopeToAudience('to'),
+ 'cc' => $status->scopeToAudience('cc'),
+ 'object' => [
+ 'id' => $status->url(),
+ 'type' => 'Note',
+ 'summary' => $status->is_nsfw ? $status->cw_summary : null,
+ 'content' => $status->rendered ?? $status->caption,
+ 'inReplyTo' => $status->in_reply_to_id ? $status->parent()->url() : null,
+ 'published' => $status->created_at->toAtomString(),
+ 'url' => $status->url(),
+ 'attributedTo' => $status->profile->permalink(),
+ 'to' => $status->scopeToAudience('to'),
+ 'cc' => $status->scopeToAudience('cc'),
+ 'sensitive' => (bool) $status->is_nsfw,
+ 'attachment' => $status->media()->orderBy('order')->get()->map(function ($media) {
+ return [
+ 'type' => $media->activityVerb(),
+ 'mediaType' => $media->mime,
+ 'url' => $media->url(),
+ 'name' => $media->caption,
+ ];
+ })->toArray(),
+ 'tag' => $tags,
+ 'commentsEnabled' => (bool) !$status->comments_disabled,
+ 'updated' => $latestEdit->created_at->toAtomString(),
+ 'capabilities' => [
+ 'announce' => 'https://www.w3.org/ns/activitystreams#Public',
+ 'like' => 'https://www.w3.org/ns/activitystreams#Public',
+ 'reply' => $status->comments_disabled == true ? '[]' : 'https://www.w3.org/ns/activitystreams#Public'
+ ],
+ 'location' => $status->place_id ? [
+ 'type' => 'Place',
+ 'name' => $status->place->name,
+ 'longitude' => $status->place->long,
+ 'latitude' => $status->place->lat,
+ 'country' => $status->place->country
+ ] : null,
+ ]
+ ];
+ }
+}
diff --git a/app/Transformer/Api/StatusStatelessTransformer.php b/app/Transformer/Api/StatusStatelessTransformer.php
index 38a3fedf8..acb522446 100644
--- a/app/Transformer/Api/StatusStatelessTransformer.php
+++ b/app/Transformer/Api/StatusStatelessTransformer.php
@@ -65,7 +65,8 @@ class StatusStatelessTransformer extends Fractal\TransformerAbstract
'media_attachments' => MediaService::get($status->id),
'account' => AccountService::get($status->profile_id, true),
'tags' => StatusHashtagService::statusTags($status->id),
- 'poll' => $poll
+ 'poll' => $poll,
+ 'edited_at' => $status->edited_at ? str_replace('+00:00', 'Z', $status->edited_at->format(DATE_RFC3339_EXTENDED)) : null,
];
}
}
diff --git a/app/Transformer/Api/StatusTransformer.php b/app/Transformer/Api/StatusTransformer.php
index 61c5f875b..f735a57be 100644
--- a/app/Transformer/Api/StatusTransformer.php
+++ b/app/Transformer/Api/StatusTransformer.php
@@ -70,6 +70,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
'tags' => StatusHashtagService::statusTags($status->id),
'poll' => $poll,
'bookmarked' => BookmarkService::get($pid, $status->id),
+ 'edited_at' => $status->edited_at ? str_replace('+00:00', 'Z', $status->edited_at->format(DATE_RFC3339_EXTENDED)) : null,
];
}
}
diff --git a/app/Util/ActivityPub/Inbox.php b/app/Util/ActivityPub/Inbox.php
index 790a48e8d..325d9b0c3 100644
--- a/app/Util/ActivityPub/Inbox.php
+++ b/app/Util/ActivityPub/Inbox.php
@@ -28,6 +28,7 @@ use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline;
use App\Jobs\DeletePipeline\DeleteRemoteStatusPipeline;
use App\Jobs\StoryPipeline\StoryExpire;
use App\Jobs\StoryPipeline\StoryFetch;
+use App\Jobs\StatusPipeline\StatusRemoteUpdatePipeline;
use App\Util\ActivityPub\Validator\Accept as AcceptValidator;
use App\Util\ActivityPub\Validator\Add as AddValidator;
@@ -128,9 +129,9 @@ class Inbox
$this->handleFlagActivity();
break;
- // case 'Update':
- // (new UpdateActivity($this->payload, $this->profile))->handle();
- // break;
+ case 'Update':
+ $this->handleUpdateActivity();
+ break;
default:
// TODO: decide how to handle invalid verbs.
@@ -1207,4 +1208,19 @@ class Inbox
return;
}
+
+ public function handleUpdateActivity()
+ {
+ $activity = $this->payload['object'];
+
+ if(!isset($activity['type'], $activity['id'])) {
+ return;
+ }
+
+ if($activity['type'] === 'Note') {
+ if(Status::whereObjectUrl($activity['id'])->exists()) {
+ StatusRemoteUpdatePipeline::dispatch($activity);
+ }
+ }
+ }
}
diff --git a/config/exp.php b/config/exp.php
index 0c9f83706..0ace5135b 100644
--- a/config/exp.php
+++ b/config/exp.php
@@ -25,6 +25,8 @@ return [
// Cached public timeline for larger instances (beta)
'cached_public_timeline' => env('EXP_CPT', false),
+ 'cached_home_timeline' => env('EXP_CHT', false),
+
// Groups (unreleased)
'gps' => env('EXP_GPS', false),
@@ -33,4 +35,10 @@ return [
// Enforce Mastoapi Compatibility (alpha)
'emc' => env('EXP_EMC', true),
+
+ // HLS Live Streaming
+ 'hls' => env('HLS_LIVE', false),
+
+ // Post Update/Edits
+ 'pue' => env('EXP_PUE', true),
];
diff --git a/public/css/admin.css b/public/css/admin.css
index 431e8ca31..658de1dbc 100644
Binary files a/public/css/admin.css and b/public/css/admin.css differ
diff --git a/public/css/app.css b/public/css/app.css
index a0176c807..0c96787bd 100644
Binary files a/public/css/app.css and b/public/css/app.css differ
diff --git a/public/css/appdark.css b/public/css/appdark.css
index e8c2fad49..414d666f8 100644
Binary files a/public/css/appdark.css and b/public/css/appdark.css differ
diff --git a/public/css/landing.css b/public/css/landing.css
index f52fd36d9..152a6a78f 100644
Binary files a/public/css/landing.css and b/public/css/landing.css differ
diff --git a/public/css/spa.css b/public/css/spa.css
index 8d2e31958..37cce8287 100644
Binary files a/public/css/spa.css and b/public/css/spa.css differ
diff --git a/public/js/about.bundle.44a18841089fdde3.js b/public/js/about.bundle.dcf91eae809841f8.js
similarity index 100%
rename from public/js/about.bundle.44a18841089fdde3.js
rename to public/js/about.bundle.dcf91eae809841f8.js
diff --git a/public/js/admin.js b/public/js/admin.js
index 48d5949b8..cb489e556 100644
Binary files a/public/js/admin.js and b/public/js/admin.js differ
diff --git a/public/js/changelog.bundle.7f58a5ccc6659eb2.js b/public/js/changelog.bundle.500c0754dd59045b.js
similarity index 100%
rename from public/js/changelog.bundle.7f58a5ccc6659eb2.js
rename to public/js/changelog.bundle.500c0754dd59045b.js
diff --git a/public/js/compose.chunk.c413851da244ae3f.js b/public/js/compose.chunk.eb564854474fa255.js
similarity index 100%
rename from public/js/compose.chunk.c413851da244ae3f.js
rename to public/js/compose.chunk.eb564854474fa255.js
diff --git a/public/js/contact.bundle.d6c1d467c11796b1.js b/public/js/contact.bundle.97bd609a4737ae8d.js
similarity index 100%
rename from public/js/contact.bundle.d6c1d467c11796b1.js
rename to public/js/contact.bundle.97bd609a4737ae8d.js
diff --git a/public/js/daci.chunk.3f13ec9fc49e9d2b.js b/public/js/daci.chunk.3f13ec9fc49e9d2b.js
deleted file mode 100644
index 42f453e93..000000000
Binary files a/public/js/daci.chunk.3f13ec9fc49e9d2b.js and /dev/null differ
diff --git a/public/js/daci.chunk.e0ca30e5fa8c81f0.js b/public/js/daci.chunk.e0ca30e5fa8c81f0.js
new file mode 100644
index 000000000..727da3337
Binary files /dev/null and b/public/js/daci.chunk.e0ca30e5fa8c81f0.js differ
diff --git a/public/js/discover.chunk.5ceb85dcb38dfbef.js b/public/js/discover.chunk.93193c6ec9a42fc4.js
similarity index 100%
rename from public/js/discover.chunk.5ceb85dcb38dfbef.js
rename to public/js/discover.chunk.93193c6ec9a42fc4.js
diff --git a/public/js/discover~findfriends.chunk.1aabfedaab1849ba.js b/public/js/discover~findfriends.chunk.1aabfedaab1849ba.js
deleted file mode 100644
index 6ed8a0034..000000000
Binary files a/public/js/discover~findfriends.chunk.1aabfedaab1849ba.js and /dev/null differ
diff --git a/public/js/discover~findfriends.chunk.1c4a19cf5fda27ad.js b/public/js/discover~findfriends.chunk.1c4a19cf5fda27ad.js
new file mode 100644
index 000000000..78fbdcc61
Binary files /dev/null and b/public/js/discover~findfriends.chunk.1c4a19cf5fda27ad.js differ
diff --git a/public/js/discover~hashtag.bundle.b8319d6999d3e2e3.js b/public/js/discover~hashtag.bundle.10cb33346c033ea7.js
similarity index 100%
rename from public/js/discover~hashtag.bundle.b8319d6999d3e2e3.js
rename to public/js/discover~hashtag.bundle.10cb33346c033ea7.js
diff --git a/public/js/discover~memories.chunk.70b04c7698c2172b.js b/public/js/discover~memories.chunk.70b04c7698c2172b.js
deleted file mode 100644
index d01487e44..000000000
Binary files a/public/js/discover~memories.chunk.70b04c7698c2172b.js and /dev/null differ
diff --git a/public/js/discover~memories.chunk.a95b0149f5e4817f.js b/public/js/discover~memories.chunk.a95b0149f5e4817f.js
new file mode 100644
index 000000000..542314a98
Binary files /dev/null and b/public/js/discover~memories.chunk.a95b0149f5e4817f.js differ
diff --git a/public/js/discover~myhashtags.chunk.089b7465b2359979.js b/public/js/discover~myhashtags.chunk.089b7465b2359979.js
deleted file mode 100644
index 0cc8aeaa4..000000000
Binary files a/public/js/discover~myhashtags.chunk.089b7465b2359979.js and /dev/null differ
diff --git a/public/js/discover~myhashtags.chunk.9aa66068d1e96512.js b/public/js/discover~myhashtags.chunk.9aa66068d1e96512.js
new file mode 100644
index 000000000..769d6347f
Binary files /dev/null and b/public/js/discover~myhashtags.chunk.9aa66068d1e96512.js differ
diff --git a/public/js/discover~serverfeed.chunk.48875685bb3cec75.js b/public/js/discover~serverfeed.chunk.48875685bb3cec75.js
new file mode 100644
index 000000000..e7fc5f6b1
Binary files /dev/null and b/public/js/discover~serverfeed.chunk.48875685bb3cec75.js differ
diff --git a/public/js/discover~serverfeed.chunk.ff59ca12d08bb810.js b/public/js/discover~serverfeed.chunk.ff59ca12d08bb810.js
deleted file mode 100644
index 0e7b1f388..000000000
Binary files a/public/js/discover~serverfeed.chunk.ff59ca12d08bb810.js and /dev/null differ
diff --git a/public/js/discover~settings.chunk.5757ad3940569422.js b/public/js/discover~settings.chunk.5757ad3940569422.js
deleted file mode 100644
index 1e34fb753..000000000
Binary files a/public/js/discover~settings.chunk.5757ad3940569422.js and /dev/null differ
diff --git a/public/js/discover~settings.chunk.58f94c148a395667.js b/public/js/discover~settings.chunk.58f94c148a395667.js
new file mode 100644
index 000000000..0ce4ac874
Binary files /dev/null and b/public/js/discover~settings.chunk.58f94c148a395667.js differ
diff --git a/public/js/dms.chunk.91ab72a8dcd1a8a8.js b/public/js/dms.chunk.a36285d6eee3b46f.js
similarity index 100%
rename from public/js/dms.chunk.91ab72a8dcd1a8a8.js
rename to public/js/dms.chunk.a36285d6eee3b46f.js
diff --git a/public/js/dms~message.chunk.1cfdf19c4525eafa.js b/public/js/dms~message.chunk.1d2a7a110371a12b.js
similarity index 100%
rename from public/js/dms~message.chunk.1cfdf19c4525eafa.js
rename to public/js/dms~message.chunk.1d2a7a110371a12b.js
diff --git a/public/js/error404.bundle.5075813f1b00e10d.js b/public/js/error404.bundle.f84c69eed21a7d82.js
similarity index 100%
rename from public/js/error404.bundle.5075813f1b00e10d.js
rename to public/js/error404.bundle.f84c69eed21a7d82.js
diff --git a/public/js/help.bundle.7c1195b63e04d568.js b/public/js/help.bundle.0d8a2725bcc8ed81.js
similarity index 100%
rename from public/js/help.bundle.7c1195b63e04d568.js
rename to public/js/help.bundle.0d8a2725bcc8ed81.js
diff --git a/public/js/home.chunk.af8ef7b54f61b18d.js b/public/js/home.chunk.af8ef7b54f61b18d.js
deleted file mode 100644
index abe40b28e..000000000
Binary files a/public/js/home.chunk.af8ef7b54f61b18d.js and /dev/null differ
diff --git a/public/js/home.chunk.f0ab2b4f7e84894c.js b/public/js/home.chunk.f0ab2b4f7e84894c.js
new file mode 100644
index 000000000..0c5f28760
Binary files /dev/null and b/public/js/home.chunk.f0ab2b4f7e84894c.js differ
diff --git a/public/js/home.chunk.f0ab2b4f7e84894c.js.LICENSE.txt b/public/js/home.chunk.f0ab2b4f7e84894c.js.LICENSE.txt
new file mode 100644
index 000000000..ae386fb79
--- /dev/null
+++ b/public/js/home.chunk.f0ab2b4f7e84894c.js.LICENSE.txt
@@ -0,0 +1 @@
+/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
diff --git a/public/js/i18n.bundle.9b9bf1b64e2aa1c1.js b/public/js/i18n.bundle.83d55d158de68d01.js
similarity index 100%
rename from public/js/i18n.bundle.9b9bf1b64e2aa1c1.js
rename to public/js/i18n.bundle.83d55d158de68d01.js
diff --git a/public/js/kb.bundle.f6ebdaac1fd552ca.js b/public/js/kb.bundle.7c3d070a9bcc0489.js
similarity index 100%
rename from public/js/kb.bundle.f6ebdaac1fd552ca.js
rename to public/js/kb.bundle.7c3d070a9bcc0489.js
diff --git a/public/js/live-player.js b/public/js/live-player.js
index 2ace6e4eb..deac8dcaa 100644
Binary files a/public/js/live-player.js and b/public/js/live-player.js differ
diff --git a/public/js/manifest.js b/public/js/manifest.js
index c1bfd97d6..5fa7b7185 100644
Binary files a/public/js/manifest.js and b/public/js/manifest.js differ
diff --git a/public/js/notifications.chunk.9de71a122956c663.js b/public/js/notifications.chunk.fa21418a86f44a18.js
similarity index 100%
rename from public/js/notifications.chunk.9de71a122956c663.js
rename to public/js/notifications.chunk.fa21418a86f44a18.js
diff --git a/public/js/post.chunk.62a9d21c9016fd95.js b/public/js/post.chunk.62a9d21c9016fd95.js
deleted file mode 100644
index af462a50d..000000000
Binary files a/public/js/post.chunk.62a9d21c9016fd95.js and /dev/null differ
diff --git a/public/js/post.chunk.fc948c7ae6cf23f0.js b/public/js/post.chunk.fc948c7ae6cf23f0.js
new file mode 100644
index 000000000..458a6c042
Binary files /dev/null and b/public/js/post.chunk.fc948c7ae6cf23f0.js differ
diff --git a/public/js/post.chunk.fc948c7ae6cf23f0.js.LICENSE.txt b/public/js/post.chunk.fc948c7ae6cf23f0.js.LICENSE.txt
new file mode 100644
index 000000000..ae386fb79
--- /dev/null
+++ b/public/js/post.chunk.fc948c7ae6cf23f0.js.LICENSE.txt
@@ -0,0 +1 @@
+/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
diff --git a/public/js/profile.chunk.0f947cc09af5c8c3.js b/public/js/profile.chunk.0f947cc09af5c8c3.js
deleted file mode 100644
index a2bdf3278..000000000
Binary files a/public/js/profile.chunk.0f947cc09af5c8c3.js and /dev/null differ
diff --git a/public/js/profile.chunk.d1c4aa06ad944495.js b/public/js/profile.chunk.d1c4aa06ad944495.js
new file mode 100644
index 000000000..a7a801777
Binary files /dev/null and b/public/js/profile.chunk.d1c4aa06ad944495.js differ
diff --git a/public/js/profile~followers.bundle.fe353e697fb7660b.js b/public/js/profile~followers.bundle.eac566ea09458e75.js
similarity index 100%
rename from public/js/profile~followers.bundle.fe353e697fb7660b.js
rename to public/js/profile~followers.bundle.eac566ea09458e75.js
diff --git a/public/js/profile~following.bundle.c406db7b14d07d36.js b/public/js/profile~following.bundle.193b1268a32bbd07.js
similarity index 100%
rename from public/js/profile~following.bundle.c406db7b14d07d36.js
rename to public/js/profile~following.bundle.193b1268a32bbd07.js
diff --git a/public/js/static~privacy.bundle.24c230550b6938b2.js b/public/js/static~privacy.bundle.60f5c03624e7626e.js
similarity index 100%
rename from public/js/static~privacy.bundle.24c230550b6938b2.js
rename to public/js/static~privacy.bundle.60f5c03624e7626e.js
diff --git a/public/js/static~tos.bundle.65caad6c0546d8c9.js b/public/js/static~tos.bundle.d5389c3b8c2569d5.js
similarity index 100%
rename from public/js/static~tos.bundle.65caad6c0546d8c9.js
rename to public/js/static~tos.bundle.d5389c3b8c2569d5.js
diff --git a/public/js/vendor.js b/public/js/vendor.js
index 3df191c49..64e7deda4 100644
Binary files a/public/js/vendor.js and b/public/js/vendor.js differ
diff --git a/public/js/vendor.js.LICENSE.txt b/public/js/vendor.js.LICENSE.txt
index 3fce34c8b..f5a9d0ea4 100644
--- a/public/js/vendor.js.LICENSE.txt
+++ b/public/js/vendor.js.LICENSE.txt
@@ -64,17 +64,6 @@
* Licensed under GPL 3.
*/
-/*!
- * Sizzle CSS Selector Engine v2.3.10
- * https://sizzlejs.com/
- *
- * Copyright JS Foundation and other contributors
- * Released under the MIT license
- * https://js.foundation/
- *
- * Date: 2023-02-14
- */
-
/*!
* Vue.js v2.7.14
* (c) 2014-2022 Evan You
@@ -82,17 +71,14 @@
*/
/*!
- * jQuery JavaScript Library v3.6.4
+ * jQuery JavaScript Library v3.7.0
* https://jquery.com/
*
- * Includes Sizzle.js
- * https://sizzlejs.com/
- *
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
- * Date: 2023-03-08T15:28Z
+ * Date: 2023-05-11T18:29Z
*/
/*!
@@ -163,628 +149,8 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
-/*! ../controller/level-helper */
-
-/*! ../crypt/decrypter */
-
-/*! ../demux/aacdemuxer */
-
-/*! ../demux/chunk-cache */
-
-/*! ../demux/id3 */
-
-/*! ../demux/mp3demuxer */
-
-/*! ../demux/mp4demuxer */
-
-/*! ../demux/transmuxer */
-
-/*! ../demux/transmuxer-interface */
-
-/*! ../demux/transmuxer-worker.ts */
-
-/*! ../demux/tsdemuxer */
-
-/*! ../errors */
-
-/*! ../events */
-
-/*! ../is-supported */
-
-/*! ../loader/date-range */
-
-/*! ../loader/fragment */
-
-/*! ../loader/fragment-loader */
-
-/*! ../loader/level-key */
-
-/*! ../loader/load-stats */
-
-/*! ../remux/mp4-remuxer */
-
-/*! ../remux/passthrough-remuxer */
-
-/*! ../task-loop */
-
-/*! ../types/cmcd */
-
-/*! ../types/demuxer */
-
-/*! ../types/level */
-
-/*! ../types/loader */
-
-/*! ../types/transmuxer */
-
-/*! ../utils/attr-list */
-
-/*! ../utils/binary-search */
-
-/*! ../utils/buffer-helper */
-
-/*! ../utils/cea-608-parser */
-
-/*! ../utils/codecs */
-
-/*! ../utils/discontinuities */
-
-/*! ../utils/ewma */
-
-/*! ../utils/ewma-bandwidth-estimator */
-
-/*! ../utils/hex */
-
-/*! ../utils/imsc1-ttml-parser */
-
-/*! ../utils/keysystem-util */
-
-/*! ../utils/logger */
-
-/*! ../utils/mediakeys-helper */
-
-/*! ../utils/mediasource-helper */
-
-/*! ../utils/mp4-tools */
-
-/*! ../utils/numeric-encoding-utils */
-
-/*! ../utils/output-filter */
-
-/*! ../utils/texttrack-utils */
-
-/*! ../utils/time-ranges */
-
-/*! ../utils/timescale-conversion */
-
-/*! ../utils/typed-array */
-
-/*! ../utils/webvtt-parser */
-
-/*! ./aac-helper */
-
-/*! ./adts */
-
-/*! ./aes-crypto */
-
-/*! ./aes-decryptor */
-
-/*! ./base-audio-demuxer */
-
-/*! ./base-playlist-controller */
-
-/*! ./base-stream-controller */
-
-/*! ./buffer-operation-queue */
-
-/*! ./config */
-
-/*! ./controller/abr-controller */
-
-/*! ./controller/audio-stream-controller */
-
-/*! ./controller/audio-track-controller */
-
-/*! ./controller/buffer-controller */
-
-/*! ./controller/cap-level-controller */
-
-/*! ./controller/cmcd-controller */
-
-/*! ./controller/eme-controller */
-
-/*! ./controller/fps-controller */
-
-/*! ./controller/fragment-tracker */
-
-/*! ./controller/id3-track-controller */
-
-/*! ./controller/latency-controller */
-
-/*! ./controller/level-controller */
-
-/*! ./controller/stream-controller */
-
-/*! ./controller/subtitle-stream-controller */
-
-/*! ./controller/subtitle-track-controller */
-
-/*! ./controller/timeline-controller */
-
-/*! ./date-range */
-
-/*! ./dummy-demuxed-track */
-
-/*! ./errors */
-
-/*! ./events */
-
-/*! ./exp-golomb */
-
-/*! ./fast-aes-key */
-
-/*! ./fragment */
-
-/*! ./fragment-finders */
-
-/*! ./fragment-loader */
-
-/*! ./fragment-tracker */
-
-/*! ./gap-controller */
-
-/*! ./hex */
-
-/*! ./is-supported */
-
-/*! ./level-details */
-
-/*! ./level-helper */
-
-/*! ./level-key */
-
-/*! ./load-stats */
-
-/*! ./loader/key-loader */
-
-/*! ./loader/playlist-loader */
-
-/*! ./logger */
-
-/*! ./m3u8-parser */
-
-/*! ./mp4-generator */
-
-/*! ./mp4-remuxer */
-
-/*! ./mp4-tools */
-
-/*! ./mpegaudio */
-
-/*! ./numeric-encoding-utils */
-
-/*! ./sample-aes */
-
-/*! ./src/polyfills/number */
-
-/*! ./texttrack-utils */
-
-/*! ./timescale-conversion */
-
-/*! ./typed-array */
-
-/*! ./types/level */
-
-/*! ./utils/cues */
-
-/*! ./utils/fetch-loader */
-
-/*! ./utils/logger */
-
-/*! ./utils/mediakeys-helper */
-
-/*! ./utils/mediasource-helper */
-
-/*! ./utils/xhr-loader */
-
-/*! ./vttcue */
-
-/*! ./vttparser */
-
-/*! ./webvtt-parser */
-
-/*! ./webworkify-webpack */
-
-/*! eventemitter3 */
-
/*! https://mths.be/punycode v1.4.1 by @mathias */
-/*! url-toolkit */
-
-/*!********************!*\
- !*** ./src/hls.ts ***!
- \********************/
-
-/*!***********************!*\
- !*** ./src/config.ts ***!
- \***********************/
-
-/*!***********************!*\
- !*** ./src/errors.ts ***!
- \***********************/
-
-/*!***********************!*\
- !*** ./src/events.ts ***!
- \***********************/
-
-/*!**************************!*\
- !*** ./src/demux/id3.ts ***!
- \**************************/
-
-/*!**************************!*\
- !*** ./src/task-loop.ts ***!
- \**************************/
-
-/*!**************************!*\
- !*** ./src/utils/hex.ts ***!
- \**************************/
-
-/*!***************************!*\
- !*** ./src/demux/adts.ts ***!
- \***************************/
-
-/*!***************************!*\
- !*** ./src/types/cmcd.ts ***!
- \***************************/
-
-/*!***************************!*\
- !*** ./src/utils/cues.ts ***!
- \***************************/
-
-/*!***************************!*\
- !*** ./src/utils/ewma.ts ***!
- \***************************/
-
-/*!****************************!*\
- !*** ./src/types/level.ts ***!
- \****************************/
-
-/*!*****************************!*\
- !*** ./src/is-supported.ts ***!
- \*****************************/
-
-/*!*****************************!*\
- !*** ./src/types/loader.ts ***!
- \*****************************/
-
-/*!*****************************!*\
- !*** ./src/utils/codecs.ts ***!
- \*****************************/
-
-/*!*****************************!*\
- !*** ./src/utils/logger.ts ***!
- \*****************************/
-
-/*!*****************************!*\
- !*** ./src/utils/vttcue.ts ***!
- \*****************************/
-
-/*!******************************!*\
- !*** ./src/types/demuxer.ts ***!
- \******************************/
-
-/*!********************************!*\
- !*** ./src/crypt/decrypter.ts ***!
- \********************************/
-
-/*!********************************!*\
- !*** ./src/demux/mpegaudio.ts ***!
- \********************************/
-
-/*!********************************!*\
- !*** ./src/demux/tsdemuxer.ts ***!
- \********************************/
-
-/*!********************************!*\
- !*** ./src/loader/fragment.ts ***!
- \********************************/
-
-/*!********************************!*\
- !*** ./src/utils/attr-list.ts ***!
- \********************************/
-
-/*!********************************!*\
- !*** ./src/utils/mp4-tools.ts ***!
- \********************************/
-
-/*!********************************!*\
- !*** ./src/utils/vttparser.ts ***!
- \********************************/
-
-/*!*********************************!*\
- !*** ./src/crypt/aes-crypto.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/demux/aacdemuxer.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/demux/exp-golomb.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/demux/mp3demuxer.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/demux/mp4demuxer.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/demux/sample-aes.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/demux/transmuxer.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/loader/level-key.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/polyfills/number.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/remux/aac-helper.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/types/transmuxer.ts ***!
- \*********************************/
-
-/*!*********************************!*\
- !*** ./src/utils/xhr-loader.ts ***!
- \*********************************/
-
-/*!**********************************!*\
- !*** ./src/demux/chunk-cache.ts ***!
- \**********************************/
-
-/*!**********************************!*\
- !*** ./src/loader/date-range.ts ***!
- \**********************************/
-
-/*!**********************************!*\
- !*** ./src/loader/key-loader.ts ***!
- \**********************************/
-
-/*!**********************************!*\
- !*** ./src/loader/load-stats.ts ***!
- \**********************************/
-
-/*!**********************************!*\
- !*** ./src/remux/mp4-remuxer.ts ***!
- \**********************************/
-
-/*!**********************************!*\
- !*** ./src/utils/time-ranges.ts ***!
- \**********************************/
-
-/*!**********************************!*\
- !*** ./src/utils/typed-array.ts ***!
- \**********************************/
-
-/*!***********************************!*\
- !*** ./src/crypt/fast-aes-key.ts ***!
- \***********************************/
-
-/*!***********************************!*\
- !*** ./src/loader/m3u8-parser.ts ***!
- \***********************************/
-
-/*!***********************************!*\
- !*** ./src/utils/fetch-loader.ts ***!
- \***********************************/
-
-/*!************************************!*\
- !*** ./src/crypt/aes-decryptor.ts ***!
- \************************************/
-
-/*!************************************!*\
- !*** ./src/remux/mp4-generator.ts ***!
- \************************************/
-
-/*!************************************!*\
- !*** ./src/utils/binary-search.ts ***!
- \************************************/
-
-/*!************************************!*\
- !*** ./src/utils/buffer-helper.ts ***!
- \************************************/
-
-/*!************************************!*\
- !*** ./src/utils/output-filter.ts ***!
- \************************************/
-
-/*!************************************!*\
- !*** ./src/utils/webvtt-parser.ts ***!
- \************************************/
-
-/*!*************************************!*\
- !*** ./src/loader/level-details.ts ***!
- \*************************************/
-
-/*!*************************************!*\
- !*** ./src/utils/cea-608-parser.ts ***!
- \*************************************/
-
-/*!*************************************!*\
- !*** ./src/utils/keysystem-util.ts ***!
- \*************************************/
-
-/*!**************************************!*\
- !*** ./src/utils/discontinuities.ts ***!
- \**************************************/
-
-/*!**************************************!*\
- !*** ./src/utils/texttrack-utils.ts ***!
- \**************************************/
-
-/*!***************************************!*\
- !*** ./src/loader/fragment-loader.ts ***!
- \***************************************/
-
-/*!***************************************!*\
- !*** ./src/loader/playlist-loader.ts ***!
- \***************************************/
-
-/*!***************************************!*\
- !*** ./src/utils/mediakeys-helper.ts ***!
- \***************************************/
-
-/*!****************************************!*\
- !*** ./src/controller/level-helper.ts ***!
- \****************************************/
-
-/*!****************************************!*\
- !*** ./src/demux/transmuxer-worker.ts ***!
- \****************************************/
-
-/*!****************************************!*\
- !*** ./src/utils/imsc1-ttml-parser.ts ***!
- \****************************************/
-
-/*!*****************************************!*\
- !*** ./src/demux/base-audio-demuxer.ts ***!
- \*****************************************/
-
-/*!*****************************************!*\
- !*** ./src/demux/webworkify-webpack.js ***!
- \*****************************************/
-
-/*!*****************************************!*\
- !*** ./src/utils/mediasource-helper.ts ***!
- \*****************************************/
-
-/*!******************************************!*\
- !*** ./src/controller/abr-controller.ts ***!
- \******************************************/
-
-/*!******************************************!*\
- !*** ./src/controller/eme-controller.ts ***!
- \******************************************/
-
-/*!******************************************!*\
- !*** ./src/controller/fps-controller.ts ***!
- \******************************************/
-
-/*!******************************************!*\
- !*** ./src/controller/gap-controller.ts ***!
- \******************************************/
-
-/*!******************************************!*\
- !*** ./src/demux/dummy-demuxed-track.ts ***!
- \******************************************/
-
-/*!******************************************!*\
- !*** ./src/remux/passthrough-remuxer.ts ***!
- \******************************************/
-
-/*!*******************************************!*\
- !*** ./src/controller/cmcd-controller.ts ***!
- \*******************************************/
-
-/*!*******************************************!*\
- !*** ./src/demux/transmuxer-interface.ts ***!
- \*******************************************/
-
-/*!*******************************************!*\
- !*** ./src/utils/timescale-conversion.ts ***!
- \*******************************************/
-
-/*!********************************************!*\
- !*** ./src/controller/fragment-finders.ts ***!
- \********************************************/
-
-/*!********************************************!*\
- !*** ./src/controller/fragment-tracker.ts ***!
- \********************************************/
-
-/*!********************************************!*\
- !*** ./src/controller/level-controller.ts ***!
- \********************************************/
-
-/*!*********************************************!*\
- !*** ./node_modules/eventemitter3/index.js ***!
- \*********************************************/
-
-/*!*********************************************!*\
- !*** ./src/controller/buffer-controller.ts ***!
- \*********************************************/
-
-/*!*********************************************!*\
- !*** ./src/controller/stream-controller.ts ***!
- \*********************************************/
-
-/*!*********************************************!*\
- !*** ./src/utils/numeric-encoding-utils.ts ***!
- \*********************************************/
-
-/*!**********************************************!*\
- !*** ./src/controller/latency-controller.ts ***!
- \**********************************************/
-
-/*!***********************************************!*\
- !*** ./src/controller/timeline-controller.ts ***!
- \***********************************************/
-
-/*!***********************************************!*\
- !*** ./src/utils/ewma-bandwidth-estimator.ts ***!
- \***********************************************/
-
-/*!************************************************!*\
- !*** ./src/controller/cap-level-controller.ts ***!
- \************************************************/
-
-/*!************************************************!*\
- !*** ./src/controller/id3-track-controller.ts ***!
- \************************************************/
-
-/*!**************************************************!*\
- !*** ./src/controller/audio-track-controller.ts ***!
- \**************************************************/
-
-/*!**************************************************!*\
- !*** ./src/controller/base-stream-controller.ts ***!
- \**************************************************/
-
-/*!**************************************************!*\
- !*** ./src/controller/buffer-operation-queue.ts ***!
- \**************************************************/
-
-/*!***************************************************!*\
- !*** ./src/controller/audio-stream-controller.ts ***!
- \***************************************************/
-
-/*!****************************************************!*\
- !*** ./src/controller/base-playlist-controller.ts ***!
- \****************************************************/
-
-/*!*****************************************************!*\
- !*** ./node_modules/url-toolkit/src/url-toolkit.js ***!
- \*****************************************************/
-
-/*!*****************************************************!*\
- !*** ./src/controller/subtitle-track-controller.ts ***!
- \*****************************************************/
-
-/*!******************************************************!*\
- !*** ./src/controller/subtitle-stream-controller.ts ***!
- \******************************************************/
-
/**
* vue-class-component v7.2.3
* (c) 2015-present Evan You
diff --git a/public/mix-manifest.json b/public/mix-manifest.json
index 2675c6ea9..8520787b0 100644
Binary files a/public/mix-manifest.json and b/public/mix-manifest.json differ
diff --git a/resources/assets/components/Post.vue b/resources/assets/components/Post.vue
index 842b15dd5..1cc57c84e 100644
--- a/resources/assets/components/Post.vue
+++ b/resources/assets/components/Post.vue
@@ -26,7 +26,7 @@
+
+
@@ -119,6 +125,7 @@
import LikesModal from './partials/post/LikeModal.vue';
import SharesModal from './partials/post/ShareModal.vue';
import ReportModal from './partials/modal/ReportPost.vue';
+ import PostEditModal from './partials/post/PostEditModal.vue';
export default {
props: {
@@ -140,7 +147,8 @@
"likes-modal": LikesModal,
"shares-modal": SharesModal,
"rightbar": Rightbar,
- "report-modal": ReportModal
+ "report-modal": ReportModal,
+ "post-edit-modal": PostEditModal
},
data() {
@@ -156,7 +164,8 @@
isReply: false,
reply: {},
showSharesModal: false,
- postStateError: false
+ postStateError: false,
+ forceUpdateIdx: 0
}
},
@@ -405,6 +414,17 @@
break;
}
},
+
+ handleEdit(status) {
+ this.$refs.editModal.show(status);
+ },
+
+ mergeUpdatedPost(post) {
+ this.post = post;
+ this.$nextTick(() => {
+ this.forceUpdateIdx++;
+ });
+ }
}
}
diff --git a/resources/assets/components/partials/post/EditHistoryModal.vue b/resources/assets/components/partials/post/EditHistoryModal.vue
new file mode 100644
index 000000000..67cf89e0a
--- /dev/null
+++ b/resources/assets/components/partials/post/EditHistoryModal.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
Post History
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ allHistory[0].account.username }}
+
+
+
{{ historyIndex == (allHistory.length - 1) ? 'created' : 'edited' }} {{ formatTime(allHistory[historyIndex].created_at) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ history.account.username }}
+
+
{{ idx == (allHistory.length - 1) ? 'created' : 'edited' }} {{ formatTime(history.created_at) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/components/partials/post/PostHeader.vue b/resources/assets/components/partials/post/PostHeader.vue
new file mode 100644
index 000000000..ddbbf740c
--- /dev/null
+++ b/resources/assets/components/partials/post/PostHeader.vue
@@ -0,0 +1,348 @@
+
+
+
+
+
diff --git a/resources/assets/sass/admin.scss b/resources/assets/sass/admin.scss
index 93c513f82..cb01e4c69 100644
--- a/resources/assets/sass/admin.scss
+++ b/resources/assets/sass/admin.scss
@@ -13,3 +13,23 @@ body, button, input, textarea {
font-size: 30px;
}
}
+
+.nav-pills .nav-item {
+ padding-right: 1rem;
+}
+
+.list-fade-bottom {
+ position: relative;
+
+ &:after {
+ content: "";
+ position: absolute;
+ z-index: 1;
+ bottom: 0;
+ left: 0;
+ pointer-events: none;
+ background-image: linear-gradient(to bottom, rgba(255,255,255, 0), rgba(255,255,255, 1) 90%);
+ width: 100%;
+ height: 10em;
+ }
+}
diff --git a/resources/assets/sass/spa.scss b/resources/assets/sass/spa.scss
index 3fea3e7e2..e2ed0e054 100644
--- a/resources/assets/sass/spa.scss
+++ b/resources/assets/sass/spa.scss
@@ -210,6 +210,7 @@ a.text-dark:hover {
.autocomplete-result-list {
background: var(--light) !important;
+ z-index: 2 !important;
}
.dropdown-menu,
@@ -261,7 +262,8 @@ span.twitter-typeahead .tt-suggestion:focus {
border-color: var(--border-color) !important;
}
-.modal-header {
+.modal-header,
+.modal-footer {
border-color: var(--border-color);
}
@@ -328,3 +330,66 @@ span.twitter-typeahead .tt-suggestion:focus {
}
}
}
+
+.compose-modal-component {
+ .form-control:focus {
+ color: var(--body-color);
+ }
+}
+
+.modal-body {
+ .nav-tabs .nav-link.active,
+ .nav-tabs .nav-item.show .nav-link {
+ background-color: transparent;
+ border-color: var(--border-color);
+ }
+
+ .nav-tabs .nav-link:hover,
+ .nav-tabs .nav-link:focus {
+ border-color: var(--border-color);
+ }
+
+ .form-control:focus {
+ color: var(--body-color);
+ }
+}
+
+.tribute-container {
+ border: 0;
+
+ ul {
+ margin-top: 0;
+ border-color: var(--border-color);
+ }
+
+ li {
+ padding: 0.5rem 1rem;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
+ font-size: 13px;
+
+ &:not(:last-child) {
+ border-bottom: 1px solid var(--border-color);
+ }
+
+ &.highlight,
+ &:hover {
+ color: var(--body-color);
+ font-weight: bold;
+ background: rgba(44, 120, 191, 0.25);
+ }
+ }
+}
+
+.timeline-status-component {
+ .username {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ margin-bottom: -3px;
+ word-break: break-word;
+
+ @media (min-width: 768px) {
+ font-size: 17px;
+ }
+ }
+}
diff --git a/routes/api.php b/routes/api.php
index 7e996df1d..a0b8b5155 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -94,6 +94,9 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::post('tags/{id}/follow', 'Api\ApiV1Controller@followHashtag')->middleware($middleware);
Route::post('tags/{id}/unfollow', 'Api\ApiV1Controller@unfollowHashtag')->middleware($middleware);
Route::get('tags/{id}', 'Api\ApiV1Controller@getHashtag')->middleware($middleware);
+
+ Route::get('statuses/{id}/history', 'StatusEditController@history')->middleware($middleware);
+ Route::put('statuses/{id}', 'StatusEditController@store')->middleware($middleware);
});
Route::group(['prefix' => 'v2'], function() use($middleware) {