Confirmation prompts (replace browser confirmation prompts)

This commit is contained in:
Hafiz
2025-11-06 12:59:57 -06:00
parent 270ff2e034
commit ccc7e7d7a7
8 changed files with 231 additions and 44 deletions

View File

@@ -1,9 +1,10 @@
<template> <template>
<b-modal <div>
id="buy-modal" <b-modal
:hide-header="true" id="buy-modal"
@change="onChange($event)" :hide-header="true"
> @change="onChange($event)"
>
<span <span
v-if="withPin" v-if="withPin"
class="badge-dialog" class="badge-dialog"
@@ -226,7 +227,9 @@
:amount-needed="item.value" :amount-needed="item.value"
/> />
</div> </div>
</b-modal> </b-modal>
<purchaseConfirmModal />
</div>
</template> </template>
<style lang="scss"> <style lang="scss">
@@ -643,6 +646,7 @@ import EquipmentAttributesGrid from '../inventory/equipment/attributesGrid.vue';
import Item from '@/components/inventory/item'; import Item from '@/components/inventory/item';
import Avatar from '@/components/avatar'; import Avatar from '@/components/avatar';
import purchaseConfirmModal from './purchaseConfirmModal.vue';
const dropEggs = eggs.drops; const dropEggs = eggs.drops;
const dropPotions = hatchingPotions.drops; const dropPotions = hatchingPotions.drops;
@@ -667,6 +671,7 @@ export default {
PinBadge, PinBadge,
CountdownBanner, CountdownBanner,
numberIncrement, numberIncrement,
purchaseConfirmModal,
}, },
mixins: [ mixins: [
avatarEditorUtilities, avatarEditorUtilities,
@@ -851,10 +856,15 @@ export default {
- ownedMounts - ownedMounts
- ownedItems; - ownedItems;
if ( if (petsRemaining < 0) {
petsRemaining < 0 const confirmed = await new Promise(resolve => {
&& !window.confirm(this.$t('purchasePetItemConfirm', { itemText: this.item.text })) // eslint-disable-line no-alert this.$root.$emit('habitica:purchase-confirm', {
) return; message: this.$t('purchasePetItemConfirm', { itemText: this.item.text }),
resolve,
});
});
if (!confirmed) return;
}
} }
if (this.item.purchaseType === 'customization') { if (this.item.purchaseType === 'customization') {
@@ -866,11 +876,11 @@ export default {
this.purchased(this.item.text); this.purchased(this.item.text);
} else { } else {
const shouldConfirmPurchase = this.item.currency === 'gems' || this.item.currency === 'hourglasses'; const shouldConfirmPurchase = this.item.currency === 'gems' || this.item.currency === 'hourglasses';
if ( if (shouldConfirmPurchase) {
shouldConfirmPurchase const confirmed = await this.confirmPurchase(this.item.currency, this.item.value * this.selectedAmountToBuy);
&& !this.confirmPurchase(this.item.currency, this.item.value * this.selectedAmountToBuy) if (!confirmed) {
) { return;
return; }
} }
if (this.genericPurchase) { if (this.genericPurchase) {
if (this.item.key === 'rebirth_orb') { if (this.item.key === 'rebirth_orb') {

View File

@@ -0,0 +1,65 @@
<template>
<b-modal
id="purchase-confirm-modal"
size="md"
:hide-footer="true"
>
<div class="text-center">
<h2 class="col-12">
{{ confirmationMessage }}
</h2>
</div>
<div class="modal-footer d-flex justify-content-between">
<button
class="btn btn-primary"
@click="confirm()"
>
{{ $t('confirm') }}
</button>
<button
class="btn btn-secondary"
@click="cancel()"
>
{{ $t('cancel') }}
</button>
</div>
</b-modal>
</template>
<script>
export default {
data () {
return {
confirmationMessage: '',
resolveCallback: null,
};
},
mounted () {
this.$root.$on('habitica:purchase-confirm', config => {
this.confirmationMessage = config.message;
this.resolveCallback = config.resolve;
this.$root.$emit('bv::show::modal', 'purchase-confirm-modal');
});
},
beforeDestroy () {
this.$root.$off('habitica:purchase-confirm');
},
methods: {
confirm () {
if (this.resolveCallback) {
this.resolveCallback(true);
}
this.close();
},
cancel () {
if (this.resolveCallback) {
this.resolveCallback(false);
}
this.close();
},
close () {
this.$root.$emit('bv::hide::modal', 'purchase-confirm-modal');
},
},
};
</script>

View File

@@ -1,9 +1,10 @@
<template> <template>
<b-modal <div>
id="buy-quest-modal" <b-modal
:hide-header="true" id="buy-quest-modal"
@change="onChange($event)" :hide-header="true"
> @change="onChange($event)"
>
<span <span
v-if="withPin" v-if="withPin"
class="badge-dialog" class="badge-dialog"
@@ -114,7 +115,9 @@
:amount-needed="item.value" :amount-needed="item.value"
/> />
</div> </div>
</b-modal> </b-modal>
<purchaseConfirmModal />
</div>
</template> </template>
<style lang="scss"> <style lang="scss">
@@ -434,6 +437,7 @@ import numberIncrement from '@/components/shared/numberIncrement';
import questDialogContent from './questDialogContent'; import questDialogContent from './questDialogContent';
import QuestRewards from './questRewards'; import QuestRewards from './questRewards';
import CloseIcon from '../../shared/closeIcon'; import CloseIcon from '../../shared/closeIcon';
import purchaseConfirmModal from '../purchaseConfirmModal.vue';
export default { export default {
components: { components: {
@@ -444,6 +448,7 @@ export default {
questDialogContent, questDialogContent,
CountdownBanner, CountdownBanner,
numberIncrement, numberIncrement,
purchaseConfirmModal,
}, },
mixins: [buyMixin, currencyMixin, notifications, numberInvalid], mixins: [buyMixin, currencyMixin, notifications, numberInvalid],
props: { props: {
@@ -510,8 +515,9 @@ export default {
this.selectedAmountToBuy = 1; this.selectedAmountToBuy = 1;
this.$emit('change', $event); this.$emit('change', $event);
}, },
buyItem () { async buyItem () {
if (!this.confirmPurchase(this.item.currency, this.item.value * this.selectedAmountToBuy)) { const confirmed = await this.confirmPurchase(this.item.currency, this.item.value * this.selectedAmountToBuy);
if (!confirmed) {
return; return;
} }
this.makeGenericPurchase(this.item, 'buyQuestModal', this.selectedAmountToBuy); this.makeGenericPurchase(this.item, 'buyQuestModal', this.selectedAmountToBuy);

View File

@@ -1,10 +1,11 @@
<template> <template>
<b-modal <div>
id="broken-task-modal" <b-modal
title="Broken Challenge" id="broken-task-modal"
size="sm" title="Broken Challenge"
:hide-footer="true" size="sm"
> :hide-footer="true"
>
<div <div
v-if="brokenChallengeTask && brokenChallengeTask.challenge" v-if="brokenChallengeTask && brokenChallengeTask.challenge"
class="modal-body" class="modal-body"
@@ -64,7 +65,9 @@
</div> </div>
</div> </div>
</div> </div>
</b-modal> </b-modal>
<deleteTaskConfirmModal />
</div>
</template> </template>
<style scoped> <style scoped>
@@ -76,8 +79,12 @@
<script> <script>
import { mapActions } from '@/libs/store'; import { mapActions } from '@/libs/store';
import notifications from '@/mixins/notifications'; import notifications from '@/mixins/notifications';
import deleteTaskConfirmModal from './deleteTaskConfirmModal.vue';
export default { export default {
components: {
deleteTaskConfirmModal,
},
mixins: [notifications], mixins: [notifications],
data () { data () {
return { return {
@@ -122,8 +129,14 @@ export default {
}); });
this.close(); this.close();
}, },
removeTask () { async removeTask () {
if (!window.confirm('Are you sure you want to delete this task?')) return; // eslint-disable-line no-alert const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
message: 'Are you sure you want to delete this task?',
resolve,
});
});
if (!confirmed) return;
this.destroyTask(this.brokenChallengeTask); this.destroyTask(this.brokenChallengeTask);
this.close(); this.close();
}, },

View File

@@ -0,0 +1,65 @@
<template>
<b-modal
id="delete-task-confirm-modal"
size="md"
:hide-footer="true"
>
<div class="text-center">
<h2 class="col-12">
{{ confirmationMessage }}
</h2>
</div>
<div class="modal-footer d-flex justify-content-between">
<button
class="btn btn-danger"
@click="confirm()"
>
{{ $t('delete') }}
</button>
<button
class="btn btn-secondary"
@click="cancel()"
>
{{ $t('cancel') }}
</button>
</div>
</b-modal>
</template>
<script>
export default {
data () {
return {
confirmationMessage: '',
resolveCallback: null,
};
},
mounted () {
this.$root.$on('habitica:delete-task-confirm', config => {
this.confirmationMessage = config.message;
this.resolveCallback = config.resolve;
this.$root.$emit('bv::show::modal', 'delete-task-confirm-modal');
});
},
beforeDestroy () {
this.$root.$off('habitica:delete-task-confirm');
},
methods: {
confirm () {
if (this.resolveCallback) {
this.resolveCallback(true);
}
this.close();
},
cancel () {
if (this.resolveCallback) {
this.resolveCallback(false);
}
this.close();
},
close () {
this.$root.$emit('bv::hide::modal', 'delete-task-confirm-modal');
},
},
};
</script>

View File

@@ -1,8 +1,9 @@
<template> <template>
<div <div>
class="task-wrapper" <div
draggable class="task-wrapper"
> draggable
>
<div <div
class="task transition" class="task transition"
:class="[{ :class="[{
@@ -418,6 +419,8 @@
:group="group" :group="group"
/> />
</div> </div>
</div>
<deleteTaskConfirmModal />
</div> </div>
</template> </template>
@@ -937,11 +940,13 @@ import scoreTask from '@/mixins/scoreTask';
import sync from '@/mixins/sync'; import sync from '@/mixins/sync';
import approvalFooter from './approvalFooter'; import approvalFooter from './approvalFooter';
import MenuDropdown from '../ui/customMenuDropdown'; import MenuDropdown from '../ui/customMenuDropdown';
import deleteTaskConfirmModal from './deleteTaskConfirmModal.vue';
export default { export default {
components: { components: {
approvalFooter, approvalFooter,
MenuDropdown, MenuDropdown,
deleteTaskConfirmModal,
}, },
directives: { directives: {
markdown: markdownDirective, markdown: markdownDirective,
@@ -1177,9 +1182,15 @@ export default {
moveToBottom () { moveToBottom () {
this.$emit('moveTo', this.task, 'bottom'); this.$emit('moveTo', this.task, 'bottom');
}, },
destroy () { async destroy () {
const type = this.$t(this.task.type); const type = this.$t(this.task.type);
if (!window.confirm(this.$t('sureDeleteType', { type }))) return; // eslint-disable-line no-alert const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
message: this.$t('sureDeleteType', { type }),
resolve,
});
});
if (!confirmed) return;
this.destroyTask(this.task); this.destroyTask(this.task);
this.$emit('taskDestroyed', this.task); this.$emit('taskDestroyed', this.task);
}, },

View File

@@ -1,6 +1,7 @@
<template> <template>
<b-modal <div>
id="task-modal" <b-modal
id="task-modal"
:no-close-on-esc="true" :no-close-on-esc="true"
:no-close-on-backdrop="true" :no-close-on-backdrop="true"
size="sm" size="sm"
@@ -606,7 +607,9 @@
</div> </div>
</form> </form>
</div> </div>
</b-modal> </b-modal>
<deleteTaskConfirmModal />
</div>
</template> </template>
<style lang="scss"> <style lang="scss">
@@ -1050,6 +1053,7 @@ import chevronIcon from '@/assets/svg/chevron.svg?raw';
import calendarIcon from '@/assets/svg/calendar.svg?raw'; import calendarIcon from '@/assets/svg/calendar.svg?raw';
import gripIcon from '@/assets/svg/grip.svg?raw'; import gripIcon from '@/assets/svg/grip.svg?raw';
import InformationIcon from '@/components/ui/informationIcon.vue'; import InformationIcon from '@/components/ui/informationIcon.vue';
import deleteTaskConfirmModal from './deleteTaskConfirmModal.vue';
export default { export default {
components: { components: {
@@ -1061,6 +1065,7 @@ export default {
selectTranslatedArray, selectTranslatedArray,
toggleCheckbox, toggleCheckbox,
lockableLabel, lockableLabel,
deleteTaskConfirmModal,
}, },
directives: { directives: {
markdown: markdownDirective, markdown: markdownDirective,
@@ -1305,9 +1310,15 @@ export default {
} }
this.$root.$emit('bv::hide::modal', 'task-modal'); this.$root.$emit('bv::hide::modal', 'task-modal');
}, },
destroy () { async destroy () {
const type = this.$t(this.task.type); const type = this.$t(this.task.type);
if (!window.confirm(this.$t('sureDeleteType', { type }))) return; // eslint-disable-line no-alert const confirmed = await new Promise(resolve => {
this.$root.$emit('habitica:delete-task-confirm', {
message: this.$t('sureDeleteType', { type }),
resolve,
});
});
if (!confirmed) return;
this.destroyTask(this.task); this.destroyTask(this.task);
this.$emit('taskDestroyed', this.task); this.$emit('taskDestroyed', this.task);
this.$root.$emit('bv::hide::modal', 'task-modal'); this.$root.$emit('bv::hide::modal', 'task-modal');

View File

@@ -39,7 +39,13 @@ export default {
}; };
const purchaseForKey = currencyToPurchaseForKey[currency]; const purchaseForKey = currencyToPurchaseForKey[currency];
return window.confirm(this.$t(purchaseForKey, { cost })); // eslint-disable-line no-alert
return new Promise(resolve => {
this.$root.$emit('habitica:purchase-confirm', {
message: this.$t(purchaseForKey, { cost }),
resolve,
});
});
}, },
}, },
}; };