Merge pull request #4374 from pixelfed/staging

Staging
pull/4394/head
daniel 3 years ago committed by GitHub
commit c643dc307b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -29,6 +29,12 @@
- Update ApiV1Controller, fix hashtag timeline ([fc1a385c](https://github.com/pixelfed/pixelfed/commit/fc1a385c))
- Update settings view, add fallback avatar ([1a83c585](https://github.com/pixelfed/pixelfed/commit/1a83c585))
- Update HashtagFollow model, add MAX_LIMIT of 250 tags per account ([ed352141](https://github.com/pixelfed/pixelfed/commit/ed352141))
- Update Notification logic, remove message and rendered fields ([6cdb5bc6](https://github.com/pixelfed/pixelfed/commit/6cdb5bc6))
- Update InstanceService, fix banner blurhash memory bug ([3aad75ab](https://github.com/pixelfed/pixelfed/commit/3aad75ab))
- Update models, remove deprecated toText and toHtml method ([ea943333](https://github.com/pixelfed/pixelfed/commit/ea943333))
- Update Notification components, add autospam notification support ([0d3b4bc2](https://github.com/pixelfed/pixelfed/commit/0d3b4bc2))
- Update AutoSpam Bouncer, generate notification on positive detections ([d5f63f8a](https://github.com/pixelfed/pixelfed/commit/d5f63f8a))
- Update admin autospam apis, remove autospam warning notifications when appropriate ([588ca653](https://github.com/pixelfed/pixelfed/commit/588ca653))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.6 (2023-05-03)](https://github.com/pixelfed/pixelfed/compare/v0.11.5...v0.11.6)

@ -31,20 +31,4 @@ class DirectMessage extends Model
{
return Auth::user()->profile->id === $this->from_id;
}
public function toText()
{
$actorName = $this->author->username;
return "{$actorName} sent a direct message.";
}
public function toHtml()
{
$actorName = $this->author->username;
$actorUrl = $this->author->url();
$url = $this->url();
return "{$actorName} sent a direct message.";
}
}

@ -32,20 +32,4 @@ class Follower extends Model
$path = $this->actor->permalink("#accepts/follows/{$this->id}{$append}");
return url($path);
}
public function toText()
{
$actorName = $this->actor->username;
return "{$actorName} ".__('notification.startedFollowingYou');
}
public function toHtml()
{
$actorName = $this->actor->username;
$actorUrl = $this->actor->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
__('notification.startedFollowingYou');
}
}

@ -14,6 +14,7 @@ use App\{
Contact,
Hashtag,
Newsroom,
Notification,
OauthClient,
Profile,
Report,
@ -30,6 +31,7 @@ use App\Jobs\DeletePipeline\DeleteRemoteStatusPipeline;
use App\Jobs\StatusPipeline\StatusDelete;
use App\Http\Resources\AdminReport;
use App\Http\Resources\AdminSpamReport;
use App\Services\NotificationService;
use App\Services\PublicTimelineService;
use App\Services\NetworkTimelineService;
@ -1126,6 +1128,14 @@ trait AdminReportController
$appeal->appeal_handled_at = now();
$appeal->save();
Notification::whereAction('autospam.warning')
->whereProfileId($appeal->user->profile_id)
->get()
->each(function($n) use($appeal) {
NotificationService::del($appeal->user->profile_id, $n->id);
$n->forceDelete();
});
StatusService::del($status->id);
}
@ -1157,6 +1167,13 @@ trait AdminReportController
$status->save();
StatusService::del($status->id);
}
Notification::whereAction('autospam.warning')
->whereProfileId($report->user->profile_id)
->get()
->each(function($n) use($report) {
NotificationService::del($report->user->profile_id, $n->id);
$n->forceDelete();
});
});
}

@ -11,6 +11,7 @@ use App\{
AccountInterstitial,
Instance,
Like,
Notification,
Media,
Profile,
Report,
@ -140,6 +141,14 @@ class AdminApiController extends Controller
StatusService::del($status->id);
Notification::whereAction('autospam.warning')
->whereProfileId($appeal->user->profile_id)
->get()
->each(function($n) use($appeal) {
NotificationService::del($appeal->user->profile_id, $n->id);
$n->forceDelete();
});
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
Cache::forget('admin-dash:reports:spam-count');
@ -164,6 +173,14 @@ class AdminApiController extends Controller
$status->save();
StatusService::del($status->id, true);
}
Notification::whereAction('autospam.warning')
->whereProfileId($report->user->profile_id)
->get()
->each(function($n) use($report) {
NotificationService::del($report->user->profile_id, $n->id);
$n->forceDelete();
});
});
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);

@ -368,8 +368,6 @@ class DirectMessageController extends Controller
$notification->profile_id = $recipient->id;
$notification->actor_id = $profile->id;
$notification->action = 'dm';
$notification->message = $dm->toText();
$notification->rendered = $dm->toHtml();
$notification->item_id = $dm->id;
$notification->item_type = "App\DirectMessage";
$notification->save();

@ -328,8 +328,6 @@ class StoryApiV1Controller extends Controller
$n->item_id = $dm->id;
$n->item_type = 'App\DirectMessage';
$n->action = 'story:comment';
$n->message = "{$request->user()->username} commented on story";
$n->rendered = "{$request->user()->username} commented on story";
$n->save();
} else {
StoryReplyDeliver::dispatch($story, $status)->onQueue('story');

@ -442,8 +442,6 @@ class StoryComposeController extends Controller
$n->item_id = $dm->id;
$n->item_type = 'App\DirectMessage';
$n->action = 'story:react';
$n->message = "{$request->user()->username} reacted to your story";
$n->rendered = "{$request->user()->username} reacted to your story";
$n->save();
} else {
StoryReactionDeliver::dispatch($story, $status)->onQueue('story');
@ -516,8 +514,6 @@ class StoryComposeController extends Controller
$n->item_id = $dm->id;
$n->item_type = 'App\DirectMessage';
$n->action = 'story:comment';
$n->message = "{$request->user()->username} commented on story";
$n->rendered = "{$request->user()->username} commented on story";
$n->save();
} else {
StoryReplyDeliver::dispatch($story, $status)->onQueue('story');

@ -94,8 +94,6 @@ class CommentPipeline implements ShouldQueue
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'comment';
$notification->message = $comment->replyToText();
$notification->rendered = $comment->replyToHtml();
$notification->item_id = $comment->id;
$notification->item_type = "App\Status";
$notification->save();

@ -97,8 +97,6 @@ class FollowPipeline implements ShouldQueue
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'follow';
$notification->message = $follower->toText();
$notification->rendered = $follower->toHtml();
$notification->item_id = $target->id;
$notification->item_type = "App\Profile";
$notification->save();

@ -84,8 +84,6 @@ class LikePipeline implements ShouldQueue
$notification->profile_id = $status->profile_id;
$notification->actor_id = $actor->id;
$notification->action = 'like';
$notification->message = $like->toText($status->in_reply_to_id ? 'comment' : 'post');
$notification->rendered = $like->toHtml($status->in_reply_to_id ? 'comment' : 'post');
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();

@ -67,10 +67,6 @@ class MentionPipeline implements ShouldQueue
'action' => 'mention',
'item_type' => 'App\Status',
'item_id' => $status->id,
],
[
'message' => $mention->toText(),
'rendered' => $mention->toHtml()
]
);

@ -76,10 +76,6 @@ class SharePipeline implements ShouldQueue
'action' => 'share',
'item_type' => 'App\Status',
'item_id' => $status->reblog_of_id ?? $status->id,
],
[
'message' => $status->shareToText(),
'rendered' => $status->shareToHtml()
]
);

@ -90,8 +90,6 @@ class StatusReplyPipeline implements ShouldQueue
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'comment';
$notification->message = $status->replyToText();
$notification->rendered = $status->replyToHtml();
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();

@ -31,21 +31,4 @@ class Like extends Model
{
return $this->belongsTo(Status::class);
}
public function toText($type = 'post')
{
$actorName = $this->actor->username;
$msg = $type == 'post' ? __('notification.likedPhoto') : __('notification.likedComment');
return "{$actorName} ".$msg;
}
public function toHtml($type = 'post')
{
$actorName = $this->actor->username;
$actorUrl = $this->actor->url();
$msg = $type == 'post' ? __('notification.likedPhoto') : __('notification.likedComment');
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".$msg;
}
}

@ -29,20 +29,4 @@ class Mention extends Model
{
return $this->belongsTo(Status::class, 'status_id', 'id');
}
public function toText()
{
$actorName = $this->status->profile->username;
return "{$actorName} ".__('notification.mentionedYou');
}
public function toHtml()
{
$actorName = $this->status->profile->username;
$actorUrl = $this->status->profile->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
__('notification.mentionedYou');
}
}

@ -69,6 +69,7 @@ class ConfigCacheService
'instance.landing.show_directory',
'instance.landing.show_explore',
'instance.admin.pid',
'instance.banner.blurhash'
// 'system.user_mode'
];

@ -5,6 +5,7 @@ namespace App\Services;
use Cache;
use App\Instance;
use App\Util\Blurhash\Blurhash;
use App\Services\ConfigCacheService;
class InstanceService
{
@ -13,7 +14,12 @@ class InstanceService
const CACHE_KEY_UNLISTED_DOMAINS = 'instances:unlisted:domains';
const CACHE_KEY_NSFW_DOMAINS = 'instances:auto_cw:domains';
const CACHE_KEY_STATS = 'pf:services:instances:stats';
const CACHE_KEY_BANNER_BLURHASH = 'pf:services:instance:header-blurhash';
const CACHE_KEY_BANNER_BLURHASH = 'pf:services:instance:header-blurhash:v1';
public function __construct()
{
ini_set('memory_limit', config('pixelfed.memory_limit', '1024M'));
}
public static function getByDomain($domain)
{
@ -87,6 +93,12 @@ class InstanceService
if(str_ends_with(config_cache('app.banner_image'), 'headers/default.jpg')) {
return 'UzJR]l{wHZRjM}R%XRkCH?X9xaWEjZj]kAjt';
}
$cached = config_cache('instance.banner.blurhash');
if($cached) {
return $cached;
}
$file = config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg'));
$image = imagecreatefromstring(file_get_contents($file));
@ -115,6 +127,8 @@ class InstanceService
return 'UzJR]l{wHZRjM}R%XRkCH?X9xaWEjZj]kAjt';
}
ConfigCacheService::put('instance.banner.blurhash', $blurhash);
return $blurhash;
});
}

