Merge pull request #4164 from pixelfed/staging

Staging
pull/4171/head
daniel 2023-02-08 04:24:49 -07:00 zatwierdzone przez GitHub
commit 2c5281522a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
50 zmienionych plików z 1067 dodań i 113 usunięć

Wyświetl plik

@ -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)

Wyświetl plik

@ -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);
}
/**

Wyświetl plik

@ -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');
}

11
package-lock.json wygenerowano
Wyświetl plik

@ -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",

Wyświetl plik

@ -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",

2
public/js/admin.js vendored

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -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

Wyświetl plik

@ -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

2
public/js/search.js vendored

File diff suppressed because one or more lines are too long

2
public/js/spa.js vendored

File diff suppressed because one or more lines are too long

2
public/js/vendor.js vendored

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -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"
}

Wyświetl plik

@ -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">&commat;{{ 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>

Wyświetl plik

@ -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">&commat;{{ 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>

Wyświetl plik

@ -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>