WIP webhook row

This commit is contained in:
negue
2023-03-11 00:03:33 +01:00
parent cc4bedbe2d
commit 1e80a7d145
7 changed files with 258 additions and 36 deletions
@@ -3,37 +3,6 @@
<div class="col-6">
<h2>{{ $t('API') }}</h2>
<p>{{ $t('APIText') }}</p>
<div class="section">
<h3>{{ $t('thirdPartyApps') }}</h3>
<ul>
<li>
<a
target="_blank"
href="https://www.beeminder.com/habitica"
>{{ $t('beeminder') }}</a>
<br>
{{ $t('beeminderDesc') }}
</li>
<li>
<div v-html="$t('chatExtension')">
</div>
<span>{{ $t('chatExtensionDesc') }}</span>
</li>
<li>
<a
target="_blank"
:href="`https://oldgods.net/habitica/habitrpg_user_data_display.html?uuid=` + user._id"
>{{ $t('dataDisplayTool') }}</a>
<br>
{{ $t('dataToolDesc') }}
</li>
<li>
<div v-html="$t('otherExtensions')"></div>
<span>{{ $t('otherDesc') }}</span>
</li>
</ul>
<hr>
</div>
</div>
<div class="col-6">
<h2>{{ $t('webhooks') }}</h2>
@@ -1,5 +1,5 @@
<template>
<div class="input-area">
<div>
<div
v-if="settingsLabel"
class="settings-label"
@@ -94,4 +94,8 @@ export default {
margin-top: 0.5rem;
}
.form-group {
margin-bottom: 0;
}
</style>
@@ -183,7 +183,6 @@ export default {
'general',
'subscription',
'siteData',
'api',
'promoCode',
'transactions',
'notifications',
@@ -35,6 +35,8 @@
</td>
</tr>
</table>
<webhooks-row />
</div>
</div>
</template>
@@ -43,9 +45,12 @@
import UserIdRow from '@/pages/settings/siteDataRows/userIdRow.vue';
import UserDataRow from '@/pages/settings/siteDataRows/userDataRow.vue';
import ApiRow from '@/pages/settings/siteDataRows/apiRow.vue';
import WebhooksRow from '@/pages/settings/siteDataRows/webhooksRow.vue';
export default {
components: { ApiRow, UserDataRow, UserIdRow },
components: {
WebhooksRow, ApiRow, UserDataRow, UserIdRow,
},
mounted () {
this.$store.dispatch('common:setTitle', {
section: this.$t('settings'),
@@ -0,0 +1,244 @@
<template>
<div>
<h2
v-once
>
{{ $t("webhooks") }}
</h2>
<div
v-once
class="webhooks-info"
v-html="$t('webhooksInfo')"
>
</div>
<div class="d-flex justify-content-center webhooks-list">
<table class="table table-striped">
<tr v-if="user.webhooks.length">
<th>{{ $t('webhookURL') }}</th>
<th>{{ $t('enabled') }}</th>
<th></th>
</tr>
<tr
v-for="(webhook, index) in user.webhooks"
:key="webhook.id"
>
<td style="width: 90%">
<div style="width: 440px">
<validated-text-input
v-model="webhook.url"
placeholder="https://habitica-integrations.com/habitica-changes/"
:is-valid="isValidUrl(webhook.url)"
@blur="saveWebhook(webhook, index)"
/>
</div>
</td>
<td style="vertical-align: middle;">
<toggle-switch
v-if="!unsaved.includes(index)"
v-model="webhook.enabled"
@change="updateWebhookEnabled(webhook, index)"
/>
</td>
<td class="menu-column">
<b-dropdown
v-if="!unsaved.includes(index)"
right="right"
toggle-class="with-icon"
class="ml-2"
:no-caret="true"
>
<template #button-content>
<span
v-once
class="svg-icon inline menuIcon"
v-html="icons.menuIcon"
>
</span>
</template>
<b-dropdown-item
class="selectListItem custom-hover--delete"
@click="deleteWebhook(webhook, index)"
>
<span class="with-icon">
<span
v-once
class="svg-icon icon-16 color"
v-html="icons.deleteIcon"
></span>
<span v-once>
{{ $t('delete') }}
</span>
</span>
</b-dropdown-item>
</b-dropdown>
</td>
</tr>
<tr>
<td colspan="3">
</td>
</tr>
</table>
</div>
<button
class="btn btn-secondary d-flex align-items-center"
tabindex="0"
@click="newUnsavedWebhook()"
>
<div
class="svg-icon icon-10 color"
v-html="icons.positive"
></div>
<div class="ml-75 mr-1">
{{ $t('addWebhook') }}
</div>
</button>
</div>
</template>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
.webhooks-info {
line-height: 1.71;
color: $gray-50;
}
.svg-icon.icon-10 {
color: $green-10;
}
.menuIcon {
width: 4px;
height: 1rem;
object-fit: contain;
}
.custom-hover--delete {
--hover-color: #{$maroon-50};
--hover-background: #ffb6b83F;
}
.webhooks-list {
margin-top: 20px;
margin-bottom: 0.5rem;
tr:first-of-type {
th {
padding: 0.25rem;
border-top: 0;
}
}
td {
padding: 0.5rem !important;
&:first-of-type {
text-align: end;
vertical-align: middle;
padding-right: 1rem !important;
font-weight: bold;
line-height: 1.71;
color: $gray-50;
}
}
}
td.menu-column {
width: 2rem;
padding-left: 0 !important;
padding-right: 0 !important;
}
</style>
<script>
import * as validator from 'validator';
import { mapState } from '@/libs/store';
import { InlineSettingMixin } from '../components/inlineSettingMixin';
import uuid from '../../../../../common/script/libs/uuid';
import positiveIcon from '@/assets/svg/positive.svg';
import ToggleSwitch from '@/components/ui/toggleSwitch.vue';
import menuIcon from '@/assets/svg/menu.svg';
import deleteIcon from '@/assets/svg/delete.svg';
import ValidatedTextInput from '@/components/ui/validatedTextInput.vue';
export default {
components: { ValidatedTextInput, ToggleSwitch },
mixins: [InlineSettingMixin],
data () {
return {
icons: Object.freeze({
positive: positiveIcon,
menuIcon,
deleteIcon,
}),
unsaved: [],
};
},
computed: {
...mapState({
user: 'user.data',
credentials: 'credentials',
}),
},
methods: {
isValidUrl (url) {
return validator.isURL(url, {
require_tld: true,
require_protocol: true,
protocols: ['http', 'https'],
});
},
async newUnsavedWebhook () {
const webhookInfo = {
id: uuid(),
type: 'taskActivity',
options: {
created: false,
updated: false,
deleted: false,
scored: true,
},
url: '',
enabled: true,
};
this.unsaved.push(
this.user.webhooks.push(webhookInfo) - 1,
);
},
async saveWebhook (webhook, index) {
if (!this.isValidUrl(webhook.url)) {
return;
}
if (this.unsaved.includes(index)) {
const createdWebhook = await this.$store.dispatch('user:addWebhook', { webhook });
this.user.webhooks[index] = createdWebhook;
this.unsaved = this.unsaved.filter(u => u !== index);
} else {
const updatedWebhook = await this.$store.dispatch('user:updateWebhook', { webhook });
this.user.webhooks[index] = updatedWebhook;
}
},
async updateWebhookEnabled (webhook, index) {
if (this.unsaved.includes(index)) {
return;
}
const updatedWebhook = await this.$store.dispatch('user:updateWebhook', { webhook });
this.user.webhooks[index] = updatedWebhook;
},
async deleteWebhook (webhook, index) {
await this.$store.dispatch('user:deleteWebhook', { webhook });
this.user.webhooks.splice(index, 1);
},
},
};
</script>
+1 -1
View File
@@ -65,7 +65,7 @@ export async function sleep (store) {
}
export async function addWebhook (store, payload) {
const response = await axios.post('/api/v4/user/webhook', payload.webhookInfo);
const response = await axios.post('/api/v4/user/webhook', payload.webhook);
return response.data.data;
}
+2 -1
View File
@@ -167,9 +167,10 @@
"generate": "Generate",
"getCodes": "Get Codes",
"webhooks": "Webhooks",
"webhooksInfo": "Habitica provides webhooks so that when certain actions occur in your account, information can be sent to a script on another website. You can specify those scripts here. Be careful with this feature because specifying an incorrect URL can cause errors or slowness in Habitica. For more information, see the wiki's <a target=\"_blank\" href=\"https://habitica.fandom.com/wiki/Webhooks\">Webhooks</a> page.",
"webhooksInfo": "Webhooks provide a way for developers to receive notifications when a particular action is performed, such as scoring or updating a Task, or sending a message in a Group. By creating a webhook, you will be able to listen to changes in Habitica and build apps that respond to these changes.<br><br>For additional information and examples on webhooks, please visit our <a target=\"_blank\" href=\"https://habitica.fandom.com/wiki/Webhooks\">API Docs</a>",
"enabled": "Enabled",
"webhookURL": "Webhook URL",
"addWebhook": "Add Webhook",
"invalidUrl": "invalid url",
"invalidWebhookId": "the \"id\" parameter should be a valid UUID.",
"webhookBooleanOption": "\"<%= option %>\" must be a Boolean value.",