@ -74,16 +74,13 @@ class MediaTagService
{
$p = $tag->status->profile;
$actor = $p->username;
$message = "{$actor} tagged you in a post.";
$rendered = "<a href='/{$actor}' class='profile-link'>{$actor}</a> tagged you in a post.";
$n = new Notification;
$n->profile_id = $tag->profile_id;
$n->actor_id = $p->id;
$n->item_id = $tag->id;
$n->item_type = 'App\MediaTag';
$n->action = 'tagged';
$n->message = $message;
$n->rendered = $rendered;
$n->save();
return;
}

@ -108,8 +108,6 @@ class ModLogService {
{
$log = $this->log;
$msg = "{$log->user_username} commented on a modlog";
$rendered = "<span class='font-weight-bold'>{$log->user_username}</span> commented on a <a href='/i/admin/users/modlogs/{$log->user_id}}' class='font-weight-bold text-decoration-none'>modlog</a>";
$item_id = $log->id;
$item_type = 'App\ModLog';
$action = 'admin.user.modlog.comment';
@ -127,8 +125,6 @@ class ModLogService {
$n->item_id = $item_id;
$n->item_type = $item_type;
$n->action = $action;
$n->message = $msg;
$n->rendered = $rendered;
$n->save();
}
}
@ -139,4 +135,4 @@ class ModLogService {
->whereItemId($this->log->id)
->delete();
}
}
}

