diff --git a/.woodpecker/build-and-publish.yml b/.woodpecker/build-and-publish.yml index 6001a00..e62bd74 100644 --- a/.woodpecker/build-and-publish.yml +++ b/.woodpecker/build-and-publish.yml @@ -8,7 +8,6 @@ steps: - pnpm build when: - event: push - - event: pull_request - name: container-build-and-publish image: docker diff --git a/components/Avatar.vue b/components/Avatar.vue index 2ad2728..ddfe1dc 100644 --- a/components/Avatar.vue +++ b/components/Avatar.vue @@ -3,10 +3,8 @@ class="display-avatar" :src="displayAvatar" :alt="displayName" /> - @@ -30,6 +28,10 @@ if (user) { if (user.avatar) { displayAvatar = user.avatar + } else if (!isCanvasBlocked()){ + displayAvatar = generateDefaultIcon(displayName, user.uuid) + } else { + displayAvatar = null } } diff --git a/components/DefaultIcon.vue b/components/DefaultIcon.vue deleted file mode 100644 index 17e6c79..0000000 --- a/components/DefaultIcon.vue +++ /dev/null @@ -1,48 +0,0 @@ - - - - - diff --git a/components/Guild/MemberEntry.vue b/components/Guild/MemberEntry.vue index 705ba0e..12e36a0 100644 --- a/components/Guild/MemberEntry.vue +++ b/components/Guild/MemberEntry.vue @@ -30,11 +30,4 @@ 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; -} diff --git a/components/Message.vue b/components/Message.vue index 542b34d..ed32c50 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -220,10 +220,7 @@ function getDayDifference(date1: Date, date2: Date) { } .message-author-avatar { - min-height: 2em; - max-height: 2em; - min-width: 2em; - max-width: 2em; + width: 100%; } .left-column { diff --git a/components/Popups/CropPopup.vue b/components/Popups/CropPopup.vue index 12c3a0b..3a9586c 100644 --- a/components/Popups/CropPopup.vue +++ b/components/Popups/CropPopup.vue @@ -10,6 +10,7 @@ + + \ No newline at end of file diff --git a/pages/servers/[serverId]/channels/[channelId].vue b/pages/servers/[serverId]/channels/[channelId].vue index bbe359e..00d4ebe 100644 --- a/pages/servers/[serverId]/channels/[channelId].vue +++ b/pages/servers/[serverId]/channels/[channelId].vue @@ -133,6 +133,13 @@ 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; diff --git a/utils/generateDefaultIcon.ts b/utils/generateDefaultIcon.ts new file mode 100644 index 0000000..1cc8d43 --- /dev/null +++ b/utils/generateDefaultIcon.ts @@ -0,0 +1,38 @@ +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" +} \ No newline at end of file diff --git a/utils/isCanvasBlocked.ts b/utils/isCanvasBlocked.ts new file mode 100644 index 0000000..3bd191e --- /dev/null +++ b/utils/isCanvasBlocked.ts @@ -0,0 +1,50 @@ +// +// 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; +} \ No newline at end of file