1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/pixelfed_ynh.git synced 2024-09-03 20:06:04 +02:00

Merge pull request #1461 from pixelfed/frontend-ui-refactor

ActivityPub improvements (Announce + Like)
This commit is contained in:
daniel 2019-06-25 00:35:51 -06:00 committed by GitHub
commit d316a647c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 156 additions and 137 deletions

View file

@ -56,7 +56,7 @@ class SearchController extends Controller
] ]
]]; ]];
} else if ($type == 'Note') { } else if ($type == 'Note') {
$item = Helpers::statusFirstOrFetch($tag, false); $item = Helpers::statusFetch($tag);
$tokens['posts'] = [[ $tokens['posts'] = [[
'count' => 0, 'count' => 0,
'url' => $item->url(), 'url' => $item->url(),

View file

@ -30,11 +30,12 @@ class StatusController extends Controller
} }
$status = Status::whereProfileId($user->id) $status = Status::whereProfileId($user->id)
->whereNull('reblog_of_id')
->whereNotIn('visibility',['draft','direct']) ->whereNotIn('visibility',['draft','direct'])
->findOrFail($id); ->findOrFail($id);
if($status->uri) { if($status->uri || $status->url) {
$url = $status->uri; $url = $status->uri ?? $status->url;
if(ends_with($url, '/activity')) { if(ends_with($url, '/activity')) {
$url = str_replace('/activity', '', $url); $url = str_replace('/activity', '', $url);
} }
@ -102,109 +103,6 @@ class StatusController extends Controller
public function store(Request $request) public function store(Request $request)
{ {
return; return;
$this->authCheck();
$user = Auth::user();
$size = Media::whereUserId($user->id)->sum('size') / 1000;
$limit = (int) config('pixelfed.max_account_size');
if ($size >= $limit) {
return redirect()->back()->with('error', 'You have exceeded your storage limit. Please click <a href="#">here</a> for more info.');
}
$this->validate($request, [
'photo.*' => 'required|mimetypes:' . config('pixelfed.media_types').'|max:' . config('pixelfed.max_photo_size'),
'caption' => 'nullable|string|max:'.config('pixelfed.max_caption_length'),
'cw' => 'nullable|string',
'filter_class' => 'nullable|alpha_dash|max:30',
'filter_name' => 'nullable|string',
'visibility' => 'required|string|min:5|max:10',
]);
if (count($request->file('photo')) > config('pixelfed.max_album_length')) {
return redirect()->back()->with('error', 'Too many files, max limit per post: '.config('pixelfed.max_album_length'));
}
$cw = $request->filled('cw') && $request->cw == 'on' ? true : false;
$monthHash = hash('sha1', date('Y').date('m'));
$userHash = hash('sha1', $user->id.(string) $user->created_at);
$profile = $user->profile;
$visibility = $this->validateVisibility($request->visibility);
$cw = $profile->cw == true ? true : $cw;
$visibility = $profile->unlisted == true && $visibility == 'public' ? 'unlisted' : $visibility;
if(config('costar.enabled') == true) {
$blockedKeywords = config('costar.keyword.block');
if($blockedKeywords !== null) {
$keywords = config('costar.keyword.block');
foreach($keywords as $kw) {
if(Str::contains($request->caption, $kw) == true) {
abort(400, 'Invalid object');
}
}
}
}
$status = new Status();
$status->profile_id = $profile->id;
$status->caption = strip_tags($request->caption);
$status->is_nsfw = $cw;
// TODO: remove deprecated visibility in favor of scope
$status->visibility = $visibility;
$status->scope = $visibility;
$status->save();
$photos = $request->file('photo');
$order = 1;
$mimes = [];
$medias = 0;
foreach ($photos as $k => $v) {
$allowedMimes = explode(',', config('pixelfed.media_types'));
if(in_array($v->getMimeType(), $allowedMimes) == false) {
continue;
}
$filter_class = $request->input('filter_class');
$filter_name = $request->input('filter_name');
$storagePath = "public/m/{$monthHash}/{$userHash}";
$path = $v->store($storagePath);
$hash = \hash_file('sha256', $v);
$media = new Media();
$media->status_id = $status->id;
$media->profile_id = $profile->id;
$media->user_id = $user->id;
$media->media_path = $path;
$media->original_sha256 = $hash;
$media->size = $v->getSize();
$media->mime = $v->getMimeType();
$media->filter_class = in_array($filter_class, Filter::classes()) ? $filter_class : null;
$media->filter_name = in_array($filter_name, Filter::names()) ? $filter_name : null;
$media->order = $order;
$media->save();
array_push($mimes, $media->mime);
ImageOptimize::dispatch($media);
$order++;
$medias++;
}
if($medias == 0) {
$status->delete();
return;
}
$status->type = (new self)::mimeTypeCheck($mimes);
$status->save();
Cache::forget('profile:status_count:'.$profile->id);
NewStatusPipeline::dispatch($status);
// TODO: Send to subscribers
return redirect($status->url());
} }
public function delete(Request $request) public function delete(Request $request)
@ -238,7 +136,9 @@ class StatusController extends Controller
$user = Auth::user(); $user = Auth::user();
$profile = $user->profile; $profile = $user->profile;
$status = Status::withCount('shares')->findOrFail($request->input('item')); $status = Status::withCount('shares')
->whereIn('scope', ['public', 'unlisted'])
->findOrFail($request->input('item'));
$count = $status->shares_count; $count = $status->shares_count;

View file

@ -2,16 +2,17 @@
namespace App\Jobs\LikePipeline; namespace App\Jobs\LikePipeline;
use App\Like; use Cache, Log, Redis;
use App\Notification; use App\{Like, Notification};
use Cache;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Log; use App\Util\ActivityPub\Helpers;
use Redis; use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use App\Transformer\ActivityPub\Verb\Like as LikeTransformer;
class LikePipeline implements ShouldQueue class LikePipeline implements ShouldQueue
{ {
@ -48,11 +49,15 @@ class LikePipeline implements ShouldQueue
$status = $this->like->status; $status = $this->like->status;
$actor = $this->like->actor; $actor = $this->like->actor;
if (!$status || $status->url !== null) { if (!$status) {
// Ignore notifications to remote statuses, or deleted statuses // Ignore notifications to deleted statuses
return; return;
} }
if($status->url && $actor->domain == null) {
return $this->remoteLikeDeliver();
}
$exists = Notification::whereProfileId($status->profile_id) $exists = Notification::whereProfileId($status->profile_id)
->whereActorId($actor->id) ->whereActorId($actor->id)
->whereAction('like') ->whereAction('like')
@ -78,4 +83,20 @@ class LikePipeline implements ShouldQueue
} catch (Exception $e) { } catch (Exception $e) {
} }
} }
public function remoteLikeDeliver()
{
$like = $this->like;
$status = $this->like->status;
$actor = $this->like->actor;
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($like, new LikeTransformer());
$activity = $fractal->createData($resource)->toArray();
$url = $status->profile->sharedInbox ?? $status->profile->inbox_url;
Helpers::sendSignedObject($actor, $url, $activity);
}
} }

View file

@ -9,6 +9,11 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use App\Transformer\ActivityPub\Verb\Announce;
use GuzzleHttp\{Pool, Client, Promise};
use App\Util\ActivityPub\HttpSignature;
class SharePipeline implements ShouldQueue class SharePipeline implements ShouldQueue
{ {
@ -60,6 +65,8 @@ class SharePipeline implements ShouldQueue
return true; return true;
} }
$this->remoteAnnounceDeliver();
try { try {
$notification = new Notification; $notification = new Notification;
$notification->profile_id = $target->id; $notification->profile_id = $target->id;
@ -78,4 +85,56 @@ class SharePipeline implements ShouldQueue
Log::error($e); Log::error($e);
} }
} }
public function remoteAnnounceDeliver()
{
$status = $this->status;
$profile = $status->profile;
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($status, new Announce());
$activity = $fractal->createData($resource)->toArray();
$audience = $status->profile->getAudienceInbox();
if(empty($audience) || $status->scope != 'public') {
// Return on profiles with no remote followers
return;
}
$payload = json_encode($activity);
$client = new Client([
'timeout' => config('federation.activitypub.delivery.timeout')
]);
$requests = function($audience) use ($client, $activity, $profile, $payload) {
foreach($audience as $url) {
$headers = HttpSignature::sign($profile, $url, $activity);
yield function() use ($client, $url, $headers, $payload) {
return $client->postAsync($url, [
'curl' => [
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HEADER => true
]
]);
};
}
};
$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();
}
} }

