1
0

TimelineStatus.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. <template>
  2. <div class="timeline-status-component">
  3. <div class="card shadow-sm" style="border-radius: 15px;">
  4. <post-header
  5. :profile="profile"
  6. :status="shadowStatus"
  7. :is-reblog="isReblog"
  8. :reblog-account="reblogAccount"
  9. @menu="openMenu"
  10. @follow="follow"
  11. @unfollow="unfollow" />
  12. <post-content
  13. :profile="profile"
  14. :status="shadowStatus" />
  15. <post-reactions
  16. v-if="reactionBar"
  17. :status="shadowStatus"
  18. :profile="profile"
  19. :admin="admin"
  20. v-on:like="like"
  21. v-on:unlike="unlike"
  22. v-on:share="shareStatus"
  23. v-on:unshare="unshareStatus"
  24. v-on:likes-modal="showLikes"
  25. v-on:shares-modal="showShares"
  26. v-on:toggle-comments="showComments"
  27. v-on:bookmark="handleBookmark"
  28. v-on:mod-tools="openModTools" />
  29. <div v-if="showCommentDrawer" class="card-footer rounded-bottom border-0" style="background: rgba(0,0,0,0.02);z-index: 3;">
  30. <comment-drawer
  31. :status="shadowStatus"
  32. :profile="profile"
  33. v-on:handle-report="handleReport"
  34. v-on:counter-change="counterChange"
  35. v-on:show-likes="showCommentLikes"
  36. v-on:follow="follow"
  37. v-on:unfollow="unfollow" />
  38. </div>
  39. </div>
  40. </div>
  41. </template>
  42. <script type="text/javascript">
  43. import CommentDrawer from './post/CommentDrawer.vue';
  44. import PostHeader from './post/PostHeader.vue';
  45. import PostContent from './post/PostContent.vue';
  46. import PostReactions from './post/PostReactions.vue';
  47. export default {
  48. props: {
  49. status: {
  50. type: Object
  51. },
  52. profile: {
  53. type: Object
  54. },
  55. reactionBar: {
  56. type: Boolean,
  57. default: true
  58. },
  59. useDropdownMenu: {
  60. type: Boolean,
  61. default: false
  62. }
  63. },
  64. components: {
  65. "comment-drawer": CommentDrawer,
  66. "post-content": PostContent,
  67. "post-header": PostHeader,
  68. "post-reactions": PostReactions
  69. },
  70. data() {
  71. return {
  72. key: 1,
  73. menuLoading: true,
  74. sensitive: false,
  75. showCommentDrawer: false,
  76. isReblogging: false,
  77. isBookmarking: false,
  78. owner: false,
  79. admin: false,
  80. license: false
  81. }
  82. },
  83. mounted() {
  84. this.license = this.shadowStatus.media_attachments && this.shadowStatus.media_attachments.length ?
  85. this.shadowStatus
  86. .media_attachments
  87. .filter(m => m.hasOwnProperty('license') && m.license && m.license.hasOwnProperty('id'))
  88. .map(m => m.license)[0] : false;
  89. this.admin = window._sharedData.user.is_admin;
  90. this.owner = this.shadowStatus.account.id == window._sharedData.user.id;
  91. if(this.shadowStatus.reply_count && this.autoloadComments && this.shadowStatus.comments_disabled === false) {
  92. setTimeout(() => {
  93. this.showCommentDrawer = true;
  94. }, 1000);
  95. }
  96. },
  97. computed: {
  98. hideCounts: {
  99. get() {
  100. return this.$store.state.hideCounts == true;
  101. }
  102. },
  103. fixedHeight: {
  104. get() {
  105. return this.$store.state.fixedHeight == true;
  106. }
  107. },
  108. autoloadComments: {
  109. get() {
  110. return this.$store.state.autoloadComments == true;
  111. }
  112. },
  113. newReactions: {
  114. get() {
  115. return this.$store.state.newReactions;
  116. },
  117. },
  118. isReblog: {
  119. get() {
  120. return this.status.reblog != null;
  121. }
  122. },
  123. reblogAccount: {
  124. get() {
  125. return this.status.reblog ? this.status.account : null;
  126. }
  127. },
  128. shadowStatus: {
  129. get() {
  130. return this.status.reblog ? this.status.reblog : this.status;
  131. }
  132. }
  133. },
  134. watch: {
  135. status: {
  136. deep: true,
  137. immediate: true,
  138. handler: function(o, n) {
  139. this.isBookmarking = false;
  140. }
  141. },
  142. },
  143. methods: {
  144. openMenu() {
  145. this.$emit('menu');
  146. },
  147. like() {
  148. this.$emit('like');
  149. },
  150. unlike() {
  151. this.$emit('unlike');
  152. },
  153. showLikes() {
  154. this.$emit('likes-modal');
  155. },
  156. showShares() {
  157. this.$emit('shares-modal');
  158. },
  159. showComments() {
  160. this.showCommentDrawer = !this.showCommentDrawer;
  161. },
  162. copyLink() {
  163. event.currentTarget.blur();
  164. App.util.clipboard(this.status.url);
  165. },
  166. shareToOther() {
  167. if (navigator.canShare) {
  168. navigator.share({
  169. url: this.status.url
  170. })
  171. .then(() => console.log('Share was successful.'))
  172. .catch((error) => console.log('Sharing failed', error));
  173. } else {
  174. swal('Not supported', 'Your current device does not support native sharing.', 'error');
  175. }
  176. },
  177. counterChange(type) {
  178. this.$emit('counter-change', type);
  179. },
  180. showCommentLikes(post) {
  181. this.$emit('comment-likes-modal', post);
  182. },
  183. shareStatus() {
  184. this.$emit('share');
  185. },
  186. unshareStatus() {
  187. this.$emit('unshare');
  188. },
  189. handleReport(post) {
  190. this.$emit('handle-report', post);
  191. },
  192. follow() {
  193. this.$emit('follow');
  194. },
  195. unfollow() {
  196. this.$emit('unfollow');
  197. },
  198. handleReblog() {
  199. this.isReblogging = true;
  200. if(this.status.reblogged) {
  201. this.$emit('unshare');
  202. } else {
  203. this.$emit('share');
  204. }
  205. setTimeout(() => {
  206. this.isReblogging = false;
  207. }, 5000);
  208. },
  209. handleBookmark() {
  210. event.currentTarget.blur();
  211. this.isBookmarking = true;
  212. this.$emit('bookmark');
  213. setTimeout(() => {
  214. this.isBookmarking = false;
  215. }, 5000);
  216. },
  217. getStatusAvatar() {
  218. if(window._sharedData.user.id == this.status.account.id) {
  219. return window._sharedData.user.avatar;
  220. }
  221. return this.status.account.avatar;
  222. },
  223. openModTools() {
  224. this.$emit('mod-tools');
  225. }
  226. }
  227. }
  228. </script>
  229. <style lang="scss">
  230. .timeline-status-component {
  231. margin-bottom: 1rem;
  232. .btn:focus {
  233. box-shadow: none !important;
  234. }
  235. .avatar {
  236. border-radius: 15px;
  237. }
  238. .VueCarousel-wrapper {
  239. .VueCarousel-slide {
  240. img {
  241. object-fit: contain;
  242. }
  243. }
  244. }
  245. .status-text {
  246. z-index: 3;
  247. &.py-0 {
  248. font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  249. }
  250. }
  251. .reaction-liked-by {
  252. font-size: 11px;
  253. font-weight: 600;
  254. font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  255. }
  256. .timestamp,
  257. .visibility,
  258. .location {
  259. color: #94a3b8;
  260. font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  261. }
  262. .invisible {
  263. display: none;
  264. }
  265. .blurhash-wrapper {
  266. img {
  267. border-radius:0;
  268. object-fit: cover;
  269. }
  270. canvas {
  271. border-radius: 0;
  272. }
  273. }
  274. .content-label-wrapper {
  275. position: relative;
  276. width: 100%;
  277. height: 400px;
  278. background-color: #000;
  279. border-radius: 0;
  280. overflow: hidden;
  281. img, canvas {
  282. max-height: 400px;
  283. cursor: pointer;
  284. }
  285. }
  286. .content-label {
  287. margin: 0;
  288. position: absolute;
  289. display: flex;
  290. flex-direction: column;
  291. align-items: center;
  292. justify-content: center;
  293. width: 100%;
  294. height: 100%;
  295. z-index: 2;
  296. border-radius: 0;
  297. background: rgba(0, 0, 0, 0.2)
  298. }
  299. .rounded-bottom {
  300. border-bottom-left-radius: 15px !important;
  301. border-bottom-right-radius: 15px !important;
  302. }
  303. .card-footer {
  304. .media {
  305. position: relative;
  306. .comment-border-link {
  307. display: block;
  308. position: absolute;
  309. top: 40px;
  310. left: 11px;
  311. width: 10px;
  312. height: calc(100% - 100px);
  313. border-left: 4px solid transparent;
  314. border-right: 4px solid transparent;
  315. background-color: #E5E7EB;
  316. background-clip: padding-box;
  317. &:hover {
  318. background-color: #BFDBFE;
  319. }
  320. }
  321. .child-reply-form {
  322. position: relative;
  323. }
  324. .comment-border-arrow {
  325. display: block;
  326. position: absolute;
  327. top: -6px;
  328. left: -33px;
  329. width: 10px;
  330. height: 29px;
  331. border-left: 4px solid transparent;
  332. border-right: 4px solid transparent;
  333. background-color: #E5E7EB;
  334. background-clip: padding-box;
  335. border-bottom: 2px solid transparent;
  336. &:after {
  337. content: '';
  338. display: block;
  339. position: absolute;
  340. top: 25px;
  341. left: 2px;
  342. width: 15px;
  343. height: 2px;
  344. background-color: #E5E7EB;
  345. }
  346. }
  347. &-status {
  348. margin-bottom: 1.3rem;
  349. }
  350. &-avatar {
  351. margin-right: 12px;
  352. border-radius: 8px;
  353. }
  354. &-body {
  355. &-comment {
  356. width: fit-content;
  357. padding: 0.4rem 0.7rem;
  358. background-color: var(--comment-bg);
  359. border-radius: 0.9rem;
  360. &-username {
  361. margin-bottom: 0.25rem !important;
  362. font-size: 14px;
  363. font-weight: 700 !important;
  364. color: var(--body-color);
  365. a {
  366. color: var(--body-color);
  367. text-decoration: none;
  368. }
  369. }
  370. &-content {
  371. margin-bottom: 0;
  372. font-size: 16px;
  373. }
  374. }
  375. &-reactions {
  376. margin-top: 0.4rem !important;
  377. margin-bottom: 0 !important;
  378. color: #B8C2CC !important;
  379. font-size: 12px;
  380. }
  381. }
  382. }
  383. }
  384. .fixedHeight {
  385. max-height: 400px;
  386. .VueCarousel-wrapper {
  387. border-radius: 15px;
  388. }
  389. .VueCarousel-slide {
  390. img {
  391. max-height: 400px;
  392. }
  393. }
  394. .blurhash-wrapper {
  395. img {
  396. height: 400px;
  397. max-height: 400px;
  398. background-color: transparent;
  399. object-fit: contain;
  400. }
  401. canvas {
  402. max-height: 400px;
  403. }
  404. }
  405. .content-label-wrapper {
  406. border-radius: 15px;
  407. }
  408. .content-label {
  409. height: 400px;
  410. border-radius: 0;
  411. }
  412. }
  413. }
  414. </style>