PronounsPage/components/CircleListInput.vue
2025-01-16 22:24:08 +00:00

132 lines
4.3 KiB
Vue

<template>
<ListInput v-model="v" :prototype="prototype()" :group="group" :readonly="readonly" :maxitems="maxitems">
<template #default="s">
<input
v-model="s.val.username"
type="text"
class="form-control"
:placeholder="`@${$t('user.account.changeUsername.header').toLowerCase()}`"
required
@input="validateUser(s.val.username)"
@keyup="validateUser(s.val.username)"
>
<button v-if="s.val.username" :key="updateKey" type="button" :class="['btn', accountStatus(s.val.username).btn]" @click.prevent>
<Tooltip v-if="accountStatus(s.val.username).text" :text="$t(accountStatus(s.val.username).text)">
<Icon :v="accountStatus(s.val.username).icon" />
</Tooltip>
<Icon v-else :v="accountStatus(s.val.username).icon" />
</button>
<input
v-model="s.val.relationship"
type="text"
class="form-control"
:placeholder="$t('profile.circles.relationship')"
required
maxlength="64"
>
</template>
<template #validation="s">
<p v-if="validation(s.val)" class="small text-danger">
<Icon v="exclamation-triangle" />
<span class="ml-1">{{ $t(validation(s.val)) }}</span>
</p>
</template>
</ListInput>
</template>
<script>
// TODO remove duplication
const normalise = (s) => decodeURIComponent(s.trim().toLowerCase());
const normaliseWithLink = (s) => normalise(s.replace(/^@/, '').replace(new RegExp('^https://.*?/@'), ''));
export default {
props: {
modelValue: {},
group: {},
readonly: { type: Boolean },
maxitems: { default: null, type: Number },
},
emits: ['update:modelValue'],
data() {
const validateUserCache = {};
for (const connection of this.modelValue) {
validateUserCache[connection.username] = [connection.locale];
}
return {
v: this.modelValue,
validateUserHandle: undefined,
validateUserCache,
updateKey: 0,
};
},
watch: {
v() {
this.$emit('update:modelValue', this.v);
},
modelValue(v) {
this.v = v;
},
},
methods: {
prototype() {
return { username: '', relationship: '' };
},
validation(v) {
if (JSON.stringify(v) === JSON.stringify(this.prototype())) {
return null;
}
if (this.accountStatus(v.username).value === false) {
return 'profile.circles.validation.userNotFound';
}
if (!v.relationship) {
return 'profile.circles.validation.required';
}
return null;
},
validateUser(v) {
if (this.validateUserHandle) {
clearTimeout(this.validateUserHandle);
}
if (!v || Object.hasOwn(this.validateUserCache, v)) {
return;
}
this.validateUserHandle = setTimeout(async () => {
let res = await $fetch(`/api/profile/versions/${encodeURIComponent(normaliseWithLink(v))}`);
if (!Array.isArray(res)) {
console.error(res);
res = [];
}
this.validateUserCache[v] = res;
this.updateKey++;
}, 500);
},
accountStatus(v) {
if (this.validateUserCache[v] === undefined) {
return {
value: undefined,
btn: 'btn-secondary text-white border',
icon: 'spinner',
};
}
if (this.validateUserCache[v].length) {
return {
value: true,
btn: 'btn-outline-success',
icon: 'check-circle',
};
}
return {
value: false,
btn: 'btn-outline-danger',
icon: 'times-circle',
text: 'profile.circles.validation.userNotFound',
};
},
},
};
</script>