View file

@ -49,6 +49,7 @@ class StatusActivityPubDeliver implements ShouldQueue
public function handle() public function handle()
{ {
$status = $this->status; $status = $this->status;
$profile = $status->profile;
if($status->local == false || $status->url || $status->uri) { if($status->local == false || $status->url || $status->uri) {
return; return;
@ -56,12 +57,11 @@ class StatusActivityPubDeliver implements ShouldQueue
$audience = $status->profile->getAudienceInbox(); $audience = $status->profile->getAudienceInbox();
if(empty($audience) || $status->visibility != 'public') { if(empty($audience) || $status->scope != 'public') {
// Return on profiles with no remote followers // Return on profiles with no remote followers
return; return;
} }
$profile = $status->profile;
$fractal = new Fractal\Manager(); $fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer()); $fractal->setSerializer(new ArraySerializer());

View file

@ -11,9 +11,16 @@ class Announce extends Fractal\TransformerAbstract
{ {
return [ return [
'@context' => 'https://www.w3.org/ns/activitystreams', '@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $status->permalink(),
'type' => 'Announce', 'type' => 'Announce',
'actor' => $status->profile->permalink(), 'actor' => $status->profile->permalink(),
'object' => $status->parent()->url() 'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'cc' => [
$status->profile->permalink(),
$status->profile->follower_url ?? $status->profile->permalink('/followers')
],
'published' => $status->created_at->format(DATE_ISO8601),
'object' => $status->parent()->url(),
]; ];
} }
} }

