Skip to content

Commit

Permalink
refactor: improve modal controller
Browse files Browse the repository at this point in the history
  • Loading branch information
Yizack committed Oct 19, 2024
1 parent b4ca83f commit d38bdce
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 23 deletions.
9 changes: 8 additions & 1 deletion app/components/ModalController.client.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<script setup lang="ts">
interface ControllerModalModel {
isVisible: boolean;
show: (callback?: () => void) => Promise<void>;
hide: () => void;
}
defineProps({
modelValue: { type: Object as () => ControllerModalModel, required: true },
id: { type: String, required: true },
title: { type: String, default: "" },
lg: { type: Boolean, default: false },
Expand All @@ -9,7 +16,7 @@ defineProps({
</script>

<template>
<div :id="id" class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" :class="{ 'modal-map': map }">
<div v-if="modelValue.isVisible" :id="id" class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" :class="{ 'modal-map': map }">
<div class="modal-dialog modal-dialog-centered" :class="{ 'modal-lg': lg, 'modal-fullscreen': fullscreen }">
<div class="modal-content">
<div v-if="title" class="modal-header">
Expand Down
9 changes: 5 additions & 4 deletions app/components/bond/BondMarkers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ const dragOptions = {
};
const submitted = ref(false);
const showModal = ref(false);
const markerController = useModalController("marker");
const form = useFormState({
id: 0 as number,
lat: null as number | null,
Expand Down Expand Up @@ -61,7 +62,7 @@ const markerModal = (marker?: MappedLoveMarker) => {
location: `${marker.lat}, ${marker.lng}`
};
}
useModalController("marker", showModal).show();
markerController.value.show();
};
const deleteMarker = async (id: number) => {
Expand Down Expand Up @@ -108,7 +109,7 @@ const submitMarker = async () => {
emit("new", { marker });
}
$toasts.add({ message: form.value.id ? t("marker_updated") : t("marker_added"), success: true });
useModalController("marker").hide();
markerController.value.hide();
};
watch(() => props.markers, (value) => {
Expand Down Expand Up @@ -152,7 +153,7 @@ watch(() => props.markers, (value) => {
</TransitionGroup>
</Draggable>
<p v-else class="m-0">{{ t("no_markers") }}</p>
<ModalController v-if="showModal" id="marker" :title="t('marker')" lg>
<ModalController id="marker" v-model="markerController" :title="t('marker')" lg>
<form @submit.prevent="submitMarker">
<div class="d-flex align-items-center gap-2 mb-2">
<Icon name="solar:info-circle-linear" class="text-primary flex-shrink-0" />
Expand Down
8 changes: 4 additions & 4 deletions app/components/bond/BondStories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const deleteStory = async (id: number) => {
const submitted = ref(false);
const supported = "PNG, JPG, WEBP, GIF";
const imageRead = ref<string | ArrayBuffer>("");
const showModal = ref(false);
const storyController = useModalController("story");
const fileChosen = ref(false);
const file = ref<File>();
const maxFileSize = computed(() => {
Expand All @@ -48,7 +48,7 @@ const storyModal = (story?: MappedLoveStory) => {
form.value.marker = props.marker.id;
}
else form.value = { ...story, hash: story.hash };
useModalController("story", showModal).show();
storyController.value.show();
};
const addImage = (event: Event) => {
Expand Down Expand Up @@ -87,7 +87,7 @@ const submitStory = async () => {
if (!story) return;
emit("new", { story, edit: Boolean(form.value.id) });
$toasts.add({ message: form.value.id ? t("story_updated") : t("story_added"), success: true });
useModalController("story").hide();
storyController.value.hide();
};
watch(() => props.marker, () => {
Expand Down Expand Up @@ -147,7 +147,7 @@ watch(() => props.marker, () => {
</div>
</div>
</Transition>
<ModalController v-if="showModal" id="story" :title="t('story')">
<ModalController id="story" v-model="storyController" :title="t('story')">
<form @submit.prevent="submitStory">
<div class="d-flex align-items-center gap-2 mb-2">
<Icon name="solar:info-circle-linear" class="text-primary flex-shrink-0" />
Expand Down
8 changes: 5 additions & 3 deletions app/pages/app/premium/billing.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const refundForm = useFormState({
reason: ""
});
const refundController = useModalController("refund");
const requestRefund = async () => {
if (!confirm(t("refund_confirm"))) return;
loading.value = true;
Expand All @@ -39,7 +41,7 @@ const requestRefund = async () => {
if (!res) return;
const { $toasts } = useNuxtApp();
$toasts.add({ message: t("refund_requested"), success: true });
useModalController("refund").hide();
refundController.value.hide();
refundForm.value.reason = "";
};
Expand Down Expand Up @@ -98,7 +100,7 @@ useSeo({
<p class="mb-0"><strong>{{ t("billing_manageable") }}</strong></p>
</div>
<div v-else-if="billing.subscription?.scheduled_change?.action === 'cancel'" class="text-center">
<button class="btn btn-lg btn-primary w-100 rounded-pill" @click="useModalController('refund').show();">{{ t("request_refund") }}</button>
<button class="btn btn-lg btn-primary w-100 rounded-pill" @click="refundController.show();">{{ t("request_refund") }}</button>
<a href="/legal/refund" target="_blank" class="small">{{ t("refund_info") }}</a>
</div>
<div v-else-if="billing.subscription?.management_urls" class="d-flex flex-column flex-lg-row gap-2">
Expand Down Expand Up @@ -189,7 +191,7 @@ useSeo({
</div>
</div>
</div>
<ModalController v-if="billing.subscription?.scheduled_change?.action === 'cancel' || billing.subscription?.status === 'canceled'" id="refund" :title="t('request_refund')">
<ModalController v-if="billing.subscription?.scheduled_change?.action === 'cancel' || billing.subscription?.status === 'canceled'" id="refund" v-model="refundController" :title="t('request_refund')">
<form @submit.prevent="requestRefund">
<div class="d-flex align-items-center gap-2 mb-2">
<Icon name="solar:info-circle-linear" class="text-primary flex-shrink-0" />
Expand Down
8 changes: 4 additions & 4 deletions app/pages/map/[code].vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ const canvasBody = ref() as Ref<HTMLElement>;
const currentStory = ref<MappedLoveStory>();
const currentStoryUser = computed(() => [partner1.value, partner2.value].find(user => user.id === currentStory.value?.user));
const showModal = ref(false);
const storyController = useModalController("story");
const openStory = (story: MappedLoveStory) => {
currentStory.value = story;
if (isMobile.value) expandCanvas.value = false;
useModalController("story", showModal).show(() => {
storyController.value.show(() => {
document.querySelector(".modal-backdrop")?.classList.add("modal-map-backdrop");
});
};
watch(showModal, (show) => {
watch(() => storyController.value.isVisible, (show) => {
if (!show) currentStory.value = undefined;
});
Expand Down Expand Up @@ -197,7 +197,7 @@ useSeo({
</div>
</div>
</div>
<ModalController v-if="showModal && currentStory" id="story" fullscreen map>
<ModalController v-if="currentStory" id="story" v-model="storyController" fullscreen map>
<div class="position-absolute start-0 top-0 py-2 px-3 bg-body bg-opacity-75 rounded shadow m-2 small">
<div class="d-flex gap-1">
<span>{{ t("uploaded_by") }}:</span>
Expand Down
15 changes: 8 additions & 7 deletions app/utils/composables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,25 @@ export const useFormState = <T extends Record<string, unknown>>(initialState: T)
return data as Ref<T> & typeof methods;
};

export const useModalController = (id: string, visible: Ref<boolean | undefined> = ref()) => {
export const useModalController = (id: string) => {
const { $bootstrap } = useNuxtApp();
return {
show: async (callback?: () => void) => {
if (!visible.value) visible.value = true;
return ref({
isVisible: false,
show: async function (callback?: () => void) {
if (!this.isVisible) this.isVisible = true;
await sleep(100);
const element = $bootstrap.showModal(id);
if (!element) return;
if (visible.value) {
if (this.isVisible) {
if (typeof callback === "function") callback();
const hideEvent = () => {
visible.value = false;
this.isVisible = false;
};
element.addEventListener("hidden.bs.modal", hideEvent, { once: true });
}
},
hide: () => $bootstrap.hideModal(id)
};
});
};

export const useSeo = (options: MappedLoveSeoOptions) => {
Expand Down

0 comments on commit d38bdce

Please sign in to comment.