Timeline.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. <template>
  2. <div class="timeline-section-component">
  3. <div v-if="!isLoaded">
  4. <status-placeholder />
  5. <status-placeholder />
  6. <status-placeholder />
  7. <status-placeholder />
  8. </div>
  9. <div v-else>
  10. <transition name="fade">
  11. <div v-if="showReblogBanner && getScope() === 'home'" class="card bg-g-amin card-body shadow-sm mb-3" style="border-radius: 15px;">
  12. <div class="d-flex justify-content-around align-items-center">
  13. <div class="flex-grow-1 ft-std">
  14. <h2 class="font-weight-bold text-white mb-0">Introducing Reblogs in feeds</h2>
  15. <hr />
  16. <p class="lead text-white mb-0">
  17. See reblogs from accounts you follow in your home feed!
  18. </p>
  19. <p class="text-white small mb-1" style="opacity:0.6">
  20. You can disable reblogs in feeds on the Timeline Settings page.
  21. </p>
  22. <hr />
  23. <div class="d-flex">
  24. <button class="btn btn-light rounded-pill font-weight-bold btn-block mr-2" @click.prevent="enableReblogs()">
  25. <template v-if="!enablingReblogs">Show reblogs in home feed</template>
  26. <b-spinner small v-else />
  27. </button>
  28. <button class="btn btn-outline-light rounded-pill font-weight-bold px-5" @click.prevent="hideReblogs()">Hide</button>
  29. </div>
  30. </div>
  31. </div>
  32. </div>
  33. </transition>
  34. <status
  35. v-for="(status, index) in feed"
  36. :key="'pf_feed:' + status.id + ':idx:' + index + ':fui:' + forceUpdateIdx"
  37. :status="status"
  38. :profile="profile"
  39. v-on:like="likeStatus(index)"
  40. v-on:unlike="unlikeStatus(index)"
  41. v-on:share="shareStatus(index)"
  42. v-on:unshare="unshareStatus(index)"
  43. v-on:menu="openContextMenu(index)"
  44. v-on:counter-change="counterChange(index, $event)"
  45. v-on:likes-modal="openLikesModal(index)"
  46. v-on:shares-modal="openSharesModal(index)"
  47. v-on:follow="follow(index)"
  48. v-on:unfollow="unfollow(index)"
  49. v-on:comment-likes-modal="openCommentLikesModal"
  50. v-on:handle-report="handleReport"
  51. v-on:bookmark="handleBookmark(index)"
  52. v-on:mod-tools="handleModTools(index)"
  53. />
  54. <div v-if="showLoadMore" class="text-center">
  55. <button
  56. class="btn btn-primary rounded-pill font-weight-bold"
  57. @click="tryToLoadMore">
  58. Load more
  59. </button>
  60. </div>
  61. <div v-if="canLoadMore">
  62. <intersect @enter="enterIntersect">
  63. <status-placeholder style="margin-bottom: 10rem;"/>
  64. </intersect>
  65. </div>
  66. <div v-if="!isLoaded && feed.length && endFeedReached" style="margin-bottom: 50vh">
  67. <div class="card card-body shadow-sm mb-3" style="border-radius: 15px;">
  68. <p class="display-4 text-center">✨</p>
  69. <p class="lead mb-0 text-center">You have reached the end of this feed</p>
  70. </div>
  71. </div>
  72. <timeline-onboarding
  73. v-if="scope == 'home' && !feed.length"
  74. :profile="profile"
  75. v-on:update-profile="updateProfile" />
  76. <empty-timeline v-if="isLoaded && scope !== 'home' && !feed.length" />
  77. </div>
  78. <context-menu
  79. v-if="showMenu"
  80. ref="contextMenu"
  81. :status="feed[postIndex]"
  82. :profile="profile"
  83. v-on:moderate="commitModeration"
  84. v-on:delete="deletePost"
  85. v-on:report-modal="handleReport"
  86. v-on:edit="handleEdit"
  87. />
  88. <likes-modal
  89. v-if="showLikesModal"
  90. ref="likesModal"
  91. :status="likesModalPost"
  92. :profile="profile"
  93. />
  94. <shares-modal
  95. v-if="showSharesModal"
  96. ref="sharesModal"
  97. :status="sharesModalPost"
  98. :profile="profile"
  99. />
  100. <report-modal
  101. ref="reportModal"
  102. :key="reportedStatusId"
  103. :status="reportedStatus"
  104. />
  105. <post-edit-modal
  106. ref="editModal"
  107. v-on:update="mergeUpdatedPost"
  108. />
  109. </div>
  110. </template>
  111. <script type="text/javascript">
  112. import StatusPlaceholder from './../partials/StatusPlaceholder.vue';
  113. import Status from './../partials/TimelineStatus.vue';
  114. import Intersect from 'vue-intersect';
  115. import ContextMenu from './../partials/post/ContextMenu.vue';
  116. import LikesModal from './../partials/post/LikeModal.vue';
  117. import SharesModal from './../partials/post/ShareModal.vue';
  118. import ReportModal from './../partials/modal/ReportPost.vue';
  119. import EmptyTimeline from './../partials/placeholders/EmptyTimeline.vue'
  120. import TimelineOnboarding from './../partials/placeholders/TimelineOnboarding.vue'
  121. import PostEditModal from './../partials/post/PostEditModal.vue';
  122. export default {
  123. props: {
  124. scope: {
  125. type: String,
  126. default: "home"
  127. },
  128. profile: {
  129. type: Object
  130. },
  131. refresh: {
  132. type: Boolean,
  133. default: false
  134. }
  135. },
  136. components: {
  137. "intersect": Intersect,
  138. "status-placeholder": StatusPlaceholder,
  139. "status": Status,
  140. "context-menu": ContextMenu,
  141. "likes-modal": LikesModal,
  142. "shares-modal": SharesModal,
  143. "report-modal": ReportModal,
  144. "empty-timeline": EmptyTimeline,
  145. "timeline-onboarding": TimelineOnboarding,
  146. "post-edit-modal": PostEditModal
  147. },
  148. data() {
  149. return {
  150. settings: [],
  151. isLoaded: false,
  152. feed: [],
  153. ids: [],
  154. max_id: 0,
  155. canLoadMore: true,
  156. showLoadMore: false,
  157. loadMoreTimeout: undefined,
  158. loadMoreAttempts: 0,
  159. isFetchingMore: false,
  160. endFeedReached: false,
  161. postIndex: 0,
  162. showMenu: false,
  163. showLikesModal: false,
  164. likesModalPost: {},
  165. showReportModal: false,
  166. reportedStatus: {},
  167. reportedStatusId: 0,
  168. showSharesModal: false,
  169. sharesModalPost: {},
  170. forceUpdateIdx: 0,
  171. showReblogBanner: false,
  172. enablingReblogs: false,
  173. baseApi: '/api/v1/pixelfed/timelines/',
  174. }
  175. },
  176. mounted() {
  177. if(window.App.config.features.hasOwnProperty('timelines')) {
  178. if(this.scope == 'local' && !window.App.config.features.timelines.local) {
  179. swal('Error', 'Cannot load this timeline', 'error');
  180. return;
  181. };
  182. if(this.scope == 'network' && !window.App.config.features.timelines.network) {
  183. swal('Error', 'Cannot load this timeline', 'error');
  184. return;
  185. };
  186. }
  187. if(window.App.config.ab.hasOwnProperty('cached_home_timeline')) {
  188. const cht = window.App.config.ab.cached_home_timeline == true;
  189. this.baseApi = cht ? '/api/v1/timelines/' : '/api/pixelfed/v1/timelines/';
  190. }
  191. this.fetchSettings();
  192. },
  193. methods: {
  194. getScope() {
  195. switch(this.scope) {
  196. case 'local':
  197. return 'public'
  198. break;
  199. case 'global':
  200. return 'network'
  201. break;
  202. default:
  203. return 'home';
  204. break;
  205. }
  206. },
  207. fetchSettings() {
  208. axios.get('/api/pixelfed/v1/web/settings')
  209. .then(res => {
  210. this.settings = res.data;
  211. if(!res.data) {
  212. this.showReblogBanner = true;
  213. } else {
  214. if(res.data.hasOwnProperty('hide_reblog_banner')) {
  215. } else if(res.data.hasOwnProperty('enable_reblogs')) {
  216. if(!res.data.enable_reblogs) {
  217. this.showReblogBanner = true;
  218. }
  219. } else {
  220. this.showReblogBanner = true;
  221. }
  222. }
  223. })
  224. .finally(() => {
  225. this.fetchTimeline();
  226. })
  227. },
  228. fetchTimeline(scrollToTop = false) {
  229. let url, params;
  230. if(this.getScope() === 'home' && this.settings && this.settings.hasOwnProperty('enable_reblogs') && this.settings.enable_reblogs) {
  231. url = this.baseApi + `home`;
  232. params = {
  233. '_pe': 1,
  234. max_id: this.max_id,
  235. limit: 6,
  236. include_reblogs: true,
  237. }
  238. } else {
  239. url = this.baseApi + this.getScope();
  240. params = {
  241. max_id: this.max_id,
  242. limit: 6,
  243. '_pe': 1,
  244. }
  245. }
  246. if(this.getScope() === 'network') {
  247. params.remote = true;
  248. url = this.baseApi + `public`;
  249. }
  250. axios.get(url, {
  251. params: params
  252. }).then(res => {
  253. let ids = res.data.map(p => {
  254. if(p && p.hasOwnProperty('relationship')) {
  255. this.$store.commit('updateRelationship', [p.relationship]);
  256. }
  257. return p.id
  258. });
  259. this.isLoaded = true;
  260. if(res.data.length == 0) {
  261. return;
  262. }
  263. this.ids = ids;
  264. this.max_id = Math.min(...ids);
  265. this.feed = res.data;
  266. if(res.data.length < 4) {
  267. this.canLoadMore = false;
  268. this.showLoadMore = true;
  269. }
  270. })
  271. .then(() => {
  272. if(scrollToTop) {
  273. this.$nextTick(() => {
  274. window.scrollTo({
  275. top: 0,
  276. left: 0,
  277. behavior: 'smooth'
  278. });
  279. this.$emit('refreshed');
  280. });
  281. }
  282. })
  283. },
  284. enterIntersect() {
  285. if(this.isFetchingMore) {
  286. return;
  287. }
  288. this.isFetchingMore = true;
  289. let url, params;
  290. if(this.getScope() === 'home' && this.settings && this.settings.hasOwnProperty('enable_reblogs') && this.settings.enable_reblogs) {
  291. url = this.baseApi + `home`;
  292. params = {
  293. '_pe': 1,
  294. max_id: this.max_id,
  295. limit: 6,
  296. include_reblogs: true,
  297. }
  298. } else {
  299. url = this.baseApi + this.getScope();
  300. params = {
  301. max_id: this.max_id,
  302. limit: 6,
  303. '_pe': 1,
  304. }
  305. }
  306. if(this.getScope() === 'network') {
  307. params.remote = true;
  308. url = this.baseApi + `public`;
  309. }
  310. axios.get(url, {
  311. params: params
  312. }).then(res => {
  313. if(!res.data.length) {
  314. this.endFeedReached = true;
  315. this.canLoadMore = false;
  316. this.isFetchingMore = false;
  317. }
  318. setTimeout(() => {
  319. res.data.forEach(p => {
  320. if(this.ids.indexOf(p.id) == -1) {
  321. if(this.max_id > p.id) {
  322. this.max_id = p.id;
  323. }
  324. this.ids.push(p.id);
  325. this.feed.push(p);
  326. if(p && p.hasOwnProperty('relationship')) {
  327. this.$store.commit('updateRelationship', [p.relationship]);
  328. }
  329. }
  330. });
  331. this.isFetchingMore = false;
  332. }, 100);
  333. });
  334. },
  335. tryToLoadMore() {
  336. this.loadMoreAttempts++;
  337. if(this.loadMoreAttempts >= 3) {
  338. this.showLoadMore = false;
  339. }
  340. this.showLoadMore = false;
  341. this.canLoadMore = true;
  342. this.loadMoreTimeout = setTimeout(() => {
  343. this.canLoadMore = false;
  344. this.showLoadMore = true;
  345. }, 5000);
  346. },
  347. likeStatus(index) {
  348. let status = this.feed[index];
  349. if(status.reblog) {
  350. status = status.reblog;
  351. let state = status.favourited;
  352. let count = status.favourites_count;
  353. this.feed[index].reblog.favourites_count = count + 1;
  354. this.feed[index].reblog.favourited = !status.favourited;
  355. } else {
  356. let state = status.favourited;
  357. let count = status.favourites_count;
  358. this.feed[index].favourites_count = count + 1;
  359. this.feed[index].favourited = !status.favourited;
  360. }
  361. axios.post('/api/v1/statuses/' + status.id + '/favourite')
  362. .then(res => {
  363. //
  364. }).catch(err => {
  365. if(status.reblog) {
  366. this.feed[index].reblog.favourites_count = count;
  367. this.feed[index].reblog.favourited = false;
  368. } else {
  369. this.feed[index].favourites_count = count;
  370. this.feed[index].favourited = false;
  371. }
  372. let el = document.createElement('p');
  373. el.classList.add('text-left');
  374. el.classList.add('mb-0');
  375. el.innerHTML = '<span class="lead">We limit certain interactions to keep our community healthy and it appears that you have reached that limit. <span class="font-weight-bold">Please try again later.</span></span>';
  376. let wrapper = document.createElement('div');
  377. wrapper.appendChild(el);
  378. if(err.response.status === 429) {
  379. swal({
  380. title: 'Too many requests',
  381. content: wrapper,
  382. icon: 'warning',
  383. buttons: {
  384. // moreInfo: {
  385. // text: "Contact a human",
  386. // visible: true,
  387. // value: "more",
  388. // className: "text-lighter bg-transparent border"
  389. // },
  390. confirm: {
  391. text: "OK",
  392. value: false,
  393. visible: true,
  394. className: "bg-transparent primary",
  395. closeModal: true
  396. }
  397. }
  398. })
  399. .then((val) => {
  400. if(val == 'more') {
  401. location.href = '/site/contact'
  402. }
  403. return;
  404. });
  405. }
  406. })
  407. },
  408. unlikeStatus(index) {
  409. let status = this.feed[index];
  410. if(status.reblog) {
  411. status = status.reblog;
  412. let state = status.favourited;
  413. let count = status.favourites_count;
  414. this.feed[index].reblog.favourites_count = count - 1;
  415. this.feed[index].reblog.favourited = !status.favourited;
  416. } else {
  417. let state = status.favourited;
  418. let count = status.favourites_count;
  419. this.feed[index].favourites_count = count - 1;
  420. this.feed[index].favourited = !status.favourited;
  421. }
  422. axios.post('/api/v1/statuses/' + status.id + '/unfavourite')
  423. .then(res => {
  424. //
  425. }).catch(err => {
  426. if(status.reblog && status.pf_type == 'share') {
  427. this.feed[index].reblog.favourites_count = count;
  428. this.feed[index].reblog.favourited = false;
  429. } else {
  430. this.feed[index].favourites_count = count;
  431. this.feed[index].favourited = false;
  432. }
  433. })
  434. },
  435. openContextMenu(idx) {
  436. this.postIndex = idx;
  437. this.showMenu = true;
  438. this.$nextTick(() => {
  439. this.$refs.contextMenu.open();
  440. });
  441. },
  442. handleModTools(idx) {
  443. this.postIndex = idx;
  444. this.showMenu = true;
  445. this.$nextTick(() => {
  446. this.$refs.contextMenu.openModMenu();
  447. });
  448. },
  449. openLikesModal(idx) {
  450. this.postIndex = idx;
  451. let post = this.feed[this.postIndex];
  452. this.likesModalPost = post.reblog ? post.reblog : post;
  453. this.showLikesModal = true;
  454. this.$nextTick(() => {
  455. this.$refs.likesModal.open();
  456. });
  457. },
  458. openSharesModal(idx) {
  459. this.postIndex = idx;
  460. let post = this.feed[this.postIndex];
  461. this.sharesModalPost = post.reblog ? post.reblog : post;
  462. this.showSharesModal = true;
  463. this.$nextTick(() => {
  464. this.$refs.sharesModal.open();
  465. });
  466. },
  467. commitModeration(type) {
  468. let idx = this.postIndex;
  469. switch(type) {
  470. case 'addcw':
  471. this.feed[idx].sensitive = true;
  472. break;
  473. case 'remcw':
  474. this.feed[idx].sensitive = false;
  475. break;
  476. case 'unlist':
  477. this.feed.splice(idx, 1);
  478. break;
  479. case 'spammer':
  480. let id = this.feed[idx].account.id;
  481. this.feed = this.feed.filter(post => {
  482. return post.account.id != id;
  483. });
  484. break;
  485. }
  486. },
  487. deletePost() {
  488. this.feed.splice(this.postIndex, 1);
  489. },
  490. counterChange(index, type) {
  491. let post = this.feed[index];
  492. switch(type) {
  493. case 'comment-increment':
  494. if(post.reblog != null) {
  495. this.feed[index].reblog.reply_count = this.feed[index].reblog.reply_count + 1;
  496. } else {
  497. this.feed[index].reply_count = this.feed[index].reply_count + 1;
  498. }
  499. break;
  500. case 'comment-decrement':
  501. if(post.reblog != null) {
  502. this.feed[index].reblog.reply_count = this.feed[index].reblog.reply_count - 1;
  503. } else {
  504. this.feed[index].reply_count = this.feed[index].reply_count - 1;
  505. }
  506. break;
  507. }
  508. },
  509. openCommentLikesModal(post) {
  510. if(post.reblog != null) {
  511. this.likesModalPost = post.reblog;
  512. } else {
  513. this.likesModalPost = post;
  514. }
  515. this.showLikesModal = true;
  516. this.$nextTick(() => {
  517. this.$refs.likesModal.open();
  518. });
  519. },
  520. shareStatus(index) {
  521. let status = this.feed[index];
  522. if(status.reblog) {
  523. status = status.reblog;
  524. let state = status.reblogged;
  525. let count = status.reblogs_count;
  526. this.feed[index].reblog.reblogs_count = count + 1;
  527. this.feed[index].reblog.reblogged = !status.reblogged;
  528. } else {
  529. let state = status.reblogged;
  530. let count = status.reblogs_count;
  531. this.feed[index].reblogs_count = count + 1;
  532. this.feed[index].reblogged = !status.reblogged;
  533. }
  534. axios.post('/api/v1/statuses/' + status.id + '/reblog')
  535. .then(res => {
  536. //
  537. }).catch(err => {
  538. if(status.reblog) {
  539. this.feed[index].reblog.reblogs_count = count;
  540. this.feed[index].reblog.reblogged = false;
  541. } else {
  542. this.feed[index].reblogs_count = count;
  543. this.feed[index].reblogged = false;
  544. }
  545. })
  546. },
  547. unshareStatus(index) {
  548. let status = this.feed[index];
  549. if(status.reblog) {
  550. status = status.reblog;
  551. let state = status.reblogged;
  552. let count = status.reblogs_count;
  553. this.feed[index].reblog.reblogs_count = count - 1;
  554. this.feed[index].reblog.reblogged = !status.reblogged;
  555. } else {
  556. let state = status.reblogged;
  557. let count = status.reblogs_count;
  558. this.feed[index].reblogs_count = count - 1;
  559. this.feed[index].reblogged = !status.reblogged;
  560. }
  561. axios.post('/api/v1/statuses/' + status.id + '/unreblog')
  562. .then(res => {
  563. //
  564. }).catch(err => {
  565. if(status.reblog) {
  566. this.feed[index].reblog.reblogs_count = count;
  567. this.feed[index].reblog.reblogged = false;
  568. } else {
  569. this.feed[index].reblogs_count = count;
  570. this.feed[index].reblogged = false;
  571. }
  572. })
  573. },
  574. handleReport(post) {
  575. this.reportedStatusId = post.id;
  576. this.$nextTick(() => {
  577. this.reportedStatus = post;
  578. this.$refs.reportModal.open();
  579. });
  580. },
  581. handleBookmark(index) {
  582. let p = this.feed[index];
  583. if(p.reblog) {
  584. p = p.reblog;
  585. }
  586. axios.post('/i/bookmark', {
  587. item: p.id
  588. })
  589. .then(res => {
  590. if(this.feed[index].reblog) {
  591. this.feed[index].reblog.bookmarked = !p.bookmarked;
  592. } else {
  593. this.feed[index].bookmarked = !p.bookmarked;
  594. }
  595. })
  596. .catch(err => {
  597. // this.feed[index].bookmarked = false;
  598. this.$bvToast.toast('Cannot bookmark post at this time.', {
  599. title: 'Bookmark Error',
  600. variant: 'danger',
  601. autoHideDelay: 5000
  602. });
  603. });
  604. },
  605. follow(index) {
  606. if(this.feed[index].reblog) {
  607. axios.post('/api/v1/accounts/' + this.feed[index].reblog.account.id + '/follow')
  608. .then(res => {
  609. this.$store.commit('updateRelationship', [res.data]);
  610. this.updateProfile({ following_count: this.profile.following_count + 1 });
  611. this.feed[index].reblog.account.followers_count = this.feed[index].reblog.account.followers_count + 1;
  612. }).catch(err => {
  613. swal('Oops!', 'An error occured when attempting to follow this account.', 'error');
  614. this.feed[index].reblog.relationship.following = false;
  615. });
  616. } else {
  617. axios.post('/api/v1/accounts/' + this.feed[index].account.id + '/follow')
  618. .then(res => {
  619. this.$store.commit('updateRelationship', [res.data]);
  620. this.updateProfile({ following_count: this.profile.following_count + 1 });
  621. this.feed[index].account.followers_count = this.feed[index].account.followers_count + 1;
  622. }).catch(err => {
  623. swal('Oops!', 'An error occured when attempting to follow this account.', 'error');
  624. this.feed[index].relationship.following = false;
  625. });
  626. }
  627. },
  628. unfollow(index) {
  629. if(this.feed[index].reblog) {
  630. axios.post('/api/v1/accounts/' + this.feed[index].reblog.account.id + '/unfollow')
  631. .then(res => {
  632. this.$store.commit('updateRelationship', [res.data]);
  633. this.updateProfile({ following_count: this.profile.following_count - 1 });
  634. this.feed[index].reblog.account.followers_count = this.feed[index].reblog.account.followers_count - 1;
  635. }).catch(err => {
  636. swal('Oops!', 'An error occured when attempting to unfollow this account.', 'error');
  637. this.feed[index].reblog.relationship.following = true;
  638. });
  639. } else {
  640. axios.post('/api/v1/accounts/' + this.feed[index].account.id + '/unfollow')
  641. .then(res => {
  642. this.$store.commit('updateRelationship', [res.data]);
  643. this.updateProfile({ following_count: this.profile.following_count - 1 });
  644. this.feed[index].account.followers_count = this.feed[index].account.followers_count - 1;
  645. }).catch(err => {
  646. swal('Oops!', 'An error occured when attempting to unfollow this account.', 'error');
  647. this.feed[index].relationship.following = true;
  648. });
  649. }
  650. },
  651. updateProfile(delta) {
  652. this.$emit('update-profile', delta);
  653. },
  654. handleRefresh() {
  655. this.isLoaded = false;
  656. this.feed = [];
  657. this.ids = [];
  658. this.max_id = 0;
  659. this.canLoadMore = true;
  660. this.showLoadMore = false;
  661. this.loadMoreTimeout = undefined;
  662. this.loadMoreAttempts = 0;
  663. this.isFetchingMore = false;
  664. this.endFeedReached = false;
  665. this.postIndex = 0;
  666. this.showMenu = false;
  667. this.showLikesModal = false;
  668. this.likesModalPost = {};
  669. this.showReportModal = false;
  670. this.reportedStatus = {};
  671. this.reportedStatusId = 0;
  672. this.showSharesModal = false;
  673. this.sharesModalPost = {};
  674. this.$nextTick(() => {
  675. this.fetchTimeline(true);
  676. });
  677. },
  678. handleEdit(status) {
  679. this.$refs.editModal.show(status);
  680. },
  681. mergeUpdatedPost(post) {
  682. this.feed = this.feed.map(p => {
  683. if(p.id == post.id) {
  684. p = post;
  685. }
  686. return p;
  687. });
  688. this.$nextTick(() => {
  689. this.forceUpdateIdx++;
  690. });
  691. },
  692. enableReblogs() {
  693. this.enablingReblogs = true;
  694. axios.post('/api/pixelfed/v1/web/settings', {
  695. field: 'enable_reblogs',
  696. value: true
  697. })
  698. .then(res => {
  699. setTimeout(() => {
  700. window.location.reload();
  701. }, 1000);
  702. })
  703. },
  704. hideReblogs() {
  705. this.showReblogBanner = false;
  706. axios.post('/api/pixelfed/v1/web/settings', {
  707. field: 'hide_reblog_banner',
  708. value: true
  709. })
  710. .then(res => {
  711. })
  712. },
  713. },
  714. watch: {
  715. 'refresh': 'handleRefresh'
  716. },
  717. beforeDestroy() {
  718. clearTimeout(this.loadMoreTimeout);
  719. }
  720. }
  721. </script>