View file

@ -11,6 +11,7 @@ class Like extends Fractal\TransformerAbstract
{ {
return [ return [
'@context' => 'https://www.w3.org/ns/activitystreams', '@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $like->actor->permalink('#likes/'.$like->id),
'type' => 'Like', 'type' => 'Like',
'actor' => $like->actor->permalink(), 'actor' => $like->actor->permalink(),
'object' => $like->status->url() 'object' => $like->status->url()

View file

@ -34,7 +34,7 @@ class StatusTransformer extends Fractal\TransformerAbstract
'muted' => null, 'muted' => null,
'sensitive' => (bool) $status->is_nsfw, 'sensitive' => (bool) $status->is_nsfw,
'spoiler_text' => $status->cw_summary, 'spoiler_text' => $status->cw_summary,
'visibility' => $status->visibility, 'visibility' => $status->visibility ?? $status->scope,
'application' => [ 'application' => [
'name' => 'web', 'name' => 'web',
'website' => null 'website' => null

View file

@ -206,7 +206,7 @@ class Helpers {
return self::fetchFromUrl($url); return self::fetchFromUrl($url);
} }
public static function statusFirstOrFetch($url, $replyTo = true) public static function statusFirstOrFetch($url, $replyTo = false)
{ {
$url = self::validateUrl($url); $url = self::validateUrl($url);
if($url == false) { if($url == false) {
@ -337,6 +337,11 @@ class Helpers {
} }
} }
public static function statusFetch($url)
{
return self::statusFirstOrFetch($url);
}
public static function importNoteAttachment($data, Status $status) public static function importNoteAttachment($data, Status $status)
{ {
if(self::verifyAttachments($data) == false) { if(self::verifyAttachments($data) == false) {
@ -435,6 +440,11 @@ class Helpers {
return $profile; return $profile;
} }
public static function profileFetch($url)
{
return self::profileFirstOrNew($url);
}
public static function sendSignedObject($senderProfile, $url, $body) public static function sendSignedObject($senderProfile, $url, $body)
{ {
abort_if(!self::validateUrl($url), 400); abort_if(!self::validateUrl($url), 400);

View file

@ -151,7 +151,7 @@ class Inbox
if(Status::whereUrl($url)->exists()) { if(Status::whereUrl($url)->exists()) {
return; return;
} }
Helpers::statusFirstOrFetch($url, false); Helpers::statusFetch($url);
return; return;
} }
@ -205,21 +205,27 @@ class Inbox
{ {
$actor = $this->actorFirstOrCreate($this->payload['actor']); $actor = $this->actorFirstOrCreate($this->payload['actor']);
$activity = $this->payload['object']; $activity = $this->payload['object'];
if(!$actor || $actor->domain == null) { if(!$actor || $actor->domain == null) {
return; return;
} }
if(Helpers::validateLocalUrl($activity) == false) { if(Helpers::validateLocalUrl($activity) == false) {
return; return;
} }
$parent = Helpers::statusFirstOrFetch($activity, true);
if(!$parent) { $parent = Helpers::statusFetch($activity);
if(empty($parent)) {
return; return;
} }
$status = Status::firstOrCreate([ $status = Status::firstOrCreate([
'profile_id' => $actor->id, 'profile_id' => $actor->id,
'reblog_of_id' => $parent->id, 'reblog_of_id' => $parent->id,
'type' => 'reply' 'type' => 'share'
]); ]);
Notification::firstOrCreate([ Notification::firstOrCreate([
'profile_id' => $parent->profile->id, 'profile_id' => $parent->profile->id,
'actor_id' => $actor->id, 'actor_id' => $actor->id,
@ -229,6 +235,7 @@ class Inbox
'item_id' => $parent->id, 'item_id' => $parent->id,
'item_type' => 'App\Status' 'item_type' => 'App\Status'
]); ]);
$parent->reblogs_count = $parent->shares()->count(); $parent->reblogs_count = $parent->shares()->count();
$parent->save(); $parent->save();
} }
@ -316,6 +323,20 @@ class Inbox
break; break;
case 'Announce': case 'Announce':
abort_if(!Helpers::validateLocalUrl($obj), 400);
$status = Helpers::statusFetch($obj);
if(!$status) {
return;
}
Status::whereProfileId($profile->id)
->whereReblogOfId($status->id)
->forceDelete();
Notification::whereProfileId($status->profile->id)
->whereActorId($profile->id)
->whereAction('share')
->whereItemId($status->reblog_of_id)
->whereItemType('App\Status')
->forceDelete();
break; break;
case 'Block': case 'Block':
@ -347,6 +368,6 @@ class Inbox
->forceDelete(); ->forceDelete();
break; break;
} }
return;
} }
} }

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

View file

@ -14,10 +14,10 @@
"/js/discover.js": "/js/discover.js?id=772fbc6c176aaa9039ec", "/js/discover.js": "/js/discover.js?id=772fbc6c176aaa9039ec",
"/js/loops.js": "/js/loops.js?id=a2291e15b8b246621b07", "/js/loops.js": "/js/loops.js?id=a2291e15b8b246621b07",
"/js/mode-dot.js": "/js/mode-dot.js?id=3c67c8f827dc52536a4b", "/js/mode-dot.js": "/js/mode-dot.js?id=3c67c8f827dc52536a4b",
"/js/profile.js": "/js/profile.js?id=fe87d1e6d75323c9f7d9", "/js/profile.js": "/js/profile.js?id=c45f4c00d9e3c9aac7c9",
"/js/quill.js": "/js/quill.js?id=d2e82cff48024d45384a", "/js/quill.js": "/js/quill.js?id=d2e82cff48024d45384a",
"/js/search.js": "/js/search.js?id=38a3a6a07e3b37a1e444", "/js/search.js": "/js/search.js?id=38a3a6a07e3b37a1e444",
"/js/status.js": "/js/status.js?id=8333e903aaf01fc6436d", "/js/status.js": "/js/status.js?id=28de331daaf3447a3d3f",
"/js/theme-monokai.js": "/js/theme-monokai.js?id=1f7b44b76af99c0b4e38", "/js/theme-monokai.js": "/js/theme-monokai.js?id=1f7b44b76af99c0b4e38",
"/js/timeline.js": "/js/timeline.js?id=3b2c69ca0262332e6542" "/js/timeline.js": "/js/timeline.js?id=52af49a558f589d1b569"
} }

View file

@ -179,14 +179,14 @@
<div class="reactions my-1"> <div class="reactions my-1">
<h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3> <h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
<h3 v-if="!status.comments_disabled" class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3> <h3 v-if="!status.comments_disabled" class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
<h3 v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus"></h3> <h3 v-if="status.visibility == 'public'" v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus"></h3>
<h3 v-bind:class="[reactions.bookmarked ? 'fas fa-bookmark text-warning m-0 float-right cursor-pointer' : 'far fa-bookmark m-0 float-right cursor-pointer']" title="Bookmark" v-on:click="bookmarkStatus"></h3> <h3 v-if="status.visibility == 'public'" v-bind:class="[reactions.bookmarked ? 'fas fa-bookmark text-warning m-0 float-right cursor-pointer' : 'far fa-bookmark m-0 float-right cursor-pointer']" title="Bookmark" v-on:click="bookmarkStatus"></h3>
</div> </div>
<div class="reaction-counts font-weight-bold mb-0"> <div class="reaction-counts font-weight-bold mb-0">
<span style="cursor:pointer;" v-on:click="likesModal"> <span style="cursor:pointer;" v-on:click="likesModal">
<span class="like-count">{{status.favourites_count || 0}}</span> likes <span class="like-count">{{status.favourites_count || 0}}</span> likes
</span> </span>
<span class="float-right" style="cursor:pointer;" v-on:click="sharesModal"> <span v-if="status.visibility == 'public'" class="float-right" style="cursor:pointer;" v-on:click="sharesModal">
<span class="share-count pl-4">{{status.reblogs_count || 0}}</span> shares <span class="share-count pl-4">{{status.reblogs_count || 0}}</span> shares
</span> </span>
</div> </div>
@ -268,13 +268,13 @@
<div class="reactions py-2"> <div class="reactions py-2">
<h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3> <h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
<h3 v-if="!status.comments_disabled" class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3> <h3 v-if="!status.comments_disabled" class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
<h3 v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary float-right cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn float-right cursor-pointer']" title="Share" v-on:click="shareStatus"></h3> <h3 v-if="status.visibility == 'public'" v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary float-right cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn float-right cursor-pointer']" title="Share" v-on:click="shareStatus"></h3>
</div> </div>
<div class="reaction-counts font-weight-bold mb-0"> <div class="reaction-counts font-weight-bold mb-0">
<span style="cursor:pointer;" v-on:click="likesModal"> <span style="cursor:pointer;" v-on:click="likesModal">
<span class="like-count">{{status.favourites_count || 0}}</span> likes <span class="like-count">{{status.favourites_count || 0}}</span> likes
</span> </span>
<span class="float-right" style="cursor:pointer;" v-on:click="sharesModal"> <span v-if="status.visibility == 'public'" class="float-right" style="cursor:pointer;" v-on:click="sharesModal">
<span class="share-count pl-4">{{status.reblogs_count || 0}}</span> shares <span class="share-count pl-4">{{status.reblogs_count || 0}}</span> shares
</span> </span>
</div> </div>

