All checks were successful
CI / container-job (push) Successful in 41s
- adonisrc.ts: Load official drive_provider and unload custom driver_provider. - packages.json: Add @headlessui/vue dependency for tab components. - AvatarController.ts: Rewrite avatar generation logic to always return the same avatar per user. - auth/UserController.ts: Add profile and profileUpdate methods to support user profile editing. - Submitter/datasetController.ts & app/models/file.ts: Adapt code to use the official drive_provider. - app/models/user.ts: Introduce “isAdmin” getter. - config/drive.ts: Create new configuration for the official drive_provider. - providers/vinejs_provider.ts: Adapt allowedExtensions control to use provided options or database enabled extensions. - resource/js/app.ts: Load default Head and Link components. - resources/js/menu.ts: Add settings-profile.edit menu point. - resources/js/Components/action-message.vue: Add new component for improved user feedback after form submissions. - New avatar-input.vue component: Enable profile picture selection. - Components/CardBox.vue: Alter layout to optionally show HeaderIcon in title bar. - FormControl.vue: Define a readonly prop for textareas. - Improve overall UI with updates to NavBar.vue, UserAvatar.vue, UserAvatarCurrentUser.vue, and add v-model support to password-meter.vue. - Remove profile editing logic from AccountInfo.vue and introduce new profile components (show.vue, update-password-form.vue, update-profile-information.vue). - app.edge: Modify page (add @inertiaHead tag) for better meta management. - routes.ts: Add new routes for editing user profiles. - General npm updates.
85 lines
2.2 KiB
Vue
85 lines
2.2 KiB
Vue
<script setup>
|
|
import { computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
username: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
defaultUrl: {
|
|
type: String,
|
|
required: false
|
|
},
|
|
api: {
|
|
type: String,
|
|
default: 'avataaars',
|
|
},
|
|
});
|
|
|
|
const avatar = computed(() => {
|
|
return props.defaultUrl ?? generateAvatarUrl(props.username);
|
|
});
|
|
|
|
const username = computed(() => props.username);
|
|
|
|
const darkenColor = (color) => {
|
|
const r = parseInt(color.slice(0, 2), 16);
|
|
const g = parseInt(color.slice(2, 4), 16);
|
|
const b = parseInt(color.slice(4, 6), 16);
|
|
|
|
const darkerR = Math.round(r * 0.6);
|
|
const darkerG = Math.round(g * 0.6);
|
|
const darkerB = Math.round(b * 0.6);
|
|
|
|
const darkerColor = ((darkerR << 16) + (darkerG << 8) + darkerB).toString(16);
|
|
|
|
return darkerColor.padStart(6, '0');
|
|
};
|
|
|
|
const getColorFromName = (name) => {
|
|
let hash = 0;
|
|
for (let i = 0; i < name.length; i++) {
|
|
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
}
|
|
let color = '#';
|
|
for (let i = 0; i < 3; i++) {
|
|
const value = (hash >> (i * 8)) & 0xff;
|
|
color += ('00' + value.toString(16)).substr(-2);
|
|
}
|
|
return color.replace('#', '');
|
|
};
|
|
|
|
const lightenColor = (hexColor, percent) => {
|
|
let r = parseInt(hexColor.substring(0, 2), 16);
|
|
let g = parseInt(hexColor.substring(2, 4), 16);
|
|
let b = parseInt(hexColor.substring(4, 6), 16);
|
|
|
|
r = Math.floor(r * (100 + percent) / 100);
|
|
g = Math.floor(g * (100 + percent) / 100);
|
|
b = Math.floor(b * (100 + percent) / 100);
|
|
|
|
r = (r < 255) ? r : 255;
|
|
g = (g < 255) ? g : 255;
|
|
b = (b < 255) ? b : 255;
|
|
|
|
const lighterHex = ((r << 16) | (g << 8) | b).toString(16);
|
|
|
|
return lighterHex.padStart(6, '0');
|
|
};
|
|
|
|
const generateAvatarUrl = (name) => {
|
|
const originalColor = getColorFromName(name);
|
|
const backgroundColor = lightenColor(originalColor, 60);
|
|
const textColor = darkenColor(originalColor);
|
|
|
|
const avatarUrl = `/api/avatar?name=${name}&size=50`;
|
|
return avatarUrl;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<img :src="avatar" :alt="username"
|
|
class="rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-slate-800" />
|
|
</div>
|
|
</template>
|