kopia lustrzana https://github.com/pixelfed/pixelfed
commit
2c5281522a
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -94,6 +94,16 @@
|
|||
- Update reply view, fix visibility filtering ([d419af4b](https://github.com/pixelfed/pixelfed/commit/d419af4b))
|
||||
- Update AP helpers, ingest attachments in replies ([c504e643](https://github.com/pixelfed/pixelfed/commit/c504e643))
|
||||
- Update Media model, use cloud filesystem url if enabled instead of cdn_url to easily update S3 media urls ([e6bc57d7](https://github.com/pixelfed/pixelfed/commit/e6bc57d7))
|
||||
- Update ap helpers, fix unset media name bug ([083f506b](https://github.com/pixelfed/pixelfed/commit/083f506b))
|
||||
- Update MediaStorageService, fix improper path ([964c62da](https://github.com/pixelfed/pixelfed/commit/964c62da))
|
||||
- Update ApiV1Controller, fix account statuses and bookmark pagination ([9f66d6b6](https://github.com/pixelfed/pixelfed/commit/9f66d6b6))
|
||||
- Update SearchApiV2Service, improve account search results ([f6a588f9](https://github.com/pixelfed/pixelfed/commit/f6a588f9))
|
||||
- Update profile model, improve avatarUrl fallback ([620ee826](https://github.com/pixelfed/pixelfed/commit/620ee826))
|
||||
- Update ApiV1Controller, use cursor pagination for favourited_by and reblogged_by endpoints ([e1c7e701](https://github.com/pixelfed/pixelfed/commit/e1c7e701))
|
||||
- Update ApiV1Controller, fix favourited_by and reblogged_by follows attribute ([1a130f3e](https://github.com/pixelfed/pixelfed/commit/1a130f3e))
|
||||
- Update notifications component, improve UX with exponential retry and loading state ([937e6d07](https://github.com/pixelfed/pixelfed/commit/937e6d07))
|
||||
- Update likeModal and shareModal components, use new pagination logic and re-add Follow/Unfollow buttons ([b565ead6](https://github.com/pixelfed/pixelfed/commit/b565ead6))
|
||||
- Update profileFeed component, fix pagination ([7cf41628](https://github.com/pixelfed/pixelfed/commit/7cf41628))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.11.4 (2022-10-04)](https://github.com/pixelfed/pixelfed/compare/v0.11.3...v0.11.4)
|
||||
|
|
|
@ -2471,15 +2471,21 @@ class ApiV1Controller extends Controller
|
|||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'page' => 'nullable|integer|min:1|max:40',
|
||||
'limit' => 'nullable|integer|min:1|max:100'
|
||||
]);
|
||||
|
||||
$limit = $request->input('limit') ?? 40;
|
||||
$limit = $request->input('limit') ?? 10;
|
||||
$user = $request->user();
|
||||
$status = Status::findOrFail($id);
|
||||
$author = intval($status->profile_id) === intval($user->profile_id) || $user->is_admin;
|
||||
|
||||
if(intval($status->profile_id) !== intval($user->profile_id)) {
|
||||
abort_if(
|
||||
!$status->type ||
|
||||
!in_array($status->type, ['photo','photo:album', 'photo:video:album', 'reply', 'text', 'video', 'video:album']),
|
||||
404,
|
||||
);
|
||||
|
||||
if(!$author) {
|
||||
if($status->scope == 'private') {
|
||||
abort_if(!FollowerService::follows($user->profile_id, $status->profile_id), 403);
|
||||
} else {
|
||||
|
@ -2487,35 +2493,46 @@ class ApiV1Controller extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$page = $request->input('page', 1);
|
||||
$start = $page == 1 ? 0 : (($page * $limit) - $limit);
|
||||
$end = $start + $limit - 1;
|
||||
$res = Status::where('reblog_of_id', $status->id)
|
||||
->orderByDesc('id')
|
||||
->cursorPaginate($limit)
|
||||
->withQueryString();
|
||||
|
||||
$ids = ReblogService::getPostReblogs($id, $start, $end);
|
||||
if(empty($ids)) {
|
||||
return [];
|
||||
if(!$res) {
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
$res = collect($ids)
|
||||
->map(function($id) {
|
||||
$status = StatusService::get($id);
|
||||
if($status) {
|
||||
return AccountService::get($status['account']['id']);
|
||||
$headers = [];
|
||||
if($author && $res->hasPages()) {
|
||||
$links = '';
|
||||
if($res->previousPageUrl()) {
|
||||
$links = '<' . $res->previousPageUrl() .'>; rel="prev"';
|
||||
}
|
||||
|
||||
if($res->nextPageUrl()) {
|
||||
if(!empty($links)) {
|
||||
$links .= ', ';
|
||||
}
|
||||
return;
|
||||
})
|
||||
->filter(function($account) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values();
|
||||
$links .= '<' . $res->nextPageUrl() .'>; rel="next"';
|
||||
}
|
||||
|
||||
$url = $request->url();
|
||||
$page = $request->input('page', 1);
|
||||
$next = $page < 40 ? $page + 1 : 40;
|
||||
$prev = $page > 1 ? $page - 1 : 1;
|
||||
$links = '<'.$url.'?page='.$next.'&limit='.$limit.'>; rel="next", <'.$url.'?page='.$prev.'&limit='.$limit.'>; rel="prev"';
|
||||
$headers = ['Link' => $links];
|
||||
}
|
||||
|
||||
return $this->json($res, 200, ['Link' => $links]);
|
||||
$res = $res->map(function($status) use($user) {
|
||||
$account = AccountService::getMastodon($status->profile_id, true);
|
||||
if(!$account) {
|
||||
return false;
|
||||
}
|
||||
$account['follows'] = $status->profile_id == $user->profile_id ? null : FollowerService::follows($user->profile_id, $status->profile_id);
|
||||
return $account;
|
||||
})
|
||||
->filter(function($account) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values();
|
||||
|
||||
return $this->json($res, 200, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2530,58 +2547,72 @@ class ApiV1Controller extends Controller
|
|||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'page' => 'nullable|integer|min:1|max:40',
|
||||
'limit' => 'nullable|integer|min:1|max:100'
|
||||
]);
|
||||
|
||||
$page = $request->input('page', 1);
|
||||
$limit = $request->input('limit') ?? 40;
|
||||
$limit = $request->input('limit') ?? 10;
|
||||
$user = $request->user();
|
||||
$status = Status::findOrFail($id);
|
||||
$offset = $page == 1 ? 0 : ($page * $limit - $limit);
|
||||
if($offset > 100) {
|
||||
if($user->profile_id != $status->profile_id) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
$author = intval($status->profile_id) === intval($user->profile_id) || $user->is_admin;
|
||||
|
||||
if(intval($status->profile_id) !== intval($user->profile_id)) {
|
||||
abort_if(
|
||||
!$status->type ||
|
||||
!in_array($status->type, ['photo','photo:album', 'photo:video:album', 'reply', 'text', 'video', 'video:album']),
|
||||
404,
|
||||
);
|
||||
|
||||
if(!$author) {
|
||||
if($status->scope == 'private') {
|
||||
abort_if(!$status->profile->followedBy($user->profile), 403);
|
||||
abort_if(!FollowerService::follows($user->profile_id, $status->profile_id), 403);
|
||||
} else {
|
||||
abort_if(!in_array($status->scope, ['public','unlisted']), 403);
|
||||
}
|
||||
|
||||
if($request->has('cursor')) {
|
||||
return $this->json([]);
|
||||
}
|
||||
}
|
||||
|
||||
$res = DB::table('likes')
|
||||
->select('likes.id', 'likes.profile_id', 'likes.status_id', 'followers.created_at')
|
||||
->leftJoin('followers', function($join) use($user, $status) {
|
||||
return $join->on('likes.profile_id', '=', 'followers.following_id')
|
||||
->where('followers.profile_id', $user->profile_id)
|
||||
->where('likes.status_id', $status->id);
|
||||
})
|
||||
->whereStatusId($status->id)
|
||||
->orderByDesc('followers.created_at')
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(function($like) {
|
||||
$account = AccountService::getMastodon($like->profile_id, true);
|
||||
$account['follows'] = isset($like->created_at);
|
||||
return $account;
|
||||
})
|
||||
->filter(function($account) use($user) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values();
|
||||
$res = Like::where('status_id', $status->id)
|
||||
->orderByDesc('id')
|
||||
->cursorPaginate($limit)
|
||||
->withQueryString();
|
||||
|
||||
$url = $request->url();
|
||||
$page = $request->input('page', 1);
|
||||
$next = $page < 40 ? $page + 1 : 40;
|
||||
$prev = $page > 1 ? $page - 1 : 1;
|
||||
$links = '<'.$url.'?page='.$next.'&limit='.$limit.'>; rel="next", <'.$url.'?page='.$prev.'&limit='.$limit.'>; rel="prev"';
|
||||
if(!$res) {
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
return $this->json($res, 200, ['Link' => $links]);
|
||||
$headers = [];
|
||||
if($author && $res->hasPages()) {
|
||||
$links = '';
|
||||
if($res->previousPageUrl()) {
|
||||
$links = '<' . $res->previousPageUrl() .'>; rel="prev"';
|
||||
}
|
||||
|
||||
if($res->nextPageUrl()) {
|
||||
if(!empty($links)) {
|
||||
$links .= ', ';
|
||||
}
|
||||
$links .= '<' . $res->nextPageUrl() .'>; rel="next"';
|
||||
}
|
||||
|
||||
$headers = ['Link' => $links];
|
||||
}
|
||||
|
||||
$res = $res->map(function($like) use($user) {
|
||||
$account = AccountService::getMastodon($like->profile_id, true);
|
||||
if(!$account) {
|
||||
return false;
|
||||
}
|
||||
$account['follows'] = $like->profile_id == $user->profile_id ? null : FollowerService::follows($user->profile_id, $like->profile_id);
|
||||
return $account;
|
||||
})
|
||||
->filter(function($account) use($user) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values();
|
||||
|
||||
return $this->json($res, 200, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -160,6 +160,10 @@ class Profile extends Model
|
|||
$url = Cache::remember('avatar:'.$this->id, 1209600, function () {
|
||||
$avatar = $this->avatar;
|
||||
|
||||
if(!$avatar) {
|
||||
return url('/storage/avatars/default.jpg');
|
||||
}
|
||||
|
||||
if($avatar->cdn_url) {
|
||||
if(substr($avatar->cdn_url, 0, 8) === 'https://') {
|
||||
return $avatar->cdn_url;
|
||||
|
@ -170,6 +174,10 @@ class Profile extends Model
|
|||
|
||||
$path = $avatar->media_path;
|
||||
|
||||
if(!$path) {
|
||||
return url('/storage/avatars/default.jpg');
|
||||
}
|
||||
|
||||
if(substr($path, 0, 6) !== 'public') {
|
||||
return url('/storage/avatars/default.jpg');
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"dependencies": {
|
||||
"@fancyapps/fancybox": "^3.5.7",
|
||||
"@trevoreyre/autocomplete-vue": "^2.2.0",
|
||||
"@web3-storage/parse-link-header": "^3.1.0",
|
||||
"animate.css": "^4.1.0",
|
||||
"bigpicture": "^2.6.2",
|
||||
"blurhash": "^1.1.3",
|
||||
|
@ -2171,6 +2172,11 @@
|
|||
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@web3-storage/parse-link-header": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@web3-storage/parse-link-header/-/parse-link-header-3.1.0.tgz",
|
||||
"integrity": "sha512-K1undnK70vLLauqdE8bq/l98isTF2FDhcP0UPpXVSjkSWe3xhAn5eRXk5jfA1E5ycNm84Ws/rQFUD7ue11nciw=="
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
|
@ -10970,6 +10976,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@web3-storage/parse-link-header": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@web3-storage/parse-link-header/-/parse-link-header-3.1.0.tgz",
|
||||
"integrity": "sha512-K1undnK70vLLauqdE8bq/l98isTF2FDhcP0UPpXVSjkSWe3xhAn5eRXk5jfA1E5ycNm84Ws/rQFUD7ue11nciw=="
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"dependencies": {
|
||||
"@fancyapps/fancybox": "^3.5.7",
|
||||
"@trevoreyre/autocomplete-vue": "^2.2.0",
|
||||
"@web3-storage/parse-link-header": "^3.1.0",
|
||||
"animate.css": "^4.1.0",
|
||||
"bigpicture": "^2.6.2",
|
||||
"blurhash": "^1.1.3",
|
||||
|
|
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";(self.webpackChunkpixelfed=self.webpackChunkpixelfed||[]).push([[9008],{23331:(e,t,o)=>{o.r(t);var a=o(70538),l=o(25518),n=o(30306),r=o.n(n),s=o(7398),d=o.n(s),c=o(92987),i=o(37409),u=o.n(i),f=o(74870),h=o.n(f),m=(o(86368),o(46737),o(19755));function p(e){return p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p(e)}window.Vue=a.default,a.default.use(h()),a.default.use(u()),a.default.use(l.default),a.default.use(r()),a.default.use(d()),a.default.use(c.default,{name:"Timeago",locale:"en"}),pixelfed.readmore=function(){m(".read-more").each((function(e,t){var o=m(this),a=o.attr("data-readmore");"undefined"!==p(a)&&!1!==a||o.readmore({collapsedHeight:45,heightMargin:48,moreLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show more</a>',lessLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show less</a>'})}))};try{document.createEvent("TouchEvent"),m("body").addClass("touch")}catch(e){}window.filesize=o(42317),m('[data-toggle="tooltip"]').tooltip();console.log("%cStop!","color:red; font-size:60px; font-weight: bold; -webkit-text-stroke: 1px black;"),console.log('%cThis is a browser feature intended for developers. If someone told you to copy and paste something here to enable a Pixelfed feature or "hack" someone\'s account, it is a scam and will give them access to your Pixelfed account.',"font-size: 18px;")}},e=>{e.O(0,[8898],(()=>{return t=23331,e(e.s=t);var t}));e.O()}]);
|
||||
"use strict";(self.webpackChunkpixelfed=self.webpackChunkpixelfed||[]).push([[9008],{23331:(e,t,o)=>{o.r(t);var a=o(70538),l=o(25518),n=o(30306),r=o.n(n),s=o(16654),d=o.n(s),c=o(92987),i=o(37409),u=o.n(i),f=o(74870),h=o.n(f),m=(o(86368),o(46737),o(19755));function p(e){return p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p(e)}window.Vue=a.default,a.default.use(h()),a.default.use(u()),a.default.use(l.default),a.default.use(r()),a.default.use(d()),a.default.use(c.default,{name:"Timeago",locale:"en"}),pixelfed.readmore=function(){m(".read-more").each((function(e,t){var o=m(this),a=o.attr("data-readmore");"undefined"!==p(a)&&!1!==a||o.readmore({collapsedHeight:45,heightMargin:48,moreLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show more</a>',lessLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show less</a>'})}))};try{document.createEvent("TouchEvent"),m("body").addClass("touch")}catch(e){}window.filesize=o(42317),m('[data-toggle="tooltip"]').tooltip();console.log("%cStop!","color:red; font-size:60px; font-weight: bold; -webkit-text-stroke: 1px black;"),console.log('%cThis is a browser feature intended for developers. If someone told you to copy and paste something here to enable a Pixelfed feature or "hack" someone\'s account, it is a scam and will give them access to your Pixelfed account.',"font-size: 18px;")}},e=>{e.O(0,[8898],(()=>{return t=23331,e(e.s=t);var t}));e.O()}]);
|
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
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,t,n={},o={};function a(e){var r=o[e];if(void 0!==r)return r.exports;var t=o[e]={id:e,loaded:!1,exports:{}};return n[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=n,e=[],a.O=(r,t,n,o)=>{if(!t){var d=1/0;for(l=0;l<e.length;l++){for(var[t,n,o]=e[l],c=!0,i=0;i<t.length;i++)(!1&o||d>=o)&&Object.keys(a.O).every((e=>a.O[e](t[i])))?t.splice(i--,1):(c=!1,o<d&&(d=o));if(c){e.splice(l--,1);var s=n();void 0!==s&&(r=s)}}return r}o=o||0;for(var l=e.length;l>0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[t,n,o]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((r,t)=>(a.f[t](e,r),r)),[])),a.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:"d4dc19a65836f5ba",1983:"ffb0bccb31e767a2",2470:"c5608f771be873ca",2521:"b4e0fef2bfd282de",2530:"8cbed746da3c3307",2586:"88ef87270cef7295",2732:"3396750e3f3e370d",3351:"9e385ba7c7242192",3365:"ffe36114c17441be",3623:"53fdd18a929791f0",4028:"2358054dc4c0a62b",4509:"1e765099c99b7409",4958:"3ef6d6fe45dbe91b",4965:"1cb17840dc8aea5f",5865:"02abe63d47f8d51e",6053:"e36cd010dbca7065",6869:"cdad3298b78ff083",7004:"37981267c6a91fdd",7019:"2f72cbbe9aa1b317",8021:"de2898072b8b7c67",8250:"233c3a2e6c08c397",8517:"fc282ba63a43593c",8600:"1346cd4a2aea418e",8625:"3d192304050edfc2",8900:"a24b7b8c20612b1b",9144:"dcf1fb170b2dae10"}[e]+".js",a.miniCssF=e=>({138:"css/spa",703:"css/admin",1242:"css/appdark",6170:"css/app",8737:"css/portfolio",9994:"css/landing"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="pixelfed:",a.l=(e,n,o,d)=>{if(r[e])r[e].push(n);else{var c,i;if(void 0!==o)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var f=s[l];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==t+o){c=f;break}}c||(i=!0,(c=document.createElement("script")).charset="utf-8",c.timeout=120,a.nc&&c.setAttribute("nonce",a.nc),c.setAttribute("data-webpack",t+o),c.src=e),r[e]=[n];var u=(t,n)=>{c.onerror=c.onload=null,clearTimeout(b);var o=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),o&&o.forEach((e=>e(n))),t)return t(n)},b=setTimeout(u.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=u.bind(null,c.onerror),c.onload=u.bind(null,c.onload),i&&document.head.appendChild(c)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={8929:0,1242:0,6170:0,8737:0,703:0,9994:0,138:0};a.f.j=(r,t)=>{var n=a.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 o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var d=a.p+a.u(r),c=new Error;a.l(d,(t=>{if(a.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),d=t&&t.target&&t.target.src;c.message="Loading chunk "+r+" failed.\n("+o+": "+d+")",c.name="ChunkLoadError",c.type=o,c.request=d,n[1](c)}}),"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,t)=>{var n,o,[d,c,i]=t,s=0;if(d.some((r=>0!==e[r]))){for(n in c)a.o(c,n)&&(a.m[n]=c[n]);if(i)var l=i(a)}for(r&&r(t);s<d.length;s++)o=d[s],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return a.O(l)},t=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),a.nc=void 0})();
|
||||
(()=>{"use strict";var e,r,t,n={},c={};function o(e){var r=c[e];if(void 0!==r)return r.exports;var t=c[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,c)=>{if(!t){var a=1/0;for(f=0;f<e.length;f++){for(var[t,n,c]=e[f],d=!0,i=0;i<t.length;i++)(!1&c||a>=c)&&Object.keys(o.O).every((e=>o.O[e](t[i])))?t.splice(i--,1):(d=!1,c<a&&(a=c));if(d){e.splice(f--,1);var s=n();void 0!==s&&(r=s)}}return r}c=c||0;for(var f=e.length;f>0&&e[f-1][2]>c;f--)e[f]=e[f-1];e[f]=[t,n,c]},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:"f18a24d3924b651a",1983:"e5709245effd8e20",2470:"294faaa69171455b",2521:"a0398e8c630f7036",2530:"075cc9fe49783f65",2586:"f335df0cd85ea00b",2732:"848e25098152c821",3351:"ddc15c2d10514bf9",3365:"37131c41fc288259",3623:"e3a7e0813bc9e3ec",4028:"6f43a867cb75b343",4509:"c647cbc1674cfea8",4958:"b33cd1cc42853828",4965:"487c14a0180fbf85",5865:"dffb139831cf2ae9",6053:"a310984a7cefe091",6869:"99838eb369862e91",7004:"4157e6be875557da",7019:"7c5f7f5c21a1d88c",8021:"3c0833e75a8155f2",8250:"c5c5f4ddf5b18688",8517:"232f6f724c527858",8600:"9ac9432f209bde4e",8625:"8a269b2c4fd0722c",8900:"c37e8a7a49d49297",9144:"fc0a2c6ff6297f24"}[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,c,a)=>{if(r[e])r[e].push(n);else{var d,i;if(void 0!==c)for(var s=document.getElementsByTagName("script"),f=0;f<s.length;f++){var l=s[f];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==t+c){d=l;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,o.nc&&d.setAttribute("nonce",o.nc),d.setAttribute("data-webpack",t+c),d.src=e),r[e]=[n];var u=(t,n)=>{d.onerror=d.onload=null,clearTimeout(b);var c=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),c&&c.forEach((e=>e(n))),t)return t(n)},b=setTimeout(u.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=u.bind(null,d.onerror),d.onload=u.bind(null,d.onload),i&&document.head.appendChild(d)}},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 c=new Promise(((t,c)=>n=e[r]=[t,c]));t.push(n[2]=c);var a=o.p+o.u(r),d=new Error;o.l(a,(t=>{if(o.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var c=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;d.message="Loading chunk "+r+" failed.\n("+c+": "+a+")",d.name="ChunkLoadError",d.type=c,d.request=a,n[1](d)}}),"chunk-"+r,r)}},o.O.j=r=>0===e[r];var r=(r,t)=>{var n,c,[a,d,i]=t,s=0;if(a.some((r=>0!==e[r]))){for(n in d)o.o(d,n)&&(o.m[n]=d[n]);if(i)var f=i(o)}for(r&&r(t);s<a.length;s++)c=a[s],o.o(e,c)&&e[c]&&e[c][0](),e[c]=0;return o.O(f)},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
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,14 +1,14 @@
|
|||
{
|
||||
"/js/app.js": "/js/app.js?id=2c33c87792376ab515157aa91da5a8ff",
|
||||
"/js/app.js": "/js/app.js?id=2215dbd84487dfd70c5aa6f8995d4801",
|
||||
"/js/activity.js": "/js/activity.js?id=672c68b980c08091308e1f0aa0881559",
|
||||
"/js/components.js": "/js/components.js?id=76b9b10bc9428af02b9440b5b943ff27",
|
||||
"/js/components.js": "/js/components.js?id=9f8d82f1340d91a3be0910a1ae0557a1",
|
||||
"/js/discover.js": "/js/discover.js?id=32fa894cb63cfe6051bcb20f330bc662",
|
||||
"/js/profile.js": "/js/profile.js?id=e31b4254f68233ffee9c05a4baaeff50",
|
||||
"/js/status.js": "/js/status.js?id=911159f4750d07cba6794fa180b64431",
|
||||
"/js/timeline.js": "/js/timeline.js?id=02c4e526090057ba0f3f0ce117c8d94d",
|
||||
"/js/compose.js": "/js/compose.js?id=0b4fda60bcc85db7cad45d5c3b95042d",
|
||||
"/js/compose-classic.js": "/js/compose-classic.js?id=359aeeb12406e705f6afd9fb86d09acb",
|
||||
"/js/search.js": "/js/search.js?id=8e7ab35b3e9f4464d4a567c92802cba0",
|
||||
"/js/search.js": "/js/search.js?id=bbcc27b06ce35776f9218570a7c02e0b",
|
||||
"/js/developers.js": "/js/developers.js?id=8a4578e14108dcd931944610c8f5e843",
|
||||
"/js/hashtag.js": "/js/hashtag.js?id=49786c1c8c69711da2f15bcdc7fa26a1",
|
||||
"/js/collectioncompose.js": "/js/collectioncompose.js?id=1e99af77f36f6bd32a086dcb00b051c9",
|
||||
|
@ -16,47 +16,47 @@
|
|||
"/js/profile-directory.js": "/js/profile-directory.js?id=24a2b981bffa2038f41284eed81eec71",
|
||||
"/js/story-compose.js": "/js/story-compose.js?id=cbb169145db25c0e92845710233771ac",
|
||||
"/js/direct.js": "/js/direct.js?id=5816111700ad8f8a89c932485f3a1e47",
|
||||
"/js/admin.js": "/js/admin.js?id=2d057b3321e57d270197e2d591028856",
|
||||
"/js/admin.js": "/js/admin.js?id=d7c4f0d03e057087bb3e49567b65ec74",
|
||||
"/js/rempro.js": "/js/rempro.js?id=e34242bd319cc8650651ad8959ef05ae",
|
||||
"/js/rempos.js": "/js/rempos.js?id=18e7be83c96f9623462945abb0f3596b",
|
||||
"/js/live-player.js": "/js/live-player.js?id=c987b3cd69e432f1b3d52da2901ed305",
|
||||
"/js/spa.js": "/js/spa.js?id=7af63fef1b834003883e3668ea79707d",
|
||||
"/js/spa.js": "/js/spa.js?id=817fca626eed68f4ec754584d7c7c05a",
|
||||
"/js/stories.js": "/js/stories.js?id=4db94699502e85543192865879bece7d",
|
||||
"/js/portfolio.js": "/js/portfolio.js?id=4fb6929e78830b1bb71410180b8b2772",
|
||||
"/js/installer.js": "/js/installer.js?id=1bee003053a612758cf75db8db61a71a",
|
||||
"/js/installer.js": "/js/installer.js?id=cd240ae970947b76ac49032ba95e0922",
|
||||
"/js/admin_invite.js": "/js/admin_invite.js?id=307a53250701e3b12164af9495e88447",
|
||||
"/js/manifest.js": "/js/manifest.js?id=fb65230a6b98288b2503e19960072fbb",
|
||||
"/js/home.chunk.c5608f771be873ca.js": "/js/home.chunk.c5608f771be873ca.js?id=ffa99546f3c102e48c619bc17ba4a2ea",
|
||||
"/js/compose.chunk.88ef87270cef7295.js": "/js/compose.chunk.88ef87270cef7295.js?id=6389b021170bc21b58fc5bc28920f9af",
|
||||
"/js/post.chunk.02abe63d47f8d51e.js": "/js/post.chunk.02abe63d47f8d51e.js?id=18768a4c14e5c5988a7f56bc50e3ad81",
|
||||
"/js/profile.chunk.cdad3298b78ff083.js": "/js/profile.chunk.cdad3298b78ff083.js?id=0f808b5e71cbd1e70c67a12d4d85353c",
|
||||
"/js/discover~memories.chunk.1cb17840dc8aea5f.js": "/js/discover~memories.chunk.1cb17840dc8aea5f.js?id=96bdb1a740731edeb504c5bb06b56f89",
|
||||
"/js/discover~myhashtags.chunk.8cbed746da3c3307.js": "/js/discover~myhashtags.chunk.8cbed746da3c3307.js?id=2603ffd819a746c616d981997b459cab",
|
||||
"/js/daci.chunk.fc282ba63a43593c.js": "/js/daci.chunk.fc282ba63a43593c.js?id=58f6648c4bded7ee0588c712c970e214",
|
||||
"/js/discover~findfriends.chunk.53fdd18a929791f0.js": "/js/discover~findfriends.chunk.53fdd18a929791f0.js?id=e38eee0338a0068ec0e92d3b62feaed1",
|
||||
"/js/discover~serverfeed.chunk.a24b7b8c20612b1b.js": "/js/discover~serverfeed.chunk.a24b7b8c20612b1b.js?id=296835c4551a1acbb8320fd296047751",
|
||||
"/js/discover~settings.chunk.9e385ba7c7242192.js": "/js/discover~settings.chunk.9e385ba7c7242192.js?id=467f69345d83ac1fa6330da9d57b69fa",
|
||||
"/js/discover.chunk.3ef6d6fe45dbe91b.js": "/js/discover.chunk.3ef6d6fe45dbe91b.js?id=2c834cf9e9b22a1183b456330f9d5a69",
|
||||
"/js/notifications.chunk.e36cd010dbca7065.js": "/js/notifications.chunk.e36cd010dbca7065.js?id=4b058bc04867e65daa3ca296a05f9797",
|
||||
"/js/dms.chunk.ffe36114c17441be.js": "/js/dms.chunk.ffe36114c17441be.js?id=f3ece9bef4000affa34229dbb7e85fb3",
|
||||
"/js/dms~message.chunk.3396750e3f3e370d.js": "/js/dms~message.chunk.3396750e3f3e370d.js?id=bd3b4b71f23988bdfaf09ed817219cb9",
|
||||
"/js/profile~followers.bundle.d4dc19a65836f5ba.js": "/js/profile~followers.bundle.d4dc19a65836f5ba.js?id=a62568f338ddace02455f4d9889bf31f",
|
||||
"/js/profile~following.bundle.3d192304050edfc2.js": "/js/profile~following.bundle.3d192304050edfc2.js?id=cf1c9e06af5d4013d75b3577dcd237c4",
|
||||
"/js/discover~hashtag.bundle.2f72cbbe9aa1b317.js": "/js/discover~hashtag.bundle.2f72cbbe9aa1b317.js?id=648a218d1bb502397313985c1ccd1e4f",
|
||||
"/js/error404.bundle.2358054dc4c0a62b.js": "/js/error404.bundle.2358054dc4c0a62b.js?id=a5c557f4d707537aa3f023a0786dfeba",
|
||||
"/js/help.bundle.37981267c6a91fdd.js": "/js/help.bundle.37981267c6a91fdd.js?id=5de97a307e5f3c6f1079fe57ff6f8294",
|
||||
"/js/kb.bundle.ffb0bccb31e767a2.js": "/js/kb.bundle.ffb0bccb31e767a2.js?id=d1d8c0f2c80a50471e4df88c0bd4ca0d",
|
||||
"/js/about.bundle.b4e0fef2bfd282de.js": "/js/about.bundle.b4e0fef2bfd282de.js?id=55b4ddaae96427389b23ab0dc12d44f0",
|
||||
"/js/contact.bundle.de2898072b8b7c67.js": "/js/contact.bundle.de2898072b8b7c67.js?id=453c2addc6c5a26681505de5a97b252d",
|
||||
"/js/i18n.bundle.233c3a2e6c08c397.js": "/js/i18n.bundle.233c3a2e6c08c397.js?id=06abe79741d5b8c93ad8de99edcd928d",
|
||||
"/js/static~privacy.bundle.1e765099c99b7409.js": "/js/static~privacy.bundle.1e765099c99b7409.js?id=3384a4144056cbda76c3d4aaab7d5120",
|
||||
"/js/static~tos.bundle.dcf1fb170b2dae10.js": "/js/static~tos.bundle.dcf1fb170b2dae10.js?id=6244cffbba6358ab51dbb877bda682ab",
|
||||
"/js/changelog.bundle.1346cd4a2aea418e.js": "/js/changelog.bundle.1346cd4a2aea418e.js?id=08c219b93662101da611a8196c6eb763",
|
||||
"/js/manifest.js": "/js/manifest.js?id=0283672b157947b75e090fa2424de5c3",
|
||||
"/js/home.chunk.294faaa69171455b.js": "/js/home.chunk.294faaa69171455b.js?id=7c19952a1cdde06b50141b2f379344d3",
|
||||
"/js/compose.chunk.f335df0cd85ea00b.js": "/js/compose.chunk.f335df0cd85ea00b.js?id=6389b021170bc21b58fc5bc28920f9af",
|
||||
"/js/post.chunk.dffb139831cf2ae9.js": "/js/post.chunk.dffb139831cf2ae9.js?id=ba38a86bfafa7948fe208b72681693b0",
|
||||
"/js/profile.chunk.99838eb369862e91.js": "/js/profile.chunk.99838eb369862e91.js?id=9ea61618bb8aee5eb1c2139a4ee81288",
|
||||
"/js/discover~memories.chunk.487c14a0180fbf85.js": "/js/discover~memories.chunk.487c14a0180fbf85.js?id=96bdb1a740731edeb504c5bb06b56f89",
|
||||
"/js/discover~myhashtags.chunk.075cc9fe49783f65.js": "/js/discover~myhashtags.chunk.075cc9fe49783f65.js?id=2c7a5243ef1dccabe13b4e415934a901",
|
||||
"/js/daci.chunk.232f6f724c527858.js": "/js/daci.chunk.232f6f724c527858.js?id=58f6648c4bded7ee0588c712c970e214",
|
||||
"/js/discover~findfriends.chunk.e3a7e0813bc9e3ec.js": "/js/discover~findfriends.chunk.e3a7e0813bc9e3ec.js?id=e38eee0338a0068ec0e92d3b62feaed1",
|
||||
"/js/discover~serverfeed.chunk.c37e8a7a49d49297.js": "/js/discover~serverfeed.chunk.c37e8a7a49d49297.js?id=296835c4551a1acbb8320fd296047751",
|
||||
"/js/discover~settings.chunk.ddc15c2d10514bf9.js": "/js/discover~settings.chunk.ddc15c2d10514bf9.js?id=467f69345d83ac1fa6330da9d57b69fa",
|
||||
"/js/discover.chunk.b33cd1cc42853828.js": "/js/discover.chunk.b33cd1cc42853828.js?id=6dcffad6c504442e374de397cc711eb6",
|
||||
"/js/notifications.chunk.a310984a7cefe091.js": "/js/notifications.chunk.a310984a7cefe091.js?id=4b058bc04867e65daa3ca296a05f9797",
|
||||
"/js/dms.chunk.37131c41fc288259.js": "/js/dms.chunk.37131c41fc288259.js?id=752e3b061c1e76baa73b5d38657bf93e",
|
||||
"/js/dms~message.chunk.848e25098152c821.js": "/js/dms~message.chunk.848e25098152c821.js?id=bd3b4b71f23988bdfaf09ed817219cb9",
|
||||
"/js/profile~followers.bundle.f18a24d3924b651a.js": "/js/profile~followers.bundle.f18a24d3924b651a.js?id=a62568f338ddace02455f4d9889bf31f",
|
||||
"/js/profile~following.bundle.8a269b2c4fd0722c.js": "/js/profile~following.bundle.8a269b2c4fd0722c.js?id=cf1c9e06af5d4013d75b3577dcd237c4",
|
||||
"/js/discover~hashtag.bundle.7c5f7f5c21a1d88c.js": "/js/discover~hashtag.bundle.7c5f7f5c21a1d88c.js?id=be75e076f14d258c1c581abc07f40af3",
|
||||
"/js/error404.bundle.6f43a867cb75b343.js": "/js/error404.bundle.6f43a867cb75b343.js?id=a5c557f4d707537aa3f023a0786dfeba",
|
||||
"/js/help.bundle.4157e6be875557da.js": "/js/help.bundle.4157e6be875557da.js?id=5de97a307e5f3c6f1079fe57ff6f8294",
|
||||
"/js/kb.bundle.e5709245effd8e20.js": "/js/kb.bundle.e5709245effd8e20.js?id=d1d8c0f2c80a50471e4df88c0bd4ca0d",
|
||||
"/js/about.bundle.a0398e8c630f7036.js": "/js/about.bundle.a0398e8c630f7036.js?id=55b4ddaae96427389b23ab0dc12d44f0",
|
||||
"/js/contact.bundle.3c0833e75a8155f2.js": "/js/contact.bundle.3c0833e75a8155f2.js?id=453c2addc6c5a26681505de5a97b252d",
|
||||
"/js/i18n.bundle.c5c5f4ddf5b18688.js": "/js/i18n.bundle.c5c5f4ddf5b18688.js?id=06abe79741d5b8c93ad8de99edcd928d",
|
||||
"/js/static~privacy.bundle.c647cbc1674cfea8.js": "/js/static~privacy.bundle.c647cbc1674cfea8.js?id=3384a4144056cbda76c3d4aaab7d5120",
|
||||
"/js/static~tos.bundle.fc0a2c6ff6297f24.js": "/js/static~tos.bundle.fc0a2c6ff6297f24.js?id=6244cffbba6358ab51dbb877bda682ab",
|
||||
"/js/changelog.bundle.9ac9432f209bde4e.js": "/js/changelog.bundle.9ac9432f209bde4e.js?id=08c219b93662101da611a8196c6eb763",
|
||||
"/css/appdark.css": "/css/appdark.css?id=de85ecce91d9ed7afa7714547eb1e26c",
|
||||
"/css/app.css": "/css/app.css?id=88a0a931d5b0e24b0d9355f548414768",
|
||||
"/css/portfolio.css": "/css/portfolio.css?id=db2c9929a56d83f9ff2aaf2161d29d36",
|
||||
"/css/admin.css": "/css/admin.css?id=619b6c6613a24e232048856e72110862",
|
||||
"/css/landing.css": "/css/landing.css?id=681e8ecf284b7cbd1f2b585b2761761d",
|
||||
"/css/spa.css": "/css/spa.css?id=602c4f74ce800b7bf45a8d8a4d8cb6e5",
|
||||
"/js/vendor.js": "/js/vendor.js?id=af0983aef26b6d2c1954f8500c9e61a8"
|
||||
"/js/vendor.js": "/js/vendor.js?id=7a1e52ad5031deed20ef6b28241ef679"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
<template>
|
||||
<div>
|
||||
<b-modal
|
||||
ref="likesModal"
|
||||
centered
|
||||
size="md"
|
||||
:scrollable="true"
|
||||
hide-footer
|
||||
header-class="py-2"
|
||||
body-class="p-0"
|
||||
title-class="w-100 text-center pl-4 font-weight-bold"
|
||||
title-tag="p"
|
||||
:title="$t('common.likes')">
|
||||
<div v-if="isLoading" class="likes-loader list-group border-top-0" style="max-height: 500px;">
|
||||
<like-placeholder />
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div v-if="!likes.length" class="d-flex justify-content-center align-items-center" style="height: 140px;">
|
||||
<p class="font-weight-bold mb-0">{{ $t('post.noLikes') }}</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="list-group" style="max-height: 500px;">
|
||||
<div v-for="(account, index) in likes" class="list-group-item border-left-0 border-right-0 px-3" :class="[ index === 0 ? 'border-top-0' : '']">
|
||||
<div class="media align-items-center">
|
||||
<img :src="account.avatar" width="40" height="40" style="border-radius: 8px;" class="mr-3 shadow-sm" onerror="this.src='/storage/avatars/default.jpg?v=0';this.onerror=null;">
|
||||
<div class="media-body">
|
||||
<p class="mb-0 text-truncate"><a :href="account.url" class="text-dark font-weight-bold text-decoration-none" @click.prevent="goToProfile(account)">{{ getUsername(account) }}</a></p>
|
||||
<p class="mb-0 mt-n1 text-dark font-weight-bold small text-break">@{{ account.acct }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
v-if="account.follows == null || account.id == user.id"
|
||||
class="btn btn-outline-muted rounded-pill btn-sm font-weight-bold"
|
||||
@click="goToProfile(profile)"
|
||||
style="width:110px;">
|
||||
View Profile
|
||||
</button>
|
||||
<button
|
||||
v-else-if="account.follows"
|
||||
class="btn btn-outline-muted rounded-pill btn-sm font-weight-bold"
|
||||
:disabled="isUpdatingFollowState"
|
||||
@click="handleUnfollow(index)"
|
||||
style="width:110px;">
|
||||
<span v-if="isUpdatingFollowState && followStateIndex === index">
|
||||
<b-spinner small />
|
||||
</span>
|
||||
<span v-else>Following</span>
|
||||
</button>
|
||||
<button
|
||||
v-else-if="!account.follows"
|
||||
class="btn btn-primary rounded-pill btn-sm font-weight-bold"
|
||||
:disabled="isUpdatingFollowState"
|
||||
@click="handleFollow(index)"
|
||||
style="width:110px;">
|
||||
<span v-if="isUpdatingFollowState && followStateIndex === index">
|
||||
<b-spinner small />
|
||||
</span>
|
||||
<span v-else>Follow</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="canLoadMore">
|
||||
<intersect @enter="enterIntersect">
|
||||
<like-placeholder class="border-top-0" />
|
||||
</intersect>
|
||||
<like-placeholder />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Intersect from 'vue-intersect'
|
||||
import LikePlaceholder from './LikeListPlaceholder.vue';
|
||||
import { parseLinkHeader } from '@web3-storage/parse-link-header';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
status: {
|
||||
type: Object
|
||||
},
|
||||
|
||||
profile: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
"intersect": Intersect,
|
||||
"like-placeholder": LikePlaceholder
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
isLoading: true,
|
||||
canLoadMore: false,
|
||||
isFetchingMore: false,
|
||||
likes: [],
|
||||
ids: [],
|
||||
page: undefined,
|
||||
isUpdatingFollowState: false,
|
||||
followStateIndex: undefined,
|
||||
user: window._sharedData.user
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
clear() {
|
||||
this.isOpen = false;
|
||||
this.isLoading = true;
|
||||
this.canLoadMore = false;
|
||||
this.isFetchingMore = false;
|
||||
this.likes = [];
|
||||
this.ids = [];
|
||||
this.page = undefined;
|
||||
},
|
||||
|
||||
fetchLikes() {
|
||||
axios.get('/api/v1/statuses/'+this.status.id+'/favourited_by', {
|
||||
params: {
|
||||
limit: 40
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
this.ids = res.data.map(a => a.id);
|
||||
this.likes = res.data;
|
||||
if(res.headers && res.headers.link) {
|
||||
const links = parseLinkHeader(res.headers.link);
|
||||
if(links.next) {
|
||||
this.page = links.next.cursor;
|
||||
this.canLoadMore = true;
|
||||
} else {
|
||||
this.canLoadMore = false;
|
||||
}
|
||||
}
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
open() {
|
||||
if(this.page) {
|
||||
this.clear();
|
||||
}
|
||||
this.isOpen = true;
|
||||
this.fetchLikes();
|
||||
this.$refs.likesModal.show();
|
||||
},
|
||||
|
||||
enterIntersect() {
|
||||
if(this.isFetchingMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isFetchingMore = true;
|
||||
|
||||
axios.get('/api/v1/statuses/'+this.status.id+'/favourited_by', {
|
||||
params: {
|
||||
limit: 10,
|
||||
cursor: this.page
|
||||
}
|
||||
}).then(res => {
|
||||
if(!res.data || !res.data.length) {
|
||||
this.canLoadMore = false;
|
||||
this.isFetchingMore = false;
|
||||
return;
|
||||
}
|
||||
res.data.forEach(user => {
|
||||
if(this.ids.indexOf(user.id) == -1) {
|
||||
this.ids.push(user.id);
|
||||
this.likes.push(user);
|
||||
}
|
||||
})
|
||||
if(res.headers && res.headers.link) {
|
||||
const links = parseLinkHeader(res.headers.link);
|
||||
if(links.next) {
|
||||
this.page = links.next.cursor;
|
||||
} else {
|
||||
this.canLoadMore = false;
|
||||
}
|
||||
}
|
||||
this.isFetchingMore = false;
|
||||
})
|
||||
},
|
||||
|
||||
getUsername(account) {
|
||||
return account.display_name ? account.display_name : account.username;
|
||||
},
|
||||
|
||||
goToProfile(account) {
|
||||
this.$router.push({
|
||||
name: 'profile',
|
||||
path: `/i/web/profile/${account.id}`,
|
||||
params: {
|
||||
id: account.id,
|
||||
cachedProfile: account,
|
||||
cachedUser: this.profile
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleFollow(index) {
|
||||
event.currentTarget.blur();
|
||||
|
||||
this.followStateIndex = index;
|
||||
this.isUpdatingFollowState = true;
|
||||
|
||||
let account = this.likes[index];
|
||||
axios.post('/api/v1/accounts/' + account.id + '/follow')
|
||||
.then(res => {
|
||||
this.likes[index].follows = true;
|
||||
this.followStateIndex = undefined;
|
||||
this.isUpdatingFollowState = false;
|
||||
});
|
||||
},
|
||||
|
||||
handleUnfollow(index) {
|
||||
event.currentTarget.blur();
|
||||
|
||||
this.followStateIndex = index;
|
||||
this.isUpdatingFollowState = true;
|
||||
|
||||
let account = this.likes[index];
|
||||
axios.post('/api/v1/accounts/' + account.id + '/unfollow')
|
||||
.then(res => {
|
||||
this.likes[index].follows = false;
|
||||
this.followStateIndex = undefined;
|
||||
this.isUpdatingFollowState = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,239 @@
|
|||
<template>
|
||||
<div>
|
||||
<b-modal
|
||||
ref="sharesModal"
|
||||
centered
|
||||
size="md"
|
||||
:scrollable="true"
|
||||
hide-footer
|
||||
header-class="py-2"
|
||||
body-class="p-0"
|
||||
title-class="w-100 text-center pl-4 font-weight-bold"
|
||||
title-tag="p"
|
||||
title="Shared By">
|
||||
<div v-if="isLoading" class="likes-loader list-group border-top-0" style="max-height: 500px;">
|
||||
<like-placeholder />
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div v-if="!likes.length" class="d-flex justify-content-center align-items-center" style="height: 140px;">
|
||||
<p class="font-weight-bold mb-0">Nobody has shared this yet!</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="list-group" style="max-height: 500px;">
|
||||
<div v-for="(account, index) in likes" class="list-group-item border-left-0 border-right-0 px-3" :class="[ index === 0 ? 'border-top-0' : '']">
|
||||
<div class="media align-items-center">
|
||||
<img :src="account.avatar" width="40" height="40" style="border-radius: 8px;" class="mr-3 shadow-sm" onerror="this.src='/storage/avatars/default.jpg?v=0';this.onerror=null;">
|
||||
<div class="media-body">
|
||||
<p class="mb-0 text-truncate"><a :href="account.url" class="text-dark font-weight-bold text-decoration-none" @click.prevent="goToProfile(account)">{{ getUsername(account) }}</a></p>
|
||||
<p class="mb-0 mt-n1 text-dark font-weight-bold small text-break">@{{ account.acct }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
v-if="account.id == user.id"
|
||||
class="btn btn-outline-muted rounded-pill btn-sm font-weight-bold"
|
||||
@click="goToProfile(profile)"
|
||||
style="width:110px;">
|
||||
View Profile
|
||||
</button>
|
||||
<button
|
||||
v-else-if="account.follows"
|
||||
class="btn btn-outline-muted rounded-pill btn-sm font-weight-bold"
|
||||
:disabled="isUpdatingFollowState"
|
||||
@click="handleUnfollow(index)"
|
||||
style="width:110px;">
|
||||
<span v-if="isUpdatingFollowState && followStateIndex === index">
|
||||
<b-spinner small />
|
||||
</span>
|
||||
<span v-else>Following</span>
|
||||
</button>
|
||||
<button
|
||||
v-else-if="!account.follows"
|
||||
class="btn btn-primary rounded-pill btn-sm font-weight-bold"
|
||||
:disabled="isUpdatingFollowState"
|
||||
@click="handleFollow(index)"
|
||||
style="width:110px;">
|
||||
<span v-if="isUpdatingFollowState && followStateIndex === index">
|
||||
<b-spinner small />
|
||||
</span>
|
||||
<span v-else>Follow</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="canLoadMore">
|
||||
<intersect @enter="enterIntersect">
|
||||
<like-placeholder class="border-top-0" />
|
||||
</intersect>
|
||||
<like-placeholder />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Intersect from 'vue-intersect'
|
||||
import LikePlaceholder from './LikeListPlaceholder.vue';
|
||||
import { parseLinkHeader } from '@web3-storage/parse-link-header';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
status: {
|
||||
type: Object
|
||||
},
|
||||
|
||||
profile: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
"intersect": Intersect,
|
||||
"like-placeholder": LikePlaceholder
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
isLoading: true,
|
||||
canLoadMore: false,
|
||||
isFetchingMore: false,
|
||||
likes: [],
|
||||
ids: [],
|
||||
page: undefined,
|
||||
isUpdatingFollowState: false,
|
||||
followStateIndex: undefined,
|
||||
user: window._sharedData.user
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
clear() {
|
||||
this.isOpen = false;
|
||||
this.isLoading = true;
|
||||
this.canLoadMore = false;
|
||||
this.isFetchingMore = false;
|
||||
this.likes = [];
|
||||
this.ids = [];
|
||||
this.page = undefined;
|
||||
},
|
||||
|
||||
fetchShares() {
|
||||
axios.get('/api/v1/statuses/'+this.status.id+'/reblogged_by', {
|
||||
params: {
|
||||
limit: 40
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
this.ids = res.data.map(a => a.id);
|
||||
this.likes = res.data;
|
||||
if(res.headers && res.headers.link) {
|
||||
const links = parseLinkHeader(res.headers.link);
|
||||
if(links.next) {
|
||||
this.page = links.next.cursor;
|
||||
this.canLoadMore = true;
|
||||
} else {
|
||||
this.canLoadMore = false;
|
||||
}
|
||||
}
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
open() {
|
||||
if(this.page) {
|
||||
this.clear();
|
||||
}
|
||||
this.isOpen = true;
|
||||
this.fetchShares();
|
||||
this.$refs.sharesModal.show();
|
||||
},
|
||||
|
||||
enterIntersect() {
|
||||
if(this.isFetchingMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isFetchingMore = true;
|
||||
|
||||
axios.get('/api/v1/statuses/'+this.status.id+'/reblogged_by', {
|
||||
params: {
|
||||
limit: 10,
|
||||
cursor: this.page
|
||||
}
|
||||
}).then(res => {
|
||||
if(!res.data || !res.data.length) {
|
||||
this.canLoadMore = false;
|
||||
this.isFetchingMore = false;
|
||||
return;
|
||||
}
|
||||
res.data.forEach(user => {
|
||||
if(this.ids.indexOf(user.id) == -1) {
|
||||
this.ids.push(user.id);
|
||||
this.likes.push(user);
|
||||
}
|
||||
})
|
||||
if(res.headers && res.headers.link) {
|
||||
const links = parseLinkHeader(res.headers.link);
|
||||
if(links.next) {
|
||||
this.page = links.next.cursor;
|
||||
} else {
|
||||
this.canLoadMore = false;
|
||||
}
|
||||
}
|
||||
this.isFetchingMore = false;
|
||||
})
|
||||
},
|
||||
|
||||
getUsername(account) {
|
||||
return account.display_name ? account.display_name : account.username;
|
||||
},
|
||||
|
||||
goToProfile(account) {
|
||||
this.$router.push({
|
||||
name: 'profile',
|
||||
path: `/i/web/profile/${account.id}`,
|
||||
params: {
|
||||
id: account.id,
|
||||
cachedProfile: account,
|
||||
cachedUser: this.profile
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleFollow(index) {
|
||||
event.currentTarget.blur();
|
||||
|
||||
this.followStateIndex = index;
|
||||
this.isUpdatingFollowState = true;
|
||||
|
||||
let account = this.likes[index];
|
||||
axios.post('/api/v1/accounts/' + account.id + '/follow')
|
||||
.then(res => {
|
||||
this.likes[index].follows = true;
|
||||
this.followStateIndex = undefined;
|
||||
this.isUpdatingFollowState = false;
|
||||
});
|
||||
},
|
||||
|
||||
handleUnfollow(index) {
|
||||
event.currentTarget.blur();
|
||||
|
||||
this.followStateIndex = index;
|
||||
this.isUpdatingFollowState = true;
|
||||
|
||||
let account = this.likes[index];
|
||||
axios.post('/api/v1/accounts/' + account.id + '/unfollow')
|
||||
.then(res => {
|
||||
this.likes[index].follows = false;
|
||||
this.followStateIndex = undefined;
|
||||
this.isUpdatingFollowState = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,415 @@
|
|||
<template>
|
||||
<div class="notifications-component">
|
||||
<div class="card shadow-sm mb-3" style="overflow: hidden;border-radius: 15px !important;">
|
||||
<div class="card-body pb-0">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="text-muted font-weight-bold">Notifications</span>
|
||||
<div v-if="feed && feed.length">
|
||||
<router-link to="/i/web/notifications" class="btn btn-outline-light btn-sm mr-2" style="color: #B8C2CC !important">
|
||||
<i class="far fa-filter"></i>
|
||||
</router-link>
|
||||
<button
|
||||
v-if="hasLoaded && feed.length"
|
||||
class="btn btn-light btn-sm"
|
||||
:class="{ 'text-lighter': isRefreshing }"
|
||||
:disabled="isRefreshing"
|
||||
@click="refreshNotifications">
|
||||
<i class="fal fa-redo"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!hasLoaded" class="notifications-component-feed">
|
||||
<div class="d-flex align-items-center justify-content-center flex-column bg-light rounded-lg p-3 mb-3" style="min-height: 100px;">
|
||||
<b-spinner variant="grow" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="notifications-component-feed">
|
||||
<template v-if="isEmpty">
|
||||
<div class="d-flex align-items-center justify-content-center flex-column bg-light rounded-lg p-3 mb-3" style="min-height: 100px;">
|
||||
<i class="fal fa-bell fa-2x text-lighter"></i>
|
||||
<p class="mt-2 small font-weight-bold text-center mb-0">{{ $t('notifications.noneFound') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div v-for="(n, index) in feed" class="mb-2">
|
||||
<div class="media align-items-center">
|
||||
<img
|
||||
class="mr-2 rounded-circle shadow-sm"
|
||||
:src="n.account.avatar"
|
||||
width="32"
|
||||
height="32"
|
||||
onerror="this.onerror=null;this.src='/storage/avatars/default.png';">
|
||||
|
||||
<div class="media-body font-weight-light small">
|
||||
<div v-if="n.type == 'favourite'">
|
||||
<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> liked your
|
||||
<span v-if="n.status && n.status.hasOwnProperty('media_attachments')">
|
||||
<a class="font-weight-bold" v-bind:href="getPostUrl(n.status)" :id="'fvn-' + n.id" @click.prevent="goToPost(n.status)">post</a>.
|
||||
<b-popover :target="'fvn-' + n.id" title="" triggers="hover" placement="top" boundary="window">
|
||||
<img :src="notificationPreview(n)" width="100px" height="100px" style="object-fit: cover;">
|
||||
</b-popover>
|
||||
</span>
|
||||
<span v-else>
|
||||
<a class="font-weight-bold" :href="getPostUrl(n.status)" @click.prevent="goToPost(n.status)">post</a>.
|
||||
</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>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'group: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="n.group_post_url">group post</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'story:react'">
|
||||
<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> reacted to your <a class="font-weight-bold" v-bind:href="'/account/direct/t/'+n.account.id">story</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'story: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" v-bind:href="'/account/direct/t/'+n.account.id">story</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'mention'">
|
||||
<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> <a class="font-weight-bold" v-bind:href="mentionUrl(n.status)" @click.prevent="goToPost(n.status)">mentioned</a> you.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'follow'">
|
||||
<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> followed you.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'share'">
|
||||
<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> shared your <a class="font-weight-bold" :href="getPostUrl(n.status)" @click.prevent="goToPost(n.status)">post</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'modlog'">
|
||||
<p class="my-0">
|
||||
<a :href="getProfileUrl(n.account)" class="font-weight-bold text-dark word-break" :title="n.account.acct">{{truncate(n.account.username)}}</a> updated a <a class="font-weight-bold" v-bind:href="n.modlog.url">modlog</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'tagged'">
|
||||
<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> tagged you in a <a class="font-weight-bold" v-bind:href="n.tagged.post_url">post</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="n.type == 'direct'">
|
||||
<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> sent a <router-link class="font-weight-bold" :to="'/i/web/direct/thread/'+n.account.id">dm</router-link>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="n.type == 'group.join.approved'">
|
||||
<p class="my-0">
|
||||
Your application to join the <a :href="n.group.url" class="font-weight-bold text-dark word-break" :title="n.group.name">{{truncate(n.group.name)}}</a> group was approved!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="n.type == 'group.join.rejected'">
|
||||
<p class="my-0">
|
||||
Your application to join <a :href="n.group.url" class="font-weight-bold text-dark word-break" :title="n.group.name">{{truncate(n.group.name)}}</a> was rejected.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="n.type == 'group:invite'">
|
||||
<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> invited you to join <a :href="n.group.url + '/invite/claim'" class="font-weight-bold text-dark word-break" :title="n.group.name">{{n.group.name}}</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<p class="my-0">
|
||||
We cannot display this notification at this time.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small text-muted font-weight-bold" :title="n.created_at">{{timeAgo(n.created_at)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="hasLoaded && feed.length == 0">
|
||||
<p class="small font-weight-bold text-center mb-0">{{ $t('notifications.noneFound') }}</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<intersect v-if="hasLoaded && canLoadMore" @enter="enterIntersect">
|
||||
<placeholder small style="margin-top: -6px" />
|
||||
<placeholder small/>
|
||||
<placeholder small/>
|
||||
<placeholder small/>
|
||||
</intersect>
|
||||
|
||||
<div v-else class="d-block" style="height: 10px;">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Placeholder from './../partials/placeholders/NotificationPlaceholder.vue';
|
||||
import Intersect from 'vue-intersect';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
profile: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
"intersect": Intersect,
|
||||
"placeholder": Placeholder
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
feed: {},
|
||||
maxId: undefined,
|
||||
isIntersecting: false,
|
||||
canLoadMore: false,
|
||||
isRefreshing: false,
|
||||
hasLoaded: false,
|
||||
isEmpty: false,
|
||||
retryTimeout: undefined,
|
||||
retryAttempts: 0
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
clearTimeout(this.retryTimeout);
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
if(this.retryAttempts == 3) {
|
||||
this.hasLoaded = true;
|
||||
this.isEmpty = true;
|
||||
clearTimeout(this.retryTimeout);
|
||||
return;
|
||||
}
|
||||
axios.get('/api/pixelfed/v1/notifications', {
|
||||
params: {
|
||||
limit: 9,
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if(!res || !res.data || !res.data.length) {
|
||||
this.retryAttempts = this.retryAttempts + 1;
|
||||
this.retryTimeout = setTimeout(() => this.init(), this.retryAttempts * 1500);
|
||||
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;
|
||||
}
|
||||
if(n.type == 'modlog' && !n.modlog) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if(!res.data.length) {
|
||||
this.canLoadMore = false;
|
||||
} else {
|
||||
this.canLoadMore = true;
|
||||
}
|
||||
|
||||
if(this.retryTimeout || this.retryAttempts) {
|
||||
this.retryAttempts = 0;
|
||||
clearTimeout(this.retryTimeout);
|
||||
}
|
||||
this.maxId = res.data[res.data.length - 1].id;
|
||||
this.feed = data;
|
||||
|
||||
this.hasLoaded = true;
|
||||
setTimeout(() => {
|
||||
this.isRefreshing = false;
|
||||
}, 15000);
|
||||
});
|
||||
},
|
||||
|
||||
refreshNotifications() {
|
||||
event.currentTarget.blur();
|
||||
this.isRefreshing = true;
|
||||
this.init();
|
||||
},
|
||||
|
||||
enterIntersect() {
|
||||
if(this.isIntersecting || !this.canLoadMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isIntersecting = true;
|
||||
|
||||
axios.get('/api/pixelfed/v1/notifications', {
|
||||
params: {
|
||||
limit: 9,
|
||||
max_id: this.maxId
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if(!res.data || !res.data.length) {
|
||||
this.canLoadMore = false;
|
||||
this.isIntersecting = false;
|
||||
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;
|
||||
}
|
||||
if(n.type == 'modlog' && !n.modlog) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if(!res.data.length) {
|
||||
this.canLoadMore = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.maxId = res.data[res.data.length - 1].id;
|
||||
this.feed.push(...data);
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.isIntersecting = false;
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
truncate(text) {
|
||||
if(text.length <= 15) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.slice(0,15) + '...'
|
||||
},
|
||||
|
||||
timeAgo(ts) {
|
||||
return window.App.util.format.timeAgo(ts);
|
||||
},
|
||||
|
||||
mentionUrl(status) {
|
||||
let username = status.account.username;
|
||||
let id = status.id;
|
||||
return '/p/' + username + '/' + id;
|
||||
},
|
||||
|
||||
redirect(url) {
|
||||
window.location.href = url;
|
||||
},
|
||||
|
||||
notificationPreview(n) {
|
||||
if(!n.status || !n.status.hasOwnProperty('media_attachments') || !n.status.media_attachments.length) {
|
||||
return '/storage/no-preview.png';
|
||||
}
|
||||
return n.status.media_attachments[0].preview_url;
|
||||
},
|
||||
|
||||
getProfileUrl(account) {
|
||||
return '/i/web/profile/' + account.id;
|
||||
},
|
||||
|
||||
getPostUrl(status) {
|
||||
if(!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
return '/i/web/post/' + status.id;
|
||||
},
|
||||
|
||||
goToPost(status) {
|
||||
this.$router.push({
|
||||
name: 'post',
|
||||
path: `/i/web/post/${status.id}`,
|
||||
params: {
|
||||
id: status.id,
|
||||
cachedStatus: status,
|
||||
cachedProfile: this.profile
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
goToProfile(account) {
|
||||
this.$router.push({
|
||||
name: 'profile',
|
||||
path: `/i/web/profile/${account.id}`,
|
||||
params: {
|
||||
id: account.id,
|
||||
cachedProfile: account,
|
||||
cachedUser: this.profile
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.notifications-component {
|
||||
&-feed {
|
||||
min-height: 50px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
overflow-y: scroll;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
.card {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
Ładowanie…
Reference in New Issue