View file

@ -242,7 +242,7 @@
<div class="reactions my-1" v-if="user.hasOwnProperty('id')"> <div class="reactions my-1" v-if="user.hasOwnProperty('id')">
<h3 v-bind:class="[status.favourited ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus(status, $event)"></h3> <h3 v-bind:class="[status.favourited ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus(status, $event)"></h3>
<h3 class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="commentFocus(status, $event)"></h3> <h3 class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="commentFocus(status, $event)"></h3>
<h3 v-bind:class="[status.reblogged ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus(status, $event)"></h3> <h3 v-if="status.visibility == 'public'" v-bind:class="[status.reblogged ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus(status, $event)"></h3>
</div> </div>
<div class="likes font-weight-bold"> <div class="likes font-weight-bold">

View file

@ -117,7 +117,7 @@
<div v-if="!modes.distractionFree" class="reactions my-1"> <div v-if="!modes.distractionFree" class="reactions my-1">
<h3 v-bind:class="[status.favourited ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus(status, $event)"></h3> <h3 v-bind:class="[status.favourited ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus(status, $event)"></h3>
<h3 v-if="!status.comments_disabled" class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="commentFocus(status, $event)"></h3> <h3 v-if="!status.comments_disabled" class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="commentFocus(status, $event)"></h3>
<h3 v-bind:class="[status.reblogged ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus(status, $event)"></h3> <h3 v-if="status.visibility == 'public'" v-bind:class="[status.reblogged ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus(status, $event)"></h3>
</div> </div>
<div class="likes font-weight-bold" v-if="expLc(status) == true && !modes.distractionFree"> <div class="likes font-weight-bold" v-if="expLc(status) == true && !modes.distractionFree">