RecentActivity.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <script setup lang="ts">
  2. import { defineAsyncComponent, ref, onMounted, onUnmounted } from "vue";
  3. import Toast from "toasters";
  4. import { storeToRefs } from "pinia";
  5. import { useWebsocketsStore } from "@/stores/websockets";
  6. import { useUserAuthStore } from "@/stores/userAuth";
  7. import ws from "@/ws";
  8. const ActivityItem = defineAsyncComponent(
  9. () => import("@/components/ActivityItem.vue")
  10. );
  11. const QuickConfirm = defineAsyncComponent(
  12. () => import("@/components/QuickConfirm.vue")
  13. );
  14. const { socket } = useWebsocketsStore();
  15. const props = defineProps({
  16. userId: {
  17. type: String,
  18. default: ""
  19. }
  20. });
  21. const username = ref("");
  22. const activities = ref([]);
  23. const position = ref(1);
  24. const maxPosition = ref(1);
  25. const offsettedFromNextSet = ref(0);
  26. const isGettingSet = ref(false);
  27. const userAuthStore = useUserAuthStore();
  28. const { userId: myUserId } = storeToRefs(userAuthStore);
  29. const { getBasicUser } = userAuthStore;
  30. const hideActivity = activityId => {
  31. socket.dispatch("activities.hideActivity", activityId, res => {
  32. if (res.status !== "success") new Toast(res.message);
  33. });
  34. };
  35. const getSet = () => {
  36. if (isGettingSet.value) return;
  37. if (position.value >= maxPosition.value) return;
  38. isGettingSet.value = true;
  39. socket.dispatch(
  40. "activities.getSet",
  41. props.userId,
  42. position.value,
  43. offsettedFromNextSet.value,
  44. res => {
  45. if (res.status === "success") {
  46. activities.value.push(...res.data.activities);
  47. position.value += 1;
  48. }
  49. isGettingSet.value = false;
  50. }
  51. );
  52. };
  53. const init = () => {
  54. if (myUserId.value !== props.userId)
  55. getBasicUser(props.userId).then((user: any) => {
  56. if (user && user.username) username.value = user.username;
  57. });
  58. socket.dispatch("activities.length", props.userId, res => {
  59. if (res.status === "success") {
  60. maxPosition.value = Math.ceil(res.data.length / 15) + 1;
  61. getSet();
  62. }
  63. });
  64. };
  65. const handleScroll = () => {
  66. const scrollPosition = document.body.clientHeight + window.scrollY;
  67. const bottomPosition = document.body.scrollHeight;
  68. if (scrollPosition + 400 >= bottomPosition) getSet();
  69. return maxPosition.value === position.value;
  70. };
  71. onMounted(() => {
  72. window.addEventListener("scroll", handleScroll);
  73. ws.onConnect(init);
  74. socket.on("event:activity.updated", res => {
  75. activities.value.find(
  76. activity => activity._id === res.data.activityId
  77. ).payload.message = res.data.message;
  78. });
  79. socket.on("event:activity.created", res => {
  80. activities.value.unshift(res.data.activity);
  81. offsettedFromNextSet.value += 1;
  82. });
  83. socket.on("event:activity.hidden", res => {
  84. activities.value = activities.value.filter(
  85. activity => activity._id !== res.data.activityId
  86. );
  87. offsettedFromNextSet.value -= 1;
  88. });
  89. socket.on("event:activity.removeAllForUser", () => {
  90. activities.value = [];
  91. position.value = 1;
  92. maxPosition.value = 1;
  93. offsettedFromNextSet.value = 0;
  94. });
  95. });
  96. onUnmounted(() => {
  97. window.removeEventListener("scroll", handleScroll);
  98. });
  99. </script>
  100. <template>
  101. <div class="content recent-activity-tab">
  102. <div v-if="activities.length > 0">
  103. <h4 class="section-title">Recent activity</h4>
  104. <p class="section-description">
  105. This is a log of all actions
  106. {{ userId === myUserId ? "you have" : `${username} has` }}
  107. taken recently
  108. </p>
  109. <hr class="section-horizontal-rule" />
  110. <div id="activity-items">
  111. <activity-item
  112. class="item activity-item universal-item"
  113. v-for="activity in activities"
  114. :key="activity._id"
  115. :activity="activity"
  116. >
  117. <template #actions>
  118. <quick-confirm
  119. v-if="userId === myUserId"
  120. @confirm="hideActivity(activity._id)"
  121. >
  122. <a content="Hide Activity" v-tippy>
  123. <i class="material-icons hide-icon"
  124. >visibility_off</i
  125. >
  126. </a>
  127. </quick-confirm>
  128. </template>
  129. </activity-item>
  130. </div>
  131. </div>
  132. <div v-else>
  133. <h5>No recent activity.</h5>
  134. </div>
  135. </div>
  136. </template>
  137. <style lang="less" scoped>
  138. .night-mode #activity-items .activity-item {
  139. background-color: var(--dark-grey-2) !important;
  140. border: 0 !important;
  141. }
  142. .content a {
  143. border-bottom: 0;
  144. }
  145. </style>