Skip to content

Commit

Permalink
Merge pull request #57 from simonsobs/dev
Browse files Browse the repository at this point in the history
Enable Reordering of Queue Items
  • Loading branch information
TaiSakuma authored May 29, 2024
2 parents d02df19 + 974eacd commit c2ba0c8
Show file tree
Hide file tree
Showing 9 changed files with 390 additions and 51 deletions.
86 changes: 82 additions & 4 deletions src/components/queue/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ import { useSubscribeScheduleQueueItems } from "@/api/use-schedule-queue-items-s
import {
useScheduleQueuePushMutation,
useScheduleQueueRemoveMutation,
useScheduleQueueMoveToFirstMutation,
useScheduleQueueMoveOneForwardMutation,
useScheduleQueueMoveToLastMutation,
useScheduleQueueMoveOneBackwardMutation,
} from "@/graphql/codegen/generated";
import type {
ScheduleQueuePushInput,
ScheduleQueuePushMutation,
ScheduleQueueRemoveMutation,
ScheduleQueueMoveToFirstMutation,
ScheduleQueueMoveOneForwardMutation,
ScheduleQueueMoveToLastMutation,
ScheduleQueueMoveOneBackwardMutation,
} from "@/graphql/codegen/generated";

export interface Item {
Expand All @@ -24,11 +32,32 @@ export interface Item {
type AddItemResult = OperationResult<ScheduleQueuePushMutation, AnyVariables>;
type DeleteItemResult = OperationResult<ScheduleQueueRemoveMutation, AnyVariables>;

type MoveItemToTopResult = OperationResult<
ScheduleQueueMoveToFirstMutation,
AnyVariables
>;
type MoveItemOneUpResult = OperationResult<
ScheduleQueueMoveOneForwardMutation,
AnyVariables
>;
type MoveItemOneDownResult = OperationResult<
ScheduleQueueMoveOneBackwardMutation,
AnyVariables
>;
type MoveItemToBottomResult = OperationResult<
ScheduleQueueMoveToLastMutation,
AnyVariables
>;

interface _UseItemsResponse {
items: Ref<Item[]>;
loading: Ref<boolean>;
addItem: (item: ScheduleQueuePushInput) => Promise<AddItemResult>;
deleteItem: (item: Item) => Promise<DeleteItemResult>;
moveItemToTop: (item: Item) => Promise<MoveItemToTopResult>;
moveItemOneUp: (item: Item) => Promise<MoveItemOneUpResult>;
moveItemOneDown: (item: Item) => Promise<MoveItemOneDownResult>;
moveItemToBottom: (item: Item) => Promise<MoveItemToBottomResult>;
}

type UseItemsResponse = _UseItemsResponse & PromiseLike<_UseItemsResponse>;
Expand Down Expand Up @@ -65,19 +94,32 @@ export function useItems(): UseItemsResponse {
};
const item_ = itemMap.value.get(item.id);
if (item_) {
Object.assign(item_, update); // Update the existing item object.
Object.assign(item_, update); // Update the existing item object.
items.value.push(item_);
} else {
itemMap.value.set(item.id, update); // new item object.
itemMap.value.set(item.id, update); // new item object.
items.value.push(update);
}
});
});

const { addItem } = useAddItem();
const { deleteItem } = useDeleteItem();

const ret = { items, loading, addItem, deleteItem };
const { moveItemToTop } = useMoveItemToTop();
const { moveItemOneUp } = useMoveItemOneUp();
const { moveItemOneDown } = useMoveItemOneDown();
const { moveItemToBottom } = useMoveItemToBottom();

const ret = {
items,
loading,
addItem,
deleteItem,
moveItemToTop,
moveItemOneUp,
moveItemOneDown,
moveItemToBottom,
};