@ -285,38 +285,6 @@ class Status extends Model
return $obj;
}
public function replyToText()
{
$actorName = $this->profile->username;
return "{$actorName} ".__('notification.commented');
}
public function replyToHtml()
{
$actorName = $this->profile->username;
$actorUrl = $this->profile->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
__('notification.commented');
}
public function shareToText()
{
$actorName = $this->profile->username;
return "{$actorName} ".__('notification.shared');
}
public function shareToHtml()
{
$actorName = $this->profile->username;
$actorUrl = $this->profile->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
__('notification.shared');
}
public function recentComments()
{
return $this->comments()->orderBy('created_at', 'desc')->take(3);

@ -23,7 +23,9 @@ class NotificationTransformer extends Fractal\TransformerAbstract
if($n->actor_id) {
$res['account'] = AccountService::get($n->actor_id);
$res['relationship'] = RelationshipService::get($n->actor_id, $n->profile_id);
if($n->profile_id != $n->actor_id) {
$res['relationship'] = RelationshipService::get($n->actor_id, $n->profile_id);
}
}
if($n->item_id && $n->item_type == 'App\Status') {
@ -66,11 +68,8 @@ class NotificationTransformer extends Fractal\TransformerAbstract
'comment' => 'comment',
'admin.user.modlog.comment' => 'modlog',
'tagged' => 'tagged',
'group:comment' => 'group:comment',
'story:react' => 'story:react',
'story:comment' => 'story:comment',
'group:join:approved' => 'group:join:approved',
'group:join:rejected' => 'group:join:rejected'
];
if(!isset($verbs[$verb])) {

@ -483,8 +483,6 @@ class Inbox
$notification->profile_id = $profile->id;
$notification->actor_id = $actor->id;
$notification->action = 'dm';
$notification->message = $dm->toText();
$notification->rendered = $dm->toHtml();
$notification->item_id = $dm->id;
$notification->item_type = "App\DirectMessage";
$notification->save();
@ -594,9 +592,6 @@ class Inbox
'action' => 'share',
'item_id' => $parent->id,
'item_type' => 'App\Status',
], [
'message' => $status->replyToText(),
'rendered' => $status->replyToHtml(),
]
);
@ -1023,8 +1018,6 @@ class Inbox
$n->item_id = $dm->id;
$n->item_type = 'App\DirectMessage';
$n->action = 'story:react';
$n->message = "{$actorProfile->username} reacted to your story";
$n->rendered = "{$actorProfile->username} reacted to your story";
$n->save();
return;
@ -1134,8 +1127,6 @@ class Inbox
$n->item_id = $dm->id;
$n->item_type = 'App\DirectMessage';
$n->action = 'story:comment';
$n->message = "{$actorProfile->username} commented on story";
$n->rendered = "{$actorProfile->username} commented on story";
$n->save();
return;

