ContextMenu.vue 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. <template>
  2. <div class="modal-stack">
  3. <b-modal ref="ctxModal"
  4. id="ctx-modal"
  5. hide-header
  6. hide-footer
  7. centered
  8. rounded
  9. size="sm"
  10. body-class="list-group-flush p-0 rounded">
  11. <div class="list-group text-center">
  12. <div v-if="status.visibility !== 'archived'" class="list-group-item d-flex p-0 m-0">
  13. <div class="border-right p-2 w-50">
  14. <a
  15. v-if="status"
  16. class="menu-option"
  17. :href="status.url"
  18. @click.prevent="ctxMenuGoToPost()">
  19. <div class="action-icon-link">
  20. <div class="icon"><i class="fal fa-images fa-lg"></i></div>
  21. <p class="mb-0">{{ $t('menu.viewPost') }}</p>
  22. </div>
  23. </a>
  24. </div>
  25. <div class="p-2 flex-grow-1">
  26. <a
  27. v-if="status"
  28. class="menu-option"
  29. :href="status.account.url"
  30. @click.prevent="ctxMenuGoToProfile()">
  31. <div class="action-icon-link">
  32. <div class="icon"><i class="fal fa-user fa-lg"></i></div>
  33. <p class="mb-0">{{ $t('menu.viewProfile') }}</p>
  34. </div>
  35. </a>
  36. </div>
  37. </div>
  38. <template v-if="ctxMenuRelationship">
  39. <a
  40. v-if="ctxMenuRelationship.following"
  41. class="list-group-item menu-option text-danger"
  42. href="#"
  43. @click.prevent="handleUnfollow">
  44. {{ $t('profile.unfollow') }}
  45. </a>
  46. <div v-else class="d-flex">
  47. <div class="p-3 border-right w-50 text-center">
  48. <a
  49. class="small menu-option text-muted"
  50. href="#"
  51. @click.prevent="handleMute">
  52. <div class="action-icon-link-inline">
  53. <div class="icon"><i class="far" :class="[ ctxMenuRelationship.muting ? 'fa-eye' : 'fa-eye-slash' ]"></i></div>
  54. <p class="text-muted mb-0">{{ ctxMenuRelationship.muting ? 'Unmute' : 'Mute' }}</p>
  55. </div>
  56. </a>
  57. </div>
  58. <div class="p-3 w-50">
  59. <a
  60. class="small menu-option text-danger"
  61. href="#"
  62. @click.prevent="handleBlock">
  63. <div class="action-icon-link-inline">
  64. <div class="icon"><i class="far fa-shield-alt"></i></div>
  65. <p class="text-danger mb-0">Block</p>
  66. </div>
  67. </a>
  68. </div>
  69. </div>
  70. </template>
  71. <a
  72. v-if="status.visibility !== 'archived'"
  73. class="list-group-item menu-option"
  74. href="#"
  75. @click.prevent="ctxMenuShare()">
  76. {{ $t('common.share') }}
  77. </a>
  78. <a
  79. v-if="status && profile && profile.is_admin == true && status.visibility !== 'archived'"
  80. class="list-group-item menu-option"
  81. href="#"
  82. @click.prevent="ctxModMenuShow()">
  83. {{ $t('menu.moderationTools') }}
  84. </a>
  85. <a
  86. v-if="status && status.account.id != profile.id"
  87. class="list-group-item menu-option text-danger"
  88. href="#"
  89. @click.prevent="ctxMenuReportPost()">
  90. {{ $t('menu.report') }}
  91. </a>
  92. <a
  93. v-if="status && profile.id == status.account.id && status.visibility !== 'archived'"
  94. class="list-group-item menu-option text-danger"
  95. href="#"
  96. @click.prevent="archivePost(status)">
  97. {{ $t('menu.archive') }}
  98. </a>
  99. <a
  100. v-if="status && profile.id == status.account.id && status.visibility == 'archived'"
  101. class="list-group-item menu-option text-danger"
  102. href="#"
  103. @click.prevent="unarchivePost(status)">
  104. {{ $t('menu.unarchive') }}
  105. </a>
  106. <a
  107. v-if="config.ab.pue && status && profile.id == status.account.id && status.visibility !== 'archived'"
  108. class="list-group-item menu-option text-danger"
  109. href="#"
  110. @click.prevent="editPost(status)">
  111. Edit
  112. </a>
  113. <a
  114. v-if="status && (profile.is_admin || profile.id == status.account.id) && status.visibility !== 'archived'"
  115. class="list-group-item menu-option text-danger"
  116. href="#"
  117. @click.prevent="deletePost(status)">
  118. <div v-if="isDeleting" class="spinner-border spinner-border-sm" role="status">
  119. <span class="sr-only">Loading...</span>
  120. </div>
  121. <div v-else>
  122. {{ $t('common.delete') }}
  123. </div>
  124. </a>
  125. <a
  126. class="list-group-item menu-option text-lighter"
  127. href="#"
  128. @click.prevent="closeCtxMenu()">
  129. {{ $t('common.cancel') }}
  130. </a>
  131. </div>
  132. </b-modal>
  133. <b-modal ref="ctxModModal"
  134. id="ctx-mod-modal"
  135. hide-header
  136. hide-footer
  137. centered
  138. rounded
  139. size="sm"
  140. body-class="list-group-flush p-0 rounded">
  141. <div class="list-group text-center">
  142. <p class="py-2 px-3 mb-0">
  143. <div
  144. class="text-center menu-option text-danger">
  145. {{ $t('menu.moderationTools') }}
  146. </div>
  147. <div class="small text-center text-muted">
  148. {{ $t('menu.selectOneOption') }}
  149. </div>
  150. </p>
  151. <a
  152. class="list-group-item menu-option"
  153. href="#"
  154. @click.prevent="moderatePost(status, 'unlist')">
  155. {{ $t('menu.unlistFromTimelines') }}
  156. </a>
  157. <a
  158. v-if="status.sensitive"
  159. class="list-group-item menu-option"
  160. href="#"
  161. @click.prevent="moderatePost(status, 'remcw')">
  162. {{ $t('menu.removeCW') }}
  163. </a>
  164. <a
  165. v-else
  166. class="list-group-item menu-option"
  167. href="#"
  168. @click.prevent="moderatePost(status, 'addcw')">
  169. {{ $t('menu.addCW') }}
  170. </a>
  171. <a
  172. class="list-group-item menu-option"
  173. href="#"
  174. @click.prevent="moderatePost(status, 'spammer')">
  175. {{ $t('menu.markAsSpammer') }}<br />
  176. <span class="small">{{ $t('menu.markAsSpammerText') }}</span>
  177. </a>
  178. <a
  179. class="list-group-item menu-option text-lighter"
  180. href="#"
  181. @click.prevent="ctxModMenuClose()">
  182. {{ $t('common.cancel') }}
  183. </a>
  184. </div>
  185. </b-modal>
  186. <b-modal ref="ctxModOtherModal"
  187. id="ctx-mod-other-modal"
  188. hide-header
  189. hide-footer
  190. centered
  191. rounded
  192. size="sm"
  193. body-class="list-group-flush p-0 rounded">
  194. <div class="list-group text-center">
  195. <p class="py-2 px-3 mb-0">
  196. <div class="text-center font-weight-bold text-danger">{{ $t('menu.moderationTools') }}</div>
  197. <div class="small text-center text-muted">{{ $t('menu.selectOneOption') }}</div>
  198. </p>
  199. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="confirmModal()">Unlist Posts</div>
  200. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="confirmModal()">Moderation Log</div>
  201. <div class="list-group-item rounded cursor-pointer text-lighter" @click="ctxModOtherMenuClose()">{{ $t('common.cancel') }}</div>
  202. </div>
  203. </b-modal>
  204. <b-modal ref="ctxShareModal"
  205. id="ctx-share-modal"
  206. title="Share"
  207. hide-footer
  208. hide-header
  209. centered
  210. rounded
  211. size="sm"
  212. body-class="list-group-flush p-0 rounded text-center">
  213. <a
  214. class="list-group-item menu-option"
  215. href="#"
  216. @click.prevent="ctxMenuCopyLink()">
  217. {{ $t('common.copyLink') }}
  218. </a>
  219. <a
  220. v-if="status && status.local == true && !status.in_reply_to_id"
  221. class="list-group-item menu-option"
  222. @click.prevent="ctxMenuEmbed()">
  223. {{ $t('menu.embed') }}
  224. </a>
  225. <a
  226. class="list-group-item menu-option text-lighter"
  227. href="#"
  228. @click.prevent="closeCtxShareMenu()">
  229. {{ $t('common.cancel') }}
  230. </a>
  231. </b-modal>
  232. <b-modal ref="ctxEmbedModal"
  233. id="ctx-embed-modal"
  234. hide-header
  235. hide-footer
  236. centered
  237. rounded
  238. size="md"
  239. body-class="p-2 rounded">
  240. <div>
  241. <div class="form-group">
  242. <textarea class="form-control disabled text-monospace" rows="8" style="overflow-y:hidden;border: 1px solid #efefef; font-size: 12px; line-height: 18px; margin: 0 0 7px;resize:none;" v-model="ctxEmbedPayload" disabled=""></textarea>
  243. </div>
  244. <div class="form-group pl-2 d-flex justify-content-center">
  245. <div class="form-check mr-3">
  246. <input class="form-check-input" type="checkbox" v-model="ctxEmbedShowCaption" :disabled="ctxEmbedCompactMode == true">
  247. <label class="form-check-label font-weight-light">
  248. {{ $t('menu.showCaption') }}
  249. </label>
  250. </div>
  251. <div class="form-check mr-3">
  252. <input class="form-check-input" type="checkbox" v-model="ctxEmbedShowLikes" :disabled="ctxEmbedCompactMode == true">
  253. <label class="form-check-label font-weight-light">
  254. {{ $t('menu.showLikes') }}
  255. </label>
  256. </div>
  257. <div class="form-check">
  258. <input class="form-check-input" type="checkbox" v-model="ctxEmbedCompactMode">
  259. <label class="form-check-label font-weight-light">
  260. {{ $t('menu.compactMode') }}
  261. </label>
  262. </div>
  263. </div>
  264. <hr>
  265. <button :class="copiedEmbed ? 'btn btn-primary btn-block btn-sm py-1 font-weight-bold disabed': 'btn btn-primary btn-block btn-sm py-1 font-weight-bold'" @click="ctxCopyEmbed" :disabled="copiedEmbed">{{copiedEmbed ? 'Embed Code Copied!' : 'Copy Embed Code'}}</button>
  266. <p class="mb-0 px-2 small text-muted">{{ $t('menu.embedConfirmText') }} <a href="/site/terms">{{ $t('site.terms') }}</a></p>
  267. </div>
  268. </b-modal>
  269. <b-modal ref="ctxReport"
  270. id="ctx-report"
  271. hide-header
  272. hide-footer
  273. centered
  274. rounded
  275. size="sm"
  276. body-class="list-group-flush p-0 rounded">
  277. <p class="py-2 px-3 mb-0">
  278. <div class="text-center font-weight-bold text-danger">{{ $t('menu.report') }}</div>
  279. <div class="small text-center text-muted">{{ $t('menu.selectOneOption') }}</div>
  280. </p>
  281. <div class="list-group text-center">
  282. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('spam')">{{ $t('menu.spam') }}</div>
  283. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('sensitive')">{{ $t('menu.sensitive') }}</div>
  284. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('abusive')">{{ $t('menu.abusive') }}</div>
  285. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="openCtxReportOtherMenu()">{{ $t('common.other') }}</div>
  286. <!-- <div class="list-group-item rounded cursor-pointer" @click="ctxReportMenuGoBack()">Go Back</div> -->
  287. <div class="list-group-item rounded cursor-pointer text-lighter" @click="ctxReportMenuGoBack()">{{ $t('common.cancel') }}</div>
  288. </div>
  289. </b-modal>
  290. <b-modal ref="ctxReportOther"
  291. id="ctx-report-other"
  292. hide-header
  293. hide-footer
  294. centered
  295. rounded
  296. size="sm"
  297. body-class="list-group-flush p-0 rounded">
  298. <p class="py-2 px-3 mb-0">
  299. <div class="text-center font-weight-bold text-danger">{{ $t('menu.report') }}</div>
  300. <div class="small text-center text-muted">{{ $t('menu.selectOneOption') }}</div>
  301. </p>
  302. <div class="list-group text-center">
  303. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('underage')">{{ $t('menu.underageAccount') }}</div>
  304. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('copyright')">{{ $t('menu.copyrightInfringement') }}</div>
  305. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('impersonation')">{{ $t('menu.impersonation') }}</div>
  306. <div class="list-group-item rounded cursor-pointer font-weight-bold" @click="sendReport('scam')">{{ $t('menu.scamOrFraud') }}</div>
  307. <div class="list-group-item rounded cursor-pointer text-lighter" @click="ctxReportOtherMenuGoBack()">{{ $t('common.cancel') }}</div>
  308. </div>
  309. </b-modal>
  310. <b-modal ref="ctxConfirm"
  311. id="ctx-confirm"
  312. hide-header
  313. hide-footer
  314. centered
  315. rounded
  316. size="sm"
  317. body-class="list-group-flush p-0 rounded">
  318. <div class="d-flex align-items-center justify-content-center py-3">
  319. <div>{{ this.confirmModalTitle }}</div>
  320. </div>
  321. <div class="d-flex border-top btn-group btn-group-block rounded-0" role="group">
  322. <button type="button" class="btn btn-outline-lighter border-left-0 border-top-0 border-bottom-0 border-right py-2" style="color: rgb(0,122,255) !important;" @click.prevent="confirmModalCancel()">{{ $t('common.cancel') }}</button>
  323. <button type="button" class="btn btn-outline-lighter border-0" style="color: rgb(0,122,255) !important;" @click.prevent="confirmModalConfirm()">Confirm</button>
  324. </div>
  325. </b-modal>
  326. </div>
  327. </template>
  328. <script type="text/javascript">
  329. export default {
  330. props: [
  331. 'status',
  332. 'profile'
  333. ],
  334. data() {
  335. return {
  336. config: window.App.config,
  337. ctxMenuStatus: false,
  338. ctxMenuRelationship: false,
  339. ctxEmbedPayload: false,
  340. copiedEmbed: false,
  341. replySending: false,
  342. ctxEmbedShowCaption: true,
  343. ctxEmbedShowLikes: false,
  344. ctxEmbedCompactMode: false,
  345. confirmModalTitle: 'Are you sure?',
  346. confirmModalIdentifer: null,
  347. confirmModalType: false,
  348. isDeleting: false
  349. }
  350. },
  351. watch: {
  352. ctxEmbedShowCaption: function (n,o) {
  353. if(n == true) {
  354. this.ctxEmbedCompactMode = false;
  355. }
  356. let mode = this.ctxEmbedCompactMode ? 'compact' : 'full';
  357. this.ctxEmbedPayload = window.App.util.embed.post(this.ctxMenuStatus.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
  358. },
  359. ctxEmbedShowLikes: function (n,o) {
  360. if(n == true) {
  361. this.ctxEmbedCompactMode = false;
  362. }
  363. let mode = this.ctxEmbedCompactMode ? 'compact' : 'full';
  364. this.ctxEmbedPayload = window.App.util.embed.post(this.ctxMenuStatus.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
  365. },
  366. ctxEmbedCompactMode: function (n,o) {
  367. if(n == true) {
  368. this.ctxEmbedShowCaption = false;
  369. this.ctxEmbedShowLikes = false;
  370. }
  371. let mode = this.ctxEmbedCompactMode ? 'compact' : 'full';
  372. this.ctxEmbedPayload = window.App.util.embed.post(this.ctxMenuStatus.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
  373. }
  374. },
  375. methods: {
  376. open() {
  377. this.ctxMenu();
  378. },
  379. openModMenu() {
  380. this.$refs.ctxModModal.show();
  381. },
  382. ctxMenu() {
  383. this.ctxMenuStatus = this.status;
  384. this.ctxEmbedPayload = window.App.util.embed.post(this.status.url);
  385. if(this.status.account.id == this.profile.id) {
  386. this.ctxMenuRelationship = false;
  387. this.$refs.ctxModal.show();
  388. } else {
  389. axios.get('/api/v1/accounts/relationships', {
  390. params: {
  391. 'id[]': this.status.account.id
  392. }
  393. }).then(res => {
  394. this.ctxMenuRelationship = res.data[0];
  395. this.$refs.ctxModal.show();
  396. });
  397. }
  398. },
  399. closeCtxMenu() {
  400. this.copiedEmbed = false;
  401. this.ctxMenuStatus = false;
  402. this.ctxMenuRelationship = false;
  403. this.$refs.ctxModal.hide();
  404. this.$refs.ctxReport.hide();
  405. this.$refs.ctxReportOther.hide();
  406. this.closeModals();
  407. },
  408. ctxMenuCopyLink() {
  409. let status = this.ctxMenuStatus;
  410. navigator.clipboard.writeText(status.url);
  411. this.closeModals();
  412. return;
  413. },
  414. ctxMenuGoToPost() {
  415. let status = this.ctxMenuStatus;
  416. this.statusUrl(status);
  417. this.closeCtxMenu();
  418. return;
  419. },
  420. ctxMenuGoToProfile() {
  421. let status = this.ctxMenuStatus;
  422. this.profileUrl(status);
  423. this.closeCtxMenu();
  424. return;
  425. },
  426. ctxMenuReportPost() {
  427. this.$refs.ctxModal.hide();
  428. // this.$refs.ctxReport.show();
  429. this.$emit('report-modal', this.ctxMenuStatus);
  430. return;
  431. },
  432. ctxMenuEmbed() {
  433. this.closeModals();
  434. this.$refs.ctxEmbedModal.show();
  435. },
  436. ctxMenuShare() {
  437. this.$refs.ctxModal.hide();
  438. this.$refs.ctxShareModal.show();
  439. },
  440. closeCtxShareMenu() {
  441. this.$refs.ctxShareModal.hide();
  442. this.$refs.ctxModal.show();
  443. },
  444. ctxCopyEmbed() {
  445. navigator.clipboard.writeText(this.ctxEmbedPayload);
  446. this.ctxEmbedShowCaption = true;
  447. this.ctxEmbedShowLikes = false;
  448. this.ctxEmbedCompactMode = false;
  449. this.$refs.ctxEmbedModal.hide();
  450. },
  451. ctxModMenuShow() {
  452. this.$refs.ctxModal.hide();
  453. this.$refs.ctxModModal.show();
  454. },
  455. ctxModOtherMenuShow() {
  456. this.$refs.ctxModal.hide();
  457. this.$refs.ctxModModal.hide();
  458. this.$refs.ctxModOtherModal.show();
  459. },
  460. ctxModMenu() {
  461. this.$refs.ctxModal.hide();
  462. },
  463. ctxModMenuClose() {
  464. this.closeModals();
  465. },
  466. ctxModOtherMenuClose() {
  467. this.closeModals();
  468. this.$refs.ctxModModal.show();
  469. },
  470. formatCount(count) {
  471. return App.util.format.count(count);
  472. },
  473. openCtxReportOtherMenu() {
  474. let s = this.ctxMenuStatus;
  475. this.closeCtxMenu();
  476. this.ctxMenuStatus = s;
  477. this.$refs.ctxReportOther.show();
  478. },
  479. ctxReportMenuGoBack() {
  480. this.$refs.ctxReportOther.hide();
  481. this.$refs.ctxReport.hide();
  482. this.$refs.ctxModal.show();
  483. },
  484. ctxReportOtherMenuGoBack() {
  485. this.$refs.ctxReportOther.hide();
  486. this.$refs.ctxModal.hide();
  487. this.$refs.ctxReport.show();
  488. },
  489. sendReport(type) {
  490. let id = this.ctxMenuStatus.id;
  491. swal({
  492. 'title': this.$t('menu.confirmReport'),
  493. 'text': this.$t('menu.confirmReportText'),
  494. 'icon': 'warning',
  495. 'buttons': true,
  496. 'dangerMode': true
  497. }).then((res) => {
  498. if(res) {
  499. axios.post('/i/report/', {
  500. 'report': type,
  501. 'type': 'post',
  502. 'id': id,
  503. }).then(res => {
  504. this.closeCtxMenu();
  505. swal(this.$t('menu.reportSent'), this.$t('menu.reportSentText'), 'success');
  506. }).catch(err => {
  507. swal(this.$t('common.oops'), this.$t('menu.reportSentError'), 'error');
  508. })
  509. } else {
  510. this.closeCtxMenu();
  511. }
  512. });
  513. },
  514. closeModals() {
  515. this.$refs.ctxModal.hide();
  516. this.$refs.ctxModModal.hide();
  517. this.$refs.ctxModOtherModal.hide();
  518. this.$refs.ctxShareModal.hide();
  519. this.$refs.ctxEmbedModal.hide();
  520. this.$refs.ctxReport.hide();
  521. this.$refs.ctxReportOther.hide();
  522. this.$refs.ctxConfirm.hide();
  523. },
  524. openCtxStatusModal() {
  525. this.closeModals();
  526. this.$refs.ctxStatusModal.show();
  527. },
  528. openConfirmModal() {
  529. this.closeModals();
  530. this.$refs.ctxConfirm.show();
  531. },
  532. closeConfirmModal() {
  533. this.closeModals();
  534. this.confirmModalTitle = 'Are you sure?';
  535. this.confirmModalType = false;
  536. this.confirmModalIdentifer = null;
  537. },
  538. confirmModalConfirm() {
  539. switch(this.confirmModalType) {
  540. case 'post.delete':
  541. axios.post('/i/delete', {
  542. type: 'status',
  543. item: this.confirmModalIdentifer
  544. }).then(res => {
  545. this.feed = this.feed.filter(s => {
  546. return s.id != this.confirmModalIdentifer;
  547. });
  548. this.closeConfirmModal();
  549. }).catch(err => {
  550. this.closeConfirmModal();
  551. swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
  552. });
  553. break;
  554. }
  555. this.closeConfirmModal();
  556. },
  557. confirmModalCancel() {
  558. this.closeConfirmModal();
  559. },
  560. moderatePost(status, action, $event) {
  561. let username = status.account.username;
  562. let pid = status.id;
  563. let msg = '';
  564. let self = this;
  565. switch(action) {
  566. case 'addcw':
  567. msg = this.$t('menu.modAddCWConfirm');
  568. swal({
  569. title: 'Confirm',
  570. text: msg,
  571. icon: 'warning',
  572. buttons: true,
  573. dangerMode: true
  574. }).then(res => {
  575. if(res) {
  576. axios.post('/api/v2/moderator/action', {
  577. action: action,
  578. item_id: status.id,
  579. item_type: 'status'
  580. }).then(res => {
  581. swal(this.$t('common.success'), this.$t('menu.modCWSuccess'), 'success');
  582. // status.sensitive = true;
  583. this.$emit('moderate', 'addcw');
  584. self.closeModals();
  585. self.ctxModMenuClose();
  586. }).catch(err => {
  587. self.closeModals();
  588. self.ctxModMenuClose();
  589. swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
  590. });
  591. }
  592. });
  593. break;
  594. case 'remcw':
  595. msg = this.$t('menu.modRemoveCWConfirm');
  596. swal({
  597. title: 'Confirm',
  598. text: msg,
  599. icon: 'warning',
  600. buttons: true,
  601. dangerMode: true
  602. }).then(res => {
  603. if(res) {
  604. axios.post('/api/v2/moderator/action', {
  605. action: action,
  606. item_id: status.id,
  607. item_type: 'status'
  608. }).then(res => {
  609. swal(this.$t('common.success'), this.$t('menu.modRemoveCWSuccess'), 'success');
  610. // status.sensitive = false;
  611. this.$emit('moderate', 'remcw');
  612. self.closeModals();
  613. self.ctxModMenuClose();
  614. }).catch(err => {
  615. self.closeModals();
  616. self.ctxModMenuClose();
  617. swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
  618. });
  619. }
  620. });
  621. break;
  622. case 'unlist':
  623. msg = this.$t('menu.modUnlistConfirm');
  624. swal({
  625. title: 'Confirm',
  626. text: msg,
  627. icon: 'warning',
  628. buttons: true,
  629. dangerMode: true
  630. }).then(res => {
  631. if(res) {
  632. axios.post('/api/v2/moderator/action', {
  633. action: action,
  634. item_id: status.id,
  635. item_type: 'status'
  636. }).then(res => {
  637. // this.feed = this.feed.filter(f => {
  638. // return f.id != status.id;
  639. // });
  640. this.$emit('moderate', 'unlist');
  641. swal(this.$t('common.success'), this.$t('menu.modUnlistSuccess'), 'success');
  642. self.closeModals();
  643. self.ctxModMenuClose();
  644. }).catch(err => {
  645. self.closeModals();
  646. self.ctxModMenuClose();
  647. swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
  648. });
  649. }
  650. });
  651. break;
  652. case 'spammer':
  653. msg = this.$t('menu.modMarkAsSpammerConfirm');
  654. swal({
  655. title: 'Confirm',
  656. text: msg,
  657. icon: 'warning',
  658. buttons: true,
  659. dangerMode: true
  660. }).then(res => {
  661. if(res) {
  662. axios.post('/api/v2/moderator/action', {
  663. action: action,
  664. item_id: status.id,
  665. item_type: 'status'
  666. }).then(res => {
  667. this.$emit('moderate', 'spammer');
  668. swal(this.$t('common.success'), this.$t('menu.modMarkAsSpammerSuccess'), 'success');
  669. self.closeModals();
  670. self.ctxModMenuClose();
  671. }).catch(err => {
  672. self.closeModals();
  673. self.ctxModMenuClose();
  674. swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
  675. });
  676. }
  677. });
  678. break;
  679. }
  680. },
  681. statusUrl(status) {
  682. if(status.account.local == true) {
  683. this.$router.push({
  684. name: 'post',
  685. path: `/i/web/post/${status.id}`,
  686. params: {
  687. id: status.id,
  688. cachedStatus: status,
  689. cachedProfile: this.profile
  690. }
  691. });
  692. return;
  693. }
  694. let permalink = this.$route.params.hasOwnProperty('id');
  695. if(permalink) {
  696. location.href = status.url;
  697. return;
  698. } else {
  699. this.$router.push({
  700. name: 'post',
  701. path: `/i/web/post/${status.id}`,
  702. params: {
  703. id: status.id,
  704. cachedStatus: status,
  705. cachedProfile: this.profile
  706. }
  707. });
  708. return;
  709. }
  710. },
  711. profileUrl(status) {
  712. this.$router.push({
  713. name: 'profile',
  714. path: `/i/web/profile/${status.account.id}`,
  715. params: {
  716. id: status.account.id,
  717. cachedProfile: status.account,
  718. cachedUser: this.profile
  719. }
  720. });
  721. return;
  722. },
  723. deletePost(status) {
  724. this.isDeleting = true;
  725. if(this.ownerOrAdmin(status) == false) {
  726. return;
  727. }
  728. swal({
  729. title: 'Confirm Delete',
  730. text: 'Are you sure you want to delete this post?',
  731. icon: "warning",
  732. buttons: true,
  733. dangerMode: true,
  734. })
  735. .then(res => {
  736. if(!res) {
  737. this.closeModals();
  738. this.isDeleting = false;
  739. } else {
  740. axios.post('/i/delete', {
  741. type: 'status',
  742. item: status.id
  743. }).then(res => {
  744. this.$emit('delete');
  745. this.closeModals();
  746. this.isDeleting = false;
  747. }).catch(err => {
  748. this.closeModals();
  749. this.isDeleting = false;
  750. swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
  751. });
  752. }
  753. })
  754. },
  755. owner(status) {
  756. return this.profile.id === status.account.id;
  757. },
  758. admin() {
  759. return this.profile.is_admin == true;
  760. },
  761. ownerOrAdmin(status) {
  762. return this.owner(status) || this.admin();
  763. },
  764. archivePost(status) {
  765. if(window.confirm(this.$t('menu.archivePostConfirm')) == false) {
  766. return;
  767. }
  768. axios.post('/api/pixelfed/v2/status/' + status.id + '/archive')
  769. .then(res => {
  770. this.$emit('delete', status.id);
  771. this.$emit('archived', status.id);
  772. this.closeModals();
  773. });
  774. },
  775. unarchivePost(status) {
  776. if(window.confirm(this.$t('menu.unarchivePostConfirm')) == false) {
  777. return;
  778. }
  779. axios.post('/api/pixelfed/v2/status/' + status.id + '/unarchive')
  780. .then(res => {
  781. this.$emit('unarchived', status.id);
  782. this.closeModals();
  783. });
  784. },
  785. editPost(status) {
  786. this.closeModals();
  787. this.$emit('edit', status);
  788. },
  789. handleMute() {
  790. if(!this.ctxMenuRelationship) {
  791. return;
  792. }
  793. let curState = this.ctxMenuRelationship.muting;
  794. swal({
  795. title: curState ? 'Confirm Unmute' : 'Confirm Mute',
  796. text: curState ? 'Are you sure you want to unmute this account?' : 'Are you sure you want to mute this account?',
  797. icon: "warning",
  798. buttons: true,
  799. dangerMode: true,
  800. })
  801. .then(res => {
  802. if(!res) {
  803. this.closeModals();
  804. } else {
  805. let url = curState ?
  806. `/api/v1/accounts/${this.status.account.id}/unmute` :
  807. `/api/v1/accounts/${this.status.account.id}/mute`;
  808. axios.post(url)
  809. .then(res => {
  810. this.closeModals();
  811. this.$emit('muted', this.status);
  812. this.$store.commit('updateRelationship', [res.data]);
  813. })
  814. .catch(err => {
  815. this.closeModals();
  816. if(err && err.response && err.response.data && err.response.data.error) {
  817. swal('Error', err.response.data.error, 'error');
  818. } else {
  819. swal('Oops!', 'An error occured, please try again later.', 'error');
  820. }
  821. })
  822. }
  823. })
  824. },
  825. handleBlock() {
  826. if(!this.ctxMenuRelationship) {
  827. return;
  828. }
  829. let curState = this.ctxMenuRelationship.blocking;
  830. swal({
  831. title: curState ? 'Confirm Unblock' : 'Confirm Block',
  832. text: curState ? 'Are you sure you want to unblock this account?' : 'Are you sure you want to block this account?',
  833. icon: "warning",
  834. buttons: true,
  835. dangerMode: true,
  836. })
  837. .then(res => {
  838. if(!res) {
  839. this.closeModals();
  840. } else {
  841. let url = curState ?
  842. `/api/v1/accounts/${this.status.account.id}/unblock` :
  843. `/api/v1/accounts/${this.status.account.id}/block`;
  844. axios.post(url)
  845. .then(res => {
  846. this.closeModals();
  847. this.$store.commit('updateRelationship', [res.data]);
  848. this.$emit('muted', this.status);
  849. })
  850. .catch(err => {
  851. this.closeModals();
  852. if(err && err.response && err.response.data && err.response.data.error) {
  853. swal('Error', err.response.data.error, 'error');
  854. } else {
  855. swal('Oops!', 'An error occured, please try again later.', 'error');
  856. }
  857. })
  858. }
  859. })
  860. },
  861. handleUnfollow() {
  862. if(!this.ctxMenuRelationship) {
  863. return;
  864. }
  865. swal({
  866. title: 'Unfollow',
  867. text: 'Are you sure you want to unfollow ' + this.status.account.username + '?',
  868. icon: "warning",
  869. buttons: true,
  870. dangerMode: true,
  871. })
  872. .then(res => {
  873. if(!res) {
  874. this.closeModals();
  875. } else {
  876. axios.post(`/api/v1/accounts/${this.status.account.id}/unfollow`)
  877. .then(res => {
  878. this.closeModals();
  879. this.$store.commit('updateRelationship', [res.data]);
  880. this.$emit('unfollow', this.status);
  881. })
  882. .catch(err => {
  883. this.closeModals();
  884. if(err && err.response && err.response.data && err.response.data.error) {
  885. swal('Error', err.response.data.error, 'error');
  886. } else {
  887. swal('Oops!', 'An error occured, please try again later.', 'error');
  888. }
  889. })
  890. }
  891. })
  892. },
  893. }
  894. }
  895. </script>
  896. <style lang="scss" scoped>
  897. .menu-option {
  898. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  899. text-decoration: none;
  900. font-weight: 500;
  901. color: var(--dark);
  902. }
  903. .list-group-item {
  904. border-color: var(--border-color);
  905. }
  906. .action-icon-link {
  907. display: flex;
  908. flex-direction: column;
  909. .icon {
  910. opacity: 0.5;
  911. margin-bottom: 5px;
  912. }
  913. p {
  914. font-weight: 600;
  915. font-size: 11px;
  916. }
  917. &-inline {
  918. display: flex;
  919. flex-direction: row;
  920. justify-content: center;
  921. align-items: center;
  922. gap: 8px;
  923. p {
  924. font-weight: bold;
  925. }
  926. }
  927. }
  928. </style>