diff --git a/client/src/components/History/CurrentHistory/HistoryDetails.vue b/client/src/components/History/CurrentHistory/HistoryDetails.vue index 1f736c2b7e18..3255b56cae91 100644 --- a/client/src/components/History/CurrentHistory/HistoryDetails.vue +++ b/client/src/components/History/CurrentHistory/HistoryDetails.vue @@ -1,17 +1,24 @@ <script setup lang="ts"> +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faArchive, faBurn, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { BBadge } from "bootstrap-vue"; import type { HistorySummary } from "@/api"; import { useHistoryStore } from "@/stores/historyStore"; +import type { DetailsLayoutSummarized } from "../Layout/types"; + import TextSummary from "@/components/Common/TextSummary.vue"; import DetailsLayout from "@/components/History/Layout/DetailsLayout.vue"; import UtcDate from "@/components/UtcDate.vue"; +library.add(faArchive, faBurn, faTrash); + interface Props { history: HistorySummary; writeable: boolean; - summarized?: "both" | "annotation" | "tags" | "none" | "hidden"; + summarized?: DetailsLayoutSummarized; } const props = withDefaults(defineProps<Props>(), { @@ -53,6 +60,18 @@ function onSave(newDetails: HistorySummary) { <span v-localize>last edited </span> <UtcDate v-if="history.update_time" :date="history.update_time" mode="elapsed" /> </BBadge> + <BBadge v-if="history.purged" pill class="alert-warning"> + <FontAwesomeIcon :icon="faBurn" /> + <span v-localize> Purged</span> + </BBadge> + <BBadge v-else-if="history.deleted" pill class="alert-danger"> + <FontAwesomeIcon :icon="faTrash" /> + <span v-localize> Deleted</span> + </BBadge> + <BBadge v-if="history.archived" pill class="alert-warning"> + <FontAwesomeIcon :icon="faArchive" /> + <span v-localize> Archived</span> + </BBadge> </template> </DetailsLayout> </template> diff --git a/client/src/components/History/CurrentHistory/HistoryOperations/HistoryOperations.vue b/client/src/components/History/CurrentHistory/HistoryOperations/HistoryOperations.vue index 4ae704e4b8fa..a9e593631b7b 100644 --- a/client/src/components/History/CurrentHistory/HistoryOperations/HistoryOperations.vue +++ b/client/src/components/History/CurrentHistory/HistoryOperations/HistoryOperations.vue @@ -12,6 +12,7 @@ library.add(faCheckSquare, faCompress); interface Props { history: HistorySummaryExtended; hasMatches: boolean; + editable: boolean; expandedCount: number; showSelection: boolean; isMultiViewItem: boolean; @@ -32,7 +33,7 @@ function onUpdateOperationStatus(updateTime: number) { <template> <section> - <nav class="content-operations d-flex justify-content-between bg-secondary"> + <nav v-if="editable" class="content-operations d-flex justify-content-between bg-secondary"> <BButtonGroup> <BButton title="Select Items" @@ -66,6 +67,17 @@ function onUpdateOperationStatus(updateTime: number) { :history="history" @update:operation-running="onUpdateOperationStatus" /> </nav> + <nav v-else-if="isMultiViewItem" class="content-operations bg-secondary"> + <BButton + title="Collapse Items" + class="rounded-0" + size="sm" + variant="link" + :disabled="!expandedCount" + @click="$emit('collapse-all')"> + <FontAwesomeIcon :icon="faCompress" fixed-width /> + </BButton> + </nav> </section> </template> diff --git a/client/src/components/History/CurrentHistory/HistoryPanel.vue b/client/src/components/History/CurrentHistory/HistoryPanel.vue index 12d07ccab26b..07de28ce0cf0 100644 --- a/client/src/components/History/CurrentHistory/HistoryPanel.vue +++ b/client/src/components/History/CurrentHistory/HistoryPanel.vue @@ -84,7 +84,7 @@ const contentItemRefs = computed(() => { const currItemFocused = useActiveElement(); const lastItemId = ref<string | null>(null); -const { currentFilterText, currentHistoryId, pinnedHistoriesSummarizedStatus } = storeToRefs(useHistoryStore()); +const { currentFilterText, currentHistoryId, pinnedHistories, storedHistories } = storeToRefs(useHistoryStore()); const { lastCheckedTime, totalMatchesCount, isWatching } = storeToRefs(useHistoryItemsStore()); const historyStore = useHistoryStore(); @@ -143,8 +143,45 @@ const formattedSearchError = computed(() => { }; }); +/** Returns a string that indicates whether all the pinned histories have annotations, + * tags, both, or none of them. + * This is used to format the `DetailsLayout` header uniformly for multi-view histories. + */ const detailsSummarized = computed(() => { - return props.isMultiViewItem ? pinnedHistoriesSummarizedStatus.value : undefined; + if (!props.isMultiViewItem) { + return undefined; + } + if (pinnedHistories.value.length === 0) { + return "hidden"; + } + + let annotation = false; + let tags = false; + let loaded = true; + + for (const h of pinnedHistories.value) { + const history = storedHistories.value[h.id]; + if (!history) { + loaded = false; + break; + } + if (history.annotation) { + annotation = true; + } + if (history.tags.length > 0) { + tags = true; + } + } + + if (!loaded || (annotation && tags)) { + return "both"; + } else if (annotation) { + return "annotation"; + } else if (tags) { + return "tags"; + } else { + return "none"; + } }); const storeFilterText = computed(() => { @@ -530,7 +567,7 @@ function setItemDragstart( :summarized="detailsSummarized" @update:history="historyStore.updateHistory($event)" /> - <HistoryMessages :history="history" :current-user="currentUser" /> + <HistoryMessages v-if="!isMultiViewItem" :history="history" :current-user="currentUser" /> <HistoryCounter :history="history" @@ -543,8 +580,8 @@ function setItemDragstart( @reloadContents="reloadContents" /> <HistoryOperations - v-if="canEditHistory" :history="history" + :editable="canEditHistory" :is-multi-view-item="isMultiViewItem" :show-selection="showSelection" :expanded-count="expandedCount" diff --git a/client/src/components/History/HistoryScrollList.vue b/client/src/components/History/HistoryScrollList.vue index 9b83f5df7afc..c99e2cb619ab 100644 --- a/client/src/components/History/HistoryScrollList.vue +++ b/client/src/components/History/HistoryScrollList.vue @@ -278,20 +278,18 @@ async function loadMore(noScroll = false) { </div> <TextSummary v-else component="h4" :description="history.name" one-line-summary /> <div class="d-flex align-items-center flex-gapx-1"> - <BBadge pill> - <FontAwesomeIcon - v-if="history.purged" - title="Purged" - :icon="faBurn" - fixed-width /> - <FontAwesomeIcon - v-else-if="history.deleted" - title="Deleted" - :icon="faTrash" - fixed-width /> + <BBadge v-if="history.purged" pill class="alert-warning" title="Purged"> + <FontAwesomeIcon :icon="faBurn" fixed-width /> </BBadge> - <BBadge v-if="history.archived" pill> - <FontAwesomeIcon title="Archived" :icon="faArchive" fixed-width /> + <BBadge v-else-if="history.deleted" pill class="alert-danger" title="Deleted"> + <FontAwesomeIcon :icon="faTrash" fixed-width /> + </BBadge> + <BBadge + v-if="history.archived && userOwnsHistory(currentUser, history)" + pill + class="alert-warning" + title="Archived"> + <FontAwesomeIcon :icon="faArchive" fixed-width /> </BBadge> <BBadge pill :title="localize('Amount of items in history')"> {{ history.count }} {{ localize("items") }} diff --git a/client/src/components/History/Layout/DetailsLayout.vue b/client/src/components/History/Layout/DetailsLayout.vue index 2498fc2f2d59..19611ba408b9 100644 --- a/client/src/components/History/Layout/DetailsLayout.vue +++ b/client/src/components/History/Layout/DetailsLayout.vue @@ -9,6 +9,8 @@ import { computed, ref } from "vue"; import { useUserStore } from "@/stores/userStore"; import l from "@/utils/localization"; +import type { DetailsLayoutSummarized } from "./types"; + import TextSummary from "@/components/Common/TextSummary.vue"; import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue"; @@ -20,7 +22,7 @@ interface Props { writeable?: boolean; annotation?: string; showAnnotation?: boolean; - summarized?: "both" | "annotation" | "tags" | "none" | "hidden"; + summarized?: DetailsLayoutSummarized; } const props = withDefaults(defineProps<Props>(), { diff --git a/client/src/stores/historyStore.ts b/client/src/stores/historyStore.ts index 477420076741..734ba55001a4 100644 --- a/client/src/stores/historyStore.ts +++ b/client/src/stores/historyStore.ts @@ -97,42 +97,6 @@ export const useHistoryStore = defineStore("historyStore", () => { }; }); - const pinnedHistoriesSummarizedStatus = computed(() => { - let annotation = false; - let tags = false; - let loaded = true; - - if (pinnedHistories.value.length === 0) { - return "hidden"; - } - - for (const h of pinnedHistories.value) { - const history = storedHistories.value[h.id]; - if (!history) { - loaded = false; - break; - } - - if (history.annotation) { - annotation = true; - } - - if (history.tags.length > 0) { - tags = true; - } - } - - if (!loaded || (annotation && tags)) { - return "both"; - } else if (annotation) { - return "annotation"; - } else if (tags) { - return "tags"; - } else { - return "none"; - } - }); - async function setCurrentHistory(historyId: string) { const currentHistory = (await setCurrentHistoryOnServer(historyId)) as HistoryDevDetailed; selectHistory(currentHistory); @@ -369,11 +333,7 @@ export const useHistoryStore = defineStore("historyStore", () => { currentHistoryId, currentFilterText, pinnedHistories, - /** Returns a string that indicates whether all the pinned histories have annotations, - * tags, both, or none of them. - * This is used to format the `DetailsLayout` header uniformly for multi-view histories. - */ - pinnedHistoriesSummarizedStatus, + storedHistories, getHistoryById, getHistoryNameById, setCurrentHistory,