@ -6,8 +6,10 @@ use App\AccountInterstitial;
use App\Status;
use Cache;
use Illuminate\Support\Str;
use App\Services\NotificationService;
use App\Services\StatusService;
use App\Jobs\ReportPipeline\AutospamNotifyAdminViaEmail;
use App\Notification;
class Bouncer {
@ -140,6 +142,15 @@ class Bouncer {
// $status->is_nsfw = true;
$status->save();
$notification = new Notification();
$notification->profile_id = $status->profile_id;
$notification->actor_id = $status->profile_id;
$notification->action = 'autospam.warning';
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();
NotificationService::add($notification->profile_id, $notification->id);
StatusService::del($status->id);
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $status->profile_id);

@ -124,5 +124,9 @@ return [
'landing' => [
'show_directory' => env('INSTANCE_LANDING_SHOW_DIRECTORY', true),
'show_explore' => env('INSTANCE_LANDING_SHOW_EXPLORE', true),
],
'banner' => [
'blurhash' => env('INSTANCE_BANNER_BLURHASH', 'UzJR]l{wHZRjM}R%XRkCH?X9xaWEjZj]kAjt')
]
];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
(()=>{"use strict";var e,r,a,t={},n={};function o(e){var r=n[e];if(void 0!==r)return r.exports;var a=n[e]={id:e,loaded:!1,exports:{}};return t[e].call(a.exports,a,a.exports,o),a.loaded=!0,a.exports}o.m=t,e=[],o.O=(r,a,t,n)=>{if(!a){var d=1/0;for(l=0;l<e.length;l++){for(var[a,t,n]=e[l],c=!0,i=0;i<a.length;i++)(!1&n||d>=n)&&Object.keys(o.O).every((e=>o.O[e](a[i])))?a.splice(i--,1):(c=!1,n<d&&(d=n));if(c){e.splice(l--,1);var s=t();void 0!==s&&(r=s)}}return r}n=n||0;for(var l=e.length;l>0&&e[l-1][2]>n;l--)e[l]=e[l-1];e[l]=[a,t,n]},o.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return o.d(r,{a:r}),r},o.d=(e,r)=>{for(var a in r)o.o(r,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:r[a]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce(((r,a)=>(o.f[a](e,r),r)),[])),o.u=e=>"js/"+{1084:"profile~followers.bundle",1983:"kb.bundle",2470:"home.chunk",2521:"about.bundle",2530:"discover~myhashtags.chunk",2586:"compose.chunk",2732:"dms~message.chunk",3351:"discover~settings.chunk",3365:"dms.chunk",3623:"discover~findfriends.chunk",4028:"error404.bundle",4509:"static~privacy.bundle",4958:"discover.chunk",4965:"discover~memories.chunk",5865:"post.chunk",6053:"notifications.chunk",6869:"profile.chunk",7004:"help.bundle",7019:"discover~hashtag.bundle",8021:"contact.bundle",8250:"i18n.bundle",8517:"daci.chunk",8600:"changelog.bundle",8625:"profile~following.bundle",8900:"discover~serverfeed.chunk",9144:"static~tos.bundle"}[e]+"."+{1084:"fe353e697fb7660b",1983:"f6ebdaac1fd552ca",2470:"25bd77760873ee83",2521:"44a18841089fdde3",2530:"089b7465b2359979",2586:"c413851da244ae3f",2732:"1cfdf19c4525eafa",3351:"5757ad3940569422",3365:"91ab72a8dcd1a8a8",3623:"1aabfedaab1849ba",4028:"5075813f1b00e10d",4509:"24c230550b6938b2",4958:"4f1b3ea93df06670",4965:"70b04c7698c2172b",5865:"881f8b0a9934e053",6053:"1a834e4a7bdbf21a",6869:"0f947cc09af5c8c3",7004:"7c1195b63e04d568",7019:"279c3460159d3af7",8021:"d6c1d467c11796b1",8250:"9b9bf1b64e2aa1c1",8517:"3f13ec9fc49e9d2b",8600:"7f58a5ccc6659eb2",8625:"c406db7b14d07d36",8900:"ff59ca12d08bb810",9144:"65caad6c0546d8c9"}[e]+".js",o.miniCssF=e=>({138:"css/spa",703:"css/admin",1242:"css/appdark",6170:"css/app",8737:"css/portfolio",9994:"css/landing"}[e]+".css"),o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},a="pixelfed:",o.l=(e,t,n,d)=>{if(r[e])r[e].push(t);else{var c,i;if(void 0!==n)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var u=s[l];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==a+n){c=u;break}}c||(i=!0,(c=document.createElement("script")).charset="utf-8",c.timeout=120,o.nc&&c.setAttribute("nonce",o.nc),c.setAttribute("data-webpack",a+n),c.src=e),r[e]=[t];var f=(a,t)=>{c.onerror=c.onload=null,clearTimeout(b);var n=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach((e=>e(t))),a)return a(t)},b=setTimeout(f.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),i&&document.head.appendChild(c)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),o.p="/",(()=>{var e={8929:0,1242:0,6170:0,8737:0,703:0,9994:0,138:0};o.f.j=(r,a)=>{var t=o.o(e,r)?e[r]:void 0;if(0!==t)if(t)a.push(t[2]);else if(/^(1242|138|6170|703|8737|8929|9994)$/.test(r))e[r]=0;else{var n=new Promise(((a,n)=>t=e[r]=[a,n]));a.push(t[2]=n);var d=o.p+o.u(r),c=new Error;o.l(d,(a=>{if(o.o(e,r)&&(0!==(t=e[r])&&(e[r]=void 0),t)){var n=a&&("load"===a.type?"missing":a.type),d=a&&a.target&&a.target.src;c.message="Loading chunk "+r+" failed.\n("+n+": "+d+")",c.name="ChunkLoadError",c.type=n,c.request=d,t[1](c)}}),"chunk-"+r,r)}},o.O.j=r=>0===e[r];var r=(r,a)=>{var t,n,[d,c,i]=a,s=0;if(d.some((r=>0!==e[r]))){for(t in c)o.o(c,t)&&(o.m[t]=c[t]);if(i)var l=i(o)}for(r&&r(a);s<d.length;s++)n=d[s],o.o(e,n)&&e[n]&&e[n][0](),e[n]=0;return o.O(l)},a=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];a.forEach(r.bind(null,0)),a.push=r.bind(null,a.push.bind(a))})(),o.nc=void 0})();
(()=>{"use strict";var e,r,t,n={},a={};function o(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.loaded=!0,t.exports}o.m=n,e=[],o.O=(r,t,n,a)=>{if(!t){var d=1/0;for(l=0;l<e.length;l++){for(var[t,n,a]=e[l],c=!0,i=0;i<t.length;i++)(!1&a||d>=a)&&Object.keys(o.O).every((e=>o.O[e](t[i])))?t.splice(i--,1):(c=!1,a<d&&(d=a));if(c){e.splice(l--,1);var s=n();void 0!==s&&(r=s)}}return r}a=a||0;for(var l=e.length;l>0&&e[l-1][2]>a;l--)e[l]=e[l-1];e[l]=[t,n,a]},o.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return o.d(r,{a:r}),r},o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce(((r,t)=>(o.f[t](e,r),r)),[])),o.u=e=>"js/"+{1084:"profile~followers.bundle",1983:"kb.bundle",2470:"home.chunk",2521:"about.bundle",2530:"discover~myhashtags.chunk",2586:"compose.chunk",2732:"dms~message.chunk",3351:"discover~settings.chunk",3365:"dms.chunk",3623:"discover~findfriends.chunk",4028:"error404.bundle",4509:"static~privacy.bundle",4958:"discover.chunk",4965:"discover~memories.chunk",5865:"post.chunk",6053:"notifications.chunk",6869:"profile.chunk",7004:"help.bundle",7019:"discover~hashtag.bundle",8021:"contact.bundle",8250:"i18n.bundle",8517:"daci.chunk",8600:"changelog.bundle",8625:"profile~following.bundle",8900:"discover~serverfeed.chunk",9144:"static~tos.bundle"}[e]+"."+{1084:"fe353e697fb7660b",1983:"f6ebdaac1fd552ca",2470:"af8ef7b54f61b18d",2521:"44a18841089fdde3",2530:"089b7465b2359979",2586:"c413851da244ae3f",2732:"1cfdf19c4525eafa",3351:"5757ad3940569422",3365:"91ab72a8dcd1a8a8",3623:"1aabfedaab1849ba",4028:"5075813f1b00e10d",4509:"24c230550b6938b2",4958:"5ceb85dcb38dfbef",4965:"70b04c7698c2172b",5865:"62a9d21c9016fd95",6053:"9de71a122956c663",6869:"0f947cc09af5c8c3",7004:"7c1195b63e04d568",7019:"b8319d6999d3e2e3",8021:"d6c1d467c11796b1",8250:"9b9bf1b64e2aa1c1",8517:"3f13ec9fc49e9d2b",8600:"7f58a5ccc6659eb2",8625:"c406db7b14d07d36",8900:"ff59ca12d08bb810",9144:"65caad6c0546d8c9"}[e]+".js",o.miniCssF=e=>({138:"css/spa",703:"css/admin",1242:"css/appdark",6170:"css/app",8737:"css/portfolio",9994:"css/landing"}[e]+".css"),o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="pixelfed:",o.l=(e,n,a,d)=>{if(r[e])r[e].push(n);else{var c,i;if(void 0!==a)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var u=s[l];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+a){c=u;break}}c||(i=!0,(c=document.createElement("script")).charset="utf-8",c.timeout=120,o.nc&&c.setAttribute("nonce",o.nc),c.setAttribute("data-webpack",t+a),c.src=e),r[e]=[n];var f=(t,n)=>{c.onerror=c.onload=null,clearTimeout(b);var a=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),a&&a.forEach((e=>e(n))),t)return t(n)},b=setTimeout(f.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=f.bind(null,c.onerror),c.onload=f.bind(null,c.onload),i&&document.head.appendChild(c)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),o.p="/",(()=>{var e={8929:0,1242:0,6170:0,8737:0,703:0,9994:0,138:0};o.f.j=(r,t)=>{var n=o.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(1242|138|6170|703|8737|8929|9994)$/.test(r))e[r]=0;else{var a=new Promise(((t,a)=>n=e[r]=[t,a]));t.push(n[2]=a);var d=o.p+o.u(r),c=new Error;o.l(d,(t=>{if(o.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var a=t&&("load"===t.type?"missing":t.type),d=t&&t.target&&t.target.src;c.message="Loading chunk "+r+" failed.\n("+a+": "+d+")",c.name="ChunkLoadError",c.type=a,c.request=d,n[1](c)}}),"chunk-"+r,r)}},o.O.j=r=>0===e[r];var r=(r,t)=>{var n,a,[d,c,i]=t,s=0;if(d.some((r=>0!==e[r]))){for(n in c)o.o(c,n)&&(o.m[n]=c[n]);if(i)var l=i(o)}for(r&&r(t);s<d.length;s++)a=d[s],o.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return o.O(l)},t=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),o.nc=void 0})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -24,10 +24,10 @@
"/js/installer.js": "/js/installer.js?id=cd240ae970947b76ac49032ba95e0922",
"/js/admin_invite.js": "/js/admin_invite.js?id=307a53250701e3b12164af9495e88447",
"/js/landing.js": "/js/landing.js?id=7e3ab65813c4bf28182f5bdf0825774c",
"/js/manifest.js": "/js/manifest.js?id=476d005bfafee68d739307577f42f5c2",
"/js/home.chunk.25bd77760873ee83.js": "/js/home.chunk.25bd77760873ee83.js?id=2c68326604072a767c6eb345bbb89bb8",
"/js/manifest.js": "/js/manifest.js?id=6a77f3ddb5e075cdea3d5b4a4ae50adb",
"/js/home.chunk.af8ef7b54f61b18d.js": "/js/home.chunk.af8ef7b54f61b18d.js?id=798319a218acc5ccfcd5088a275a08a3",
"/js/compose.chunk.c413851da244ae3f.js": "/js/compose.chunk.c413851da244ae3f.js?id=6389b021170bc21b58fc5bc28920f9af",
"/js/post.chunk.881f8b0a9934e053.js": "/js/post.chunk.881f8b0a9934e053.js?id=89108b6e10ed316dc2d17ae28ea61262",
"/js/post.chunk.62a9d21c9016fd95.js": "/js/post.chunk.62a9d21c9016fd95.js?id=df8928c784843ce754d2a1d6b7b70459",
"/js/profile.chunk.0f947cc09af5c8c3.js": "/js/profile.chunk.0f947cc09af5c8c3.js?id=fbf3060fadbca5da7c77b174dca58782",
"/js/discover~memories.chunk.70b04c7698c2172b.js": "/js/discover~memories.chunk.70b04c7698c2172b.js?id=437858b8ee0fce1fcaf4625b1ab669ba",
"/js/discover~myhashtags.chunk.089b7465b2359979.js": "/js/discover~myhashtags.chunk.089b7465b2359979.js?id=daac449d68cd3030296937e709d6d5ab",
@ -35,13 +35,13 @@
"/js/discover~findfriends.chunk.1aabfedaab1849ba.js": "/js/discover~findfriends.chunk.1aabfedaab1849ba.js?id=1801453e5066687ec8f7a20832b77982",
"/js/discover~serverfeed.chunk.ff59ca12d08bb810.js": "/js/discover~serverfeed.chunk.ff59ca12d08bb810.js?id=ceb384971dccbc3891fee0a33fdf977b",
"/js/discover~settings.chunk.5757ad3940569422.js": "/js/discover~settings.chunk.5757ad3940569422.js?id=4d083efb280d6934177f7b007feded3a",
"/js/discover.chunk.4f1b3ea93df06670.js": "/js/discover.chunk.4f1b3ea93df06670.js?id=6dcffad6c504442e374de397cc711eb6",
"/js/notifications.chunk.1a834e4a7bdbf21a.js": "/js/notifications.chunk.1a834e4a7bdbf21a.js?id=9137fe578cf3544cd3f4f30d5a8538ee",
"/js/discover.chunk.5ceb85dcb38dfbef.js": "/js/discover.chunk.5ceb85dcb38dfbef.js?id=01d27c04319b2be9e31b3c2ddf8f5d9e",
"/js/notifications.chunk.9de71a122956c663.js": "/js/notifications.chunk.9de71a122956c663.js?id=f72a08113822f6ed5ab218a9ea21457d",
"/js/dms.chunk.91ab72a8dcd1a8a8.js": "/js/dms.chunk.91ab72a8dcd1a8a8.js?id=752e3b061c1e76baa73b5d38657bf93e",
"/js/dms~message.chunk.1cfdf19c4525eafa.js": "/js/dms~message.chunk.1cfdf19c4525eafa.js?id=bd3b4b71f23988bdfaf09ed817219cb9",
"/js/profile~followers.bundle.fe353e697fb7660b.js": "/js/profile~followers.bundle.fe353e697fb7660b.js?id=dc50b57aa36b027c3f5a81efe9525bf2",
"/js/profile~following.bundle.c406db7b14d07d36.js": "/js/profile~following.bundle.c406db7b14d07d36.js?id=b9088a98eeb05f06e241d398ffad5fbd",
"/js/discover~hashtag.bundle.279c3460159d3af7.js": "/js/discover~hashtag.bundle.279c3460159d3af7.js?id=a13b7c78b2de89468a0d0a176919dd3f",
"/js/discover~hashtag.bundle.b8319d6999d3e2e3.js": "/js/discover~hashtag.bundle.b8319d6999d3e2e3.js?id=7371d3f4dbb5b598a300ad15b448c37b",
"/js/error404.bundle.5075813f1b00e10d.js": "/js/error404.bundle.5075813f1b00e10d.js?id=a5c557f4d707537aa3f023a0786dfeba",
"/js/help.bundle.7c1195b63e04d568.js": "/js/help.bundle.7c1195b63e04d568.js?id=5de97a307e5f3c6f1079fe57ff6f8294",
"/js/kb.bundle.f6ebdaac1fd552ca.js": "/js/kb.bundle.f6ebdaac1fd552ca.js?id=d1d8c0f2c80a50471e4df88c0bd4ca0d",

