Compare commits

...

5 commits

Author SHA1 Message Date
7bc9858736
fix: friend icon sizes
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
ci/woodpecker/pr/build-and-publish Pipeline was successful
2025-07-19 23:52:53 +02:00
a2714cfae7
actually fix avatar sizing 2025-07-19 23:51:28 +02:00
c7ef63d2bd
feat: fix member list to use the new default avatar 2025-07-19 23:46:37 +02:00
ed38340249
feat: implement new guild icons 2025-07-19 23:39:39 +02:00
25501147ae
chore: remove old icon generation 2025-07-19 23:39:16 +02:00
9 changed files with 73 additions and 113 deletions

View file

@ -3,8 +3,10 @@
class="display-avatar"
:src="displayAvatar"
:alt="displayName" />
<Icon v-else
name="lucide:user"
<DefaultIcon v-else-if="user"
class="display-avatar"
:name="displayName"
:seed="user.uuid"
:alt="displayName" />
</template>
@ -28,10 +30,6 @@ if (user) {
if (user.avatar) {
displayAvatar = user.avatar
} else if (!isCanvasBlocked()){
displayAvatar = generateDefaultIcon(displayName, user.uuid)
} else {
displayAvatar = null
}
}

View file

@ -0,0 +1,48 @@
<template>
<div :style="`background-color: ${generateIrcColor(seed, 50)}`"
class="default-icon">
<span class="default-icon-text">
{{ previewName }}
</span>
</div>
</template>
<script lang="ts" setup>
const props = defineProps<{
seed: string,
name: string
}>();
let previewName = "";
if (props.name.length > 3) {
let guildName: string[] = props.name.split(' ')
for (let i = 0; i < 3; i ++) {
if (guildName.length > i) {
previewName += guildName[i].charAt(0)
} else {
break
}
}
} else {
previewName = props.name
}
</script>
<style scoped>
.default-icon {
display: flex;
align-items: center;
justify-content: center;
}
.default-icon-text {
/* helps centre the icon, yes, this is NOT perfect */
margin-top: -0.15em;
font-weight: bold;
color: var(--secondary-text-color)
}
</style>

View file

@ -30,4 +30,11 @@ const hidePopup = () => {
.member-item {
position: relative; /* Set the position to relative for absolute positioning of the popup */
}
.member-avatar {
min-height: 2.3em;
max-height: 2.3em;
min-width: 2.3em;
max-width: 2.3em;
}
</style>

View file

@ -220,7 +220,10 @@ function getDayDifference(date1: Date, date2: Date) {
}
.message-author-avatar {
width: 100%;
min-height: 2em;
max-height: 2em;
min-width: 2em;
max-width: 2em;
}
.left-column {

View file

@ -36,7 +36,7 @@ const props = defineProps<{
.user-avatar {
min-width: 2.3em;
max-width: 2.3em;
min-width: 2.3em;
min-height: 2.3em;
max-height: 2.3em;
}

View file

@ -17,19 +17,15 @@
</div>
<VerticalSpacer />
<div class="left-column-segment" id="left-column-middle">
<NuxtLink v-for="guild of guilds" :href="`/servers/${guild.uuid}`">
<NuxtLink v-for="guild of guilds" :href="`/servers/${guild.uuid}`" id="guild-icon-container">
<NuxtImg v-if="guild.icon"
class="sidebar-icon guild-icon"
:alt="guild.name"
:src="guild.icon" />
<NuxtImg v-else-if="!blockedCanvas"
class="sidebar-icon guild-icon"
<DefaultIcon
class="sidebar-icon guild-icon"
:alt="guild.name"
:src="generateDefaultIcon(guild.name, guild.uuid)" />
<Icon v-else name="lucide:server"
:style="`color: ${generateIrcColor(guild.uuid, 50)}`"
class="sidebar-icon guild-icon"
:alt="guild.name" />
:name="guild.name" :seed="guild.uuid"/>
</NuxtLink>
</div>
<VerticalSpacer />
@ -52,6 +48,7 @@
<script lang="ts" setup>
import { ModalBase } from '#components';
import { render } from 'vue';
import DefaultIcon from '~/components/DefaultIcon.vue';
import GuildDropdown from '~/components/Guild/GuildDropdown.vue';
import Button from '~/components/UserInterface/Button.vue';
import VerticalSpacer from '~/components/UserInterface/VerticalSpacer.vue';
@ -63,8 +60,6 @@ const createButtonContainer = ref<HTMLButtonElement>();
const api = useApi();
const blockedCanvas = isCanvasBlocked()
const options = [
{ name: "Join", value: "join", callback: async () => {
console.log("join guild!");
@ -249,6 +244,10 @@ function createDropdown() {
height: var(--sidebar-icon-width);
}
#guild-icon-container {
text-decoration: none;
}
.guild-icon {
border-radius: var(--guild-icon-radius);
}

View file

@ -133,13 +133,6 @@ function handleMemberClick(member: GuildMemberResponse) {
text-overflow: ellipsis;
}
.member-avatar {
min-width: 2.3em;
max-width: 2.3em;
min-width: 2.3em;
max-height: 2.3em;
}
.member-display-name {
overflow: hidden;
text-overflow: ellipsis;

View file

@ -1,38 +0,0 @@
export default (name: string, seed: string): string => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (canvas && ctx) {
canvas.width = 256;
canvas.height = 256;
// get the first char from every word in the guild name
let previewName = "";
if (name.length > 3) {
let guildName: string[] = name.split(' ')
for (let i = 0; i < 3; i ++) {
if (guildName.length > i) {
previewName += guildName[i].charAt(0)
} else {
break
}
}
} else {
previewName = name
}
// fill background using seeded colour
ctx.fillStyle = generateIrcColor(seed, 50)
ctx.fillRect(0, 0, 256, 256)
ctx.fillStyle = 'white'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.font = `bold 96px Arial, Helvetica, sans-serif`
// 136 isn't actually centered, but it *looks* centered
ctx.fillText(previewName, 128, 136)
return canvas.toDataURL("image/png");
}
return "https://tenor.com/view/dame-da-ne-guy-kiryukazuma-kiryu-yakuza-yakuza-0-gif-14355451116903905918"
}

View file

@ -1,50 +0,0 @@
//
// Canvas Blocker &
// Firefox privacy.resistFingerprinting Detector.
// (c) 2018 // JOHN OZBAY // CRYPT.EE
// MIT License
//
export default () => {
// create a 1px image data
var blocked = false;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
// some blockers just return an undefined ctx. So let's check that first.
if (ctx) {
var imageData = ctx.createImageData(1,1);
var originalImageData = imageData.data;
// set pixels to RGB 128
originalImageData[0]=128;
originalImageData[1]=128;
originalImageData[2]=128;
originalImageData[3]=255;
// set this to canvas
ctx.putImageData(imageData,1,1);
try {
// now get the data back from canvas.
var checkData = ctx.getImageData(1, 1, 1, 1).data;
// If this is firefox, and privacy.resistFingerprinting is enabled,
// OR a browser extension blocking the canvas,
// This will return RGB all white (255,255,255) instead of the (128,128,128) we put.
// so let's check the R and G to see if they're 255 or 128 (matching what we've initially set)
if (originalImageData[0] !== checkData[0] && originalImageData[1] !== checkData[1]) {
blocked = true;
console.log("Canvas is blocked. Will display warning.");
}
} catch (error) {
// some extensions will return getImageData null. this is to account for that.
blocked = true;
console.log("Canvas is blocked. Will display warning.");
}
} else {
blocked = true;
console.log("Canvas is blocked. Will display warning.");
}
return blocked;
}