Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Reordering of Queue Items #57

Merged
merged 6 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 };
}
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