@ -0,0 +1,585 @@
<template>
<div class="web-wrapper notification-metro-component">
<div v-if="isLoaded" class="container-fluid mt-3">
<div class="row">
<div class="col-md-3 d-md-block">
<sidebar :user="profile" />
</div>
<div class="col-md-9 col-lg-9 col-xl-5 offset-xl-1">
<template v-if="tabIndex === 0">
<h1 class="font-weight-bold">
Notifications
</h1>
<p class="small mt-n2">&nbsp;</p>
</template>
<template v-else-if="tabIndex === 10">
<div class="d-flex align-items-center mb-3">
<a class="text-muted" href="#" @click.prevent="tabIndex = 0" style="opacity:0.3">
<i class="far fa-chevron-circle-left fa-2x mr-3" title="Go back to notifications"></i>
</a>
<h1 class="font-weight-bold">
Follow Requests
</h1>
</div>
</template>
<template v-else>
<h1 class="font-weight-bold">
{{ tabs[tabIndex].name }}
</h1>
<p class="small text-lighter mt-n2">{{ tabs[tabIndex].description }}</p>
</template>
<div v-if="!notificationsLoaded">
<placeholder />
</div>
<template v-else>
<ul v-if="tabIndex != 10 && notificationsLoaded && notifications && notifications.length" class="notification-filters nav nav-tabs nav-fill mb-3">
<li v-for="(item, idx) in tabs" class="nav-item">
<a
class="nav-link"
:class="{ active: tabIndex === idx }"
href="#"
@click.prevent="toggleTab(idx)">
<i
class="mr-1 nav-link-icon"
:class="[ item.icon ]"
>
</i>
<span class="d-none d-xl-inline-block">
{{ item.name }}
</span>
</a>
</li>
</ul>
<div v-if="notificationsEmpty && followRequestsChecked && !followRequests.accounts.length && notificationRetries < 2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 text-center">
<img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
<p class="lead text-muted font-weight-bold">{{ $t('notifications.noneFound') }}</p>
</div>
</div>
</div>
<div v-else-if="!notificationsLoaded || tabSwitching || ((notificationsEmpty && notificationRetries < 2 ) || !notifications && !followRequests && !followRequests.accounts && !followRequests.accounts.length)">
<placeholder />
</div>
<div v-else>
<div v-if="tabIndex === 0">
<div
v-if="followRequests && followRequests.hasOwnProperty('accounts') && followRequests.accounts.length"
class="card card-body shadow-none border border-warning rounded-pill mb-3 py-2">
<div class="media align-items-center">
<i class="far fa-exclamation-circle mr-3 text-warning"></i>
<div class="media-body">
<p class="mb-0">
<strong>{{ followRequests.count }} follow {{ followRequests.count > 1 ? 'requests' : 'request' }}</strong>
</p>
</div>
<a
class="ml-2 small d-flex font-weight-bold primary text-uppercase mb-0"
href="#"
@click.prevent="showFollowRequests()">
View<span class="d-none d-md-block">&nbsp;Follow Requests</span>
</a>
</div>
</div>
<div v-if="notificationsLoaded">
<notification
v-for="(n, index) in notifications"
:key="`notification:${index}:${n.id}`"
:n="n" />
<div v-if="notifications && notificationsLoaded && !notifications.length && notificationRetries <= 2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 text-center">
<img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
<p class="lead text-muted font-weight-bold">{{ $t('notifications.noneFound') }}</p>
</div>
</div>
</div>
<div v-if="canLoadMore">
<intersect @enter="enterIntersect">
<placeholder />
</intersect>
</div>
</div>
</div>
<div v-else-if="tabIndex === 10">
<div v-if="followRequests && followRequests.accounts && followRequests.accounts.length" class="list-group">
<div v-for="(acct, index) in followRequests.accounts" class="list-group-item">
<div class="media align-items-center">
<router-link :to="`/i/web/profile/${acct.account.id}`" class="primary">
<img :src="acct.avatar" width="80" height="80" class="rounded-lg shadow mr-3" onerror="this.onerror=null;this.src='/storage/avatars/default.jpg?v=0';">
</router-link>
<div class="media-body mr-3">
<p class="font-weight-bold mb-0 text-break" style="font-size:17px">
<router-link :to="`/i/web/profile/${acct.account.id}`" class="primary">
{{ acct.username }}
</router-link>
</p>
<p class="mb-1 text-muted text-break" style="font-size:11px">{{ truncate(acct.account.note_text, 100) }}</p>
<div class="d-flex text-lighter" style="font-size:11px">
<span class="mr-3">
<span class="font-weight-bold">{{ acct.account.statuses_count }}</span>
<span>Posts</span>
</span>
<span>
<span class="font-weight-bold">{{ acct.account.followers_count }}</span>
<span>Followers</span>
</span>
</div>
</div>
<div class="d-flex flex-column d-md-block">
<button
class="btn btn-outline-success py-1 btn-sm font-weight-bold rounded-pill mr-2 mb-1"
@click.prevent="handleFollowRequest('accept', index)"
>
Accept
</button>
<button class="btn btn-outline-lighter py-1 btn-sm font-weight-bold rounded-pill mb-1"
@click.prevent="handleFollowRequest('reject', index)"
>
Reject
</button>
</div>
</div>
</div>
</div>
</div>
<div v-else>
<div v-if="filteredLoaded">
<div class="card card-body bg-transparent shadow-none border p-2 mb-3 rounded-pill text-lighter">
<div class="media align-items-center small">
<i class="far fa-exclamation-triangle mx-2"></i>
<div class="media-body">
<p class="mb-0 font-weight-bold">Filtering results may not include older notifications</p>
</div>
</div>
</div>
<div v-if="filteredFeed.length">
<notification
v-for="(n, index) in filteredFeed"
:key="`notification:filtered:${index}:${n.id}`"
:n="n" />
</div>
<div v-else>
<div v-if="filteredEmpty && notificationRetries <= 2">
<div class="card card-body shadow-sm border-0 d-flex flex-row align-items-center" style="border-radius: 20px;gap:1rem;">
<i class="far fa-inbox fa-2x text-muted"></i>
<div class="font-weight-bold">No recent {{ tabs[tabIndex].name }}!</div>
</div>
</div>
<placeholder v-else />
</div>
<div v-if="canLoadMoreFiltered">
<intersect @enter="enterFilteredIntersect">
<placeholder />
</intersect>
</div>
</div>
<div v-else>
<placeholder />
</div>
</div>
</div>
</template>
</div>
</div>
<drawer />
</div>
</div>
</template>
<script type="text/javascript">
import Drawer from './partials/drawer.vue';
import Sidebar from './partials/sidebar.vue';
import Notification from './partials/timeline/Notification.vue';
import Placeholder from './partials/placeholders/NotificationPlaceholder.vue';
import Intersect from 'vue-intersect';
export default {
components: {
"drawer": Drawer,
"sidebar": Sidebar,
"intersect": Intersect,
"notification": Notification,
"placeholder": Placeholder,
},
data() {
return {
isLoaded: false,
profile: undefined,
ids: [],
notifications: undefined,
notificationsLoaded: false,
notificationRetries: 0,
notificationsEmpty: true,
notificationRetryTimeout: undefined,
max_id: undefined,
canLoadMore: false,
isIntersecting: false,
tabIndex: 0,
tabs: [
{
id: 'all',
name: 'All',
icon: 'far fa-bell',
types: []
},
{
id: 'mentions',
name: 'Mentions',
description: 'Replies to your posts and posts you were mentioned in',
icon: 'far fa-at',
types: ['comment', 'mention']
},
{
id: 'likes',
name: 'Likes',
description: 'Accounts that liked your posts',
icon: 'far fa-heart',
types: ['favourite']
},
{
id: 'followers',
name: 'Followers',
description: 'Accounts that followed you',
icon: 'far fa-user-plus',
types: ['follow']
},
{
id: 'reblogs',
name: 'Reblogs',
description: 'Accounts that shared or reblogged your posts',
icon: 'far fa-retweet',
types: ['share']
},
{
id: 'direct',
name: 'DMs',
description: 'Direct messages you have with other accounts',
icon: 'far fa-envelope',
types: ['direct']
},
],
tabSwitching: false,
filteredFeed: [],
filteredLoaded: false,
filteredIsIntersecting: false,
filteredMaxId: undefined,
canLoadMoreFiltered: true,
filterPaginationTimeout: undefined,
filteredIterations: 0,
filteredEmpty: false,
followRequests: [],
followRequestsChecked: false,
followRequestsPage: 1
}
},
updated() {
},
mounted() {
this.profile = window._sharedData.user;
this.isLoaded = true;
if(this.profile.locked) {
this.fetchFollowRequests();
}
this.fetchNotifications();
},
beforeDestroy() {
clearTimeout(this.notificationRetryTimeout);
},
methods: {
fetchNotifications() {
this.notificationRetries++;
axios.get('/api/pixelfed/v1/notifications?pg=true')
.then(res => {
if(!res || !res.data || !res.data.length) {
if(this.notificationRetries == 2) {
clearTimeout(this.notificationRetryTimeout);
this.canLoadMore = false;
this.notificationsLoaded = true;
this.notificationsEmpty = true;
return;
}
this.notificationRetryTimeout = setTimeout(() => {
this.fetchNotifications();
}, 1000);
return;
}
let data = res.data.filter(n => {
if(n.type == 'share' && !n.status) {
return false;
}
if(n.type == 'comment' && !n.status) {
return false;
}
if(n.type == 'mention' && !n.status) {
return false;
}
if(n.type == 'favourite' && !n.status) {
return false;
}
if(n.type == 'follow' && !n.account) {
return false;
}
return true;
});
let ids = res.data.map(n => n.id);
this.max_id = Math.min(...ids);
this.ids.push(...ids);
this.notifications = data;
this.notificationsLoaded = true;
this.notificationsEmpty = false;
this.canLoadMore = true;
});
},
enterIntersect() {
if(this.isIntersecting) {
return;
}
if(!isFinite(this.max_id)) {
return;
}
this.isIntersecting = true;
axios.get('/api/pixelfed/v1/notifications', {
params: {
max_id: this.max_id
}
}).then(res => {
if(!res.data.length) {
this.canLoadMore = false;
}
let ids = res.data.map(n => n.id);
this.max_id = Math.min(...ids);
this.notifications.push(...res.data);
this.isIntersecting = false;
})
},
toggleTab(idx) {
this.tabSwitching = true;
this.canLoadMoreFiltered = true;
this.filteredEmpty = false;
this.filteredIterations = 0;
this.filterFeed(this.tabs[idx].id);
},
filterFeed(type) {
switch(type) {
case 'all':
this.tabIndex = 0;
this.filteredFeed = [];
this.filteredLoaded = false;
this.filteredIsIntersecting = false;
this.filteredMaxId = undefined;
this.canLoadMoreFiltered = false;
this.tabSwitching = false;
break;
case 'mentions':
this.tabIndex = 1;
this.filteredMaxId = this.max_id;
this.filteredFeed = this.notifications.filter(n => this.tabs[this.tabIndex].types.includes(n.type));
this.filteredIsIntersecting = false;
this.tabSwitching = false;
this.filteredLoaded = true;
break;
case 'likes':
this.tabIndex = 2;
this.filteredMaxId = this.max_id;
this.filteredFeed = this.notifications.filter(n => n.type === 'favourite');
this.filteredIsIntersecting = false;
this.tabSwitching = false;
this.filteredLoaded = true;
break;
case 'followers':
this.tabIndex = 3;
this.filteredMaxId = this.max_id;
this.filteredFeed = this.notifications.filter(n => n.type === 'follow');
this.filteredIsIntersecting = false;
this.tabSwitching = false;
this.filteredLoaded = true;
break;
case 'reblogs':
this.tabIndex = 4;
this.filteredMaxId = this.max_id;
this.filteredFeed = this.notifications.filter(n => n.type === 'share');
this.filteredIsIntersecting = false;
this.tabSwitching = false;
this.filteredLoaded = true;
break;
case 'direct':
this.tabIndex = 5;
this.filteredMaxId = this.max_id;
this.filteredFeed = this.notifications.filter(n => n.type === 'direct');
this.filteredIsIntersecting = false;
this.tabSwitching = false;
this.filteredLoaded = true;
break;
}
},
enterFilteredIntersect() {
if( !this.canLoadMoreFiltered ||
this.filteredIsIntersecting ||
this.filteredIterations > 10
) {
if(this.filteredFeed.length == 0) {
this.filteredEmpty = true;
this.canLoadMoreFiltered = false;
}
return;
}
if(!isFinite(this.max_id) || !isFinite(this.filteredMaxId)) {
this.canLoadMoreFiltered = false;
return;
}
this.filteredIsIntersecting = true;
axios.get('/api/pixelfed/v1/notifications', {
params: {
max_id: this.filteredMaxId,
limit: 40
}
})
.then(res => {
let mids = res.data.map(n => n.id);
let max_id = Math.min(...mids);
if(max_id < this.max_id) {
this.max_id = max_id;
res.data.forEach(n => {
if(this.ids.indexOf(n.id) == -1) {
this.ids.push(n.id);
this.notifications.push(n);
} else {
}
});
}
this.filteredIterations++;
if(this.filterPaginationTimeout && this.filterPaginationTimeout < 500) {
clearTimeout(this.filterPaginationTimeout);
}
if(!res.data || !res.data.length) {
this.canLoadMoreFiltered = false;
}
if(!res.data.length) {
this.canLoadMoreFiltered = false;
}
let ids = res.data.map(n => n.id);
this.filteredMaxId = Math.min(...ids);
let types = this.tabs[this.tabIndex].types;
let data = res.data.filter(n => types.includes(n.type));
this.filteredFeed.push(...data);
this.filteredIsIntersecting = false;
if(this.filteredFeed.length < 10) {
setTimeout(() => this.enterFilteredIntersect(), 500);
}
this.filterPaginationTimeout = setTimeout(() => {
this.canLoadMoreFiltered = false;
}, 2000);
})
.catch(err => {
this.canLoadMoreFiltered = false;
})
},
fetchFollowRequests() {
axios.get('/account/follow-requests.json')
.then(res => {
if(this.followRequestsPage == 1) {
this.followRequests = res.data;
this.followRequestsChecked = true;
} else {
this.followRequests.accounts.push(...res.data.accounts);
}
this.followRequestsPage++;
});
},
showFollowRequests() {
this.tabSwitching = false;
this.filteredEmpty = false;
this.filteredIterations = 0;
this.tabIndex = 10;
},
handleFollowRequest(action, index) {
if(!window.confirm('Are you sure you want to ' + action + ' this follow request?')) {
return;
}
axios.post('/account/follow-requests', {
action: action,
id: this.followRequests.accounts[index].rid
})
.then(res => {
this.followRequests.count--;
this.followRequests.accounts.splice(index, 1);
this.toggleTab(0);
})
},
truncate(str, len = 40) {
return _.truncate(str, { length: len });
}
}
}
</script>
<style lang="scss" scoped>
.notification-metro-component {
.notification-filters {
.nav-link {
font-size: 12px;
&.active {
font-weight: bold;
}
&-icon:not(.active) {
opacity: 0.5;
}
&:not(.active) {
color: #9ca3af;
}
}
}
}
</style>

