RecentActivity.vue 4.1 KB

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