mirror of https://github.com/pixelfed/pixelfed
commit
2c5281522a
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
@ -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>
|
Loading…
Reference in New Issue