@ -37,6 +37,15 @@
<div v-for="(n, index) in feed" class="mb-2">
<div class="media align-items-center">
<img
v-if="n.type === 'autospam.warning'"
class="mr-2 rounded-circle shadow-sm p-1"
style="border: 2px solid var(--danger)"
src="/img/pixelfed-icon-color.svg"
width="32"
height="32"
/>
<img
v-else
class="mr-2 rounded-circle shadow-sm"
:src="n.account.avatar"
width="32"
@ -58,6 +67,14 @@
</span>
</p>
</div>
<div v-else-if="n.type == 'autospam.warning'">
<p class="my-0">
Your recent <a :href="getPostUrl(n.status)" @click.prevent="goToPost(n.status)" class="font-weight-bold">post</a> has been unlisted.
</p>
<p class="mt-n1 mb-0">
<span class="small text-muted"><a href="#" class="font-weight-bold" @click.prevent="showAutospamInfo(n.status)">Click here</a> for more info.</span>
</p>
</div>
<div v-else-if="n.type == 'comment'">
<p class="my-0">
<a :href="getProfileUrl(n.account)" class="font-weight-bold text-dark word-break" :title="n.account.acct">{{n.account.local == false ? '@':''}}{{truncate(n.account.username)}}</a> commented on your <a class="font-weight-bold" :href="getPostUrl(n.status)" @click.prevent="goToPost(n.status)">post</a>.
@ -383,6 +400,20 @@
}
})
},
showAutospamInfo(status) {
let el = document.createElement('p');
el.classList.add('text-left');
el.classList.add('mb-0');
el.innerHTML = '<p class="">We use automated systems to help detect potential abuse and spam. Your recent <a href="/i/web/post/' + status.id + '" class="font-weight-bold">post</a> was flagged for review. <br /> <p class=""><span class="font-weight-bold">Don\'t worry! Your post will be reviewed by a human</span>, and they will restore your post if they determine it appropriate.</p><p style="font-size:12px">Once a human approves your post, any posts you create after will not be marked as unlisted. If you delete this post and share more posts before a human can approve any of them, you will need to wait for at least one unlisted post to be reviewed by a human.';
let wrapper = document.createElement('div');
wrapper.appendChild(el);
swal({
title: 'Why was my post unlisted?',
content: wrapper,
icon: 'warning'
})
}
}
}
</script>

Loading…
Cancel
Save