return {
...ret,
Expand Down Expand Up @@ -105,3 +147,39 @@ function useDeleteItem() {

return { deleteItem };
}

function useMoveItemToTop() {
const { executeMutation } = useScheduleQueueMoveToFirstMutation();
async function moveItemToTop(item: Item) {
return await executeMutation({ id: item.id });
}

return { moveItemToTop };
}

function useMoveItemOneUp() {
const { executeMutation } = useScheduleQueueMoveOneForwardMutation();
async function moveItemOneUp(item: Item) {
return await executeMutation({ id: item.id });
}

return { moveItemOneUp };
}

function useMoveItemOneDown() {
const { executeMutation } = useScheduleQueueMoveOneBackwardMutation();
async function moveItemOneDown(item: Item) {
return await executeMutation({ id: item.id });
}

return { moveItemOneDown };
}

function useMoveItemToBottom() {
const { executeMutation } = useScheduleQueueMoveToLastMutation();
async function moveItemToBottom(item: Item) {
return await executeMutation({ id: item.id });
}

return { moveItemToBottom };
}
File renamed without changes.
148 changes: 148 additions & 0 deletions src/components/queue/view/TopFrame.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<template>
<div class="d-flex">
<VBtn v-if="mobile" variant="text" icon="mdi-close" @click="show = false"></VBtn>
<VSpacer></VSpacer>
<VBtn
variant="text"
icon="mdi-trash-can-outline"
@click="dialogConfirmDelete = true"
></VBtn>
<VMenu :close-on-content-click="false">
<template v-slot:activator="{ props }">
<VBtn v-bind="props" variant="text" icon="mdi-dots-horizontal"></VBtn>
</template>
<VList>
<VListSubheader>Move (Reorder)</VListSubheader>
<VListItem :disabled="atTop" @click="moveToTop">
<template v-slot:prepend>
<v-icon> mdi-arrow-up </v-icon>
</template>
To Top
</VListItem>
<VListItem :disabled="atTop" @click="moveOneUp">
<template v-slot:prepend>
<v-icon> mdi-arrow-up-thin </v-icon>
</template>
One Up
</VListItem>
<VListItem :disabled="atBottom" @click="moveOneDown">
<template v-slot:prepend>
<v-icon> mdi-arrow-down-thin </v-icon>
</template>
One Down
</VListItem>
<VListItem :disabled="atBottom" @click="moveToBottom">
<template v-slot:prepend>
<v-icon> mdi-arrow-down</v-icon>
</template>
To Bottom
</VListItem>
</VList>
</VMenu>
<DeleteConfirmationDialog
v-model="dialogConfirmDelete"
:item="item"
@confirm="onDeleteConfirmed"
>
</DeleteConfirmationDialog>
<LoadingIndicator v-model="loading"> </LoadingIndicator>
<ErrorDialog v-model="dialogError" :error="error"> </ErrorDialog>
</div>
</template>

<script setup lang="ts">
import { ref, computed, toRefs } from "vue";
import { useDisplay } from "vuetify";
import type { CombinedError } from "@urql/vue";
import { useItems } from "../items";
import type { Item } from "../items";
import DeleteConfirmationDialog from "./DeleteConfirmationDialog.vue";
import LoadingIndicator from "../LoadingIndicator.vue";
import ErrorDialog from "../ErrorDialog.vue";
const { mobile } = useDisplay();
interface Props {
item: Item;
nItems: number;
}
const props = defineProps<Props>();
const { item } = toRefs(props);
const show = defineModel<boolean>();
const dialogConfirmDelete = ref(false);
const loading = ref<boolean>(false);
const dialogError = ref<boolean>(false);
const error = ref<CombinedError>();
const {
items,
deleteItem,
moveItemToTop,
moveItemOneUp,
moveItemOneDown,
moveItemToBottom,
} = useItems();
const nItems = computed(() => items.value?.length ?? 0);
const atTop = computed(() => item.value.order === 1);
const atBottom = computed(() => item.value.order === nItems.value);
async function onDeleteConfirmed() {
loading.value = true;
const result = await deleteItem(item.value);
loading.value = false;
if (result.error) {
error.value = result.error;
dialogError.value = true;
return;
}
show.value = false;
}
async function moveToTop() {
loading.value = true;
const result = await moveItemToTop(item.value);
loading.value = false;
if (result.error) {
error.value = result.error;
dialogError.value = true;
return;
}
}
async function moveOneUp() {
loading.value = true;
const result = await moveItemOneUp(item.value);
loading.value = false;
if (result.error) {
error.value = result.error;
dialogError.value = true;
return;
}
}
async function moveOneDown() {
loading.value = true;
const result = await moveItemOneDown(item.value);
loading.value = false;
if (result.error) {
error.value = result.error;
dialogError.value = true;
return;
}
}
async function moveToBottom() {
loading.value = true;
const result = await moveItemToBottom(item.value);
loading.value = false;
if (result.error) {
error.value = result.error;
dialogError.value = true;
return;
}
}
</script>

<style scoped></style>
53 changes: 7 additions & 46 deletions src/components/queue/view/ViewDialog.vue
Original file line number Diff line number Diff line change
@@ -1,49 +1,27 @@
<template>
<v-dialog v-model="show" :fullscreen="mobile" :transition="transition">
<v-sheet class="g-container pa-4" :class="{ 'g-mobile': mobile }">
<div class="g-top d-flex">
<v-btn
v-if="mobile"
variant="text"
icon="mdi-close"
@click="show = false"
></v-btn>
<v-spacer v-if="!mobile"></v-spacer>
<v-btn
variant="text"
icon="mdi-trash-can-outline"
@click="dialogConfirmDelete = true"
></v-btn>
<div class="g-top">
<TopFrame v-model="show" :item="item" :n-items="nItems"> </TopFrame>
</div>
<div class="g-content">
<item-view :item="item" :n-items="nItems"> </item-view>
<ContentFrame :item="item" :n-items="nItems"> </ContentFrame>
</div>
<div class="g-bottom d-flex" v-if="!mobile">
<v-spacer></v-spacer>
<v-btn variant="text" @click="show = false">Close</v-btn>
</div>
</v-sheet>
<delete-confirmation-dialog
v-model="dialogConfirmDelete"
:item="item"
@confirm="onDeleteConfirmed"
>
</delete-confirmation-dialog>
<LoadingIndicator v-model="loading"> </LoadingIndicator>
<ErrorDialog v-model="dialogError" :error="error"> </ErrorDialog>
</v-dialog>
</template>

<script setup lang="ts">
import { ref, computed, toRefs } from "vue";
import { computed, toRefs } from "vue";
import { useDisplay } from "vuetify";
import type { CombinedError } from "@urql/vue";
import ItemView from "./ItemView.vue";
import TopFrame from "./TopFrame.vue";
import ContentFrame from "./ContentFrame.vue";
import { useItems } from "../items";
import type { Item } from "../items";
import DeleteConfirmationDialog from "./DeleteConfirmationDialog.vue";
import LoadingIndicator from "../LoadingIndicator.vue";
import ErrorDialog from "../ErrorDialog.vue";
const { mobile } = useDisplay();
Expand All @@ -57,26 +35,9 @@ interface Props {
const props = defineProps<Props>();
const { item } = toRefs(props);
const show = defineModel<boolean>();
const dialogConfirmDelete = ref(false);
const loading = ref<boolean>(false);
const dialogError = ref<boolean>(false);
const error = ref<CombinedError>();
const { items, deleteItem } = useItems();
const { items } = useItems();
const nItems = computed(() => items.value?.length ?? 0);
async function onDeleteConfirmed() {
loading.value = true;
const result = await deleteItem(item.value);
loading.value = false;
if (result.error) {
error.value = result.error;
dialogError.value = true;
return;
}
show.value = false;
}
</script>

<style scoped>
Expand Down
Loading

0 comments on commit c2ba0c8

Please sign in to comment.