Merge branch 'beta'

This commit is contained in:
ThatOneCalculator 2023-03-14 16:27:12 -07:00
commit 33e7bb2fc5
No known key found for this signature in database
GPG key ID: 8703CACD01000000
5 changed files with 95 additions and 83 deletions

View file

@ -11,5 +11,4 @@ pipeline:
password:
# Secret 'docker_password' needs to be set in the CI settings
from_secret: docker_password
branches: beta

View file

@ -1,6 +1,6 @@
{
"name": "calckey",
"version": "13.1.3-rc",
"version": "13.1.3-rc2",
"codename": "aqua",
"repository": {
"type": "git",

View file

@ -4,73 +4,69 @@ import { Emojis } from "@/models/index.js";
import { toPunyNullable } from "./convert-host.js";
import { IsNull } from "typeorm";
const legacies: Record<string, string> = {
like: "👍",
love: "❤️", // ここに記述する場合は異体字セレクタを入れない <- not that good because modern browsers just display it as the red heart so just convert it to it to not end up with two seperate reactions of "the same emoji" for the user
laugh: "😆",
hmm: "🤔",
surprise: "😮",
congrats: "🎉",
angry: "💢",
confused: "😥",
rip: "😇",
pudding: "🍮",
star: "⭐",
};
const legacies = new Map([
['like', '👍'],
['love', '❤️'],
['laugh', '😆'],
['hmm', '🤔'],
['surprise', '😮'],
['congrats', '🎉'],
['angry', '💢'],
['confused', '😥'],
['rip', '😇'],
['pudding', '🍮'],
['star', '⭐'],
]);
export async function getFallbackReaction(): Promise<string> {
export async function getFallbackReaction() {
const meta = await fetchMeta();
return meta.defaultReaction;
}
export function convertLegacyReactions(reactions: Record<string, number>) {
const _reactions = {} as Record<string, number>;
const _reactions = new Map();
const decodedReactions = new Map();
for (const reaction of Object.keys(reactions)) {
for (const reaction in reactions) {
if (reactions[reaction] <= 0) continue;
if (Object.keys(legacies).includes(reaction)) {
if (_reactions[legacies[reaction]]) {
_reactions[legacies[reaction]] += reactions[reaction];
} else {
_reactions[legacies[reaction]] = reactions[reaction];
}
} else if (reaction === "♥️") {
if (_reactions["❤️"]) {
_reactions["❤️"] += reactions[reaction];
} else {
_reactions["❤️"] = reactions[reaction];
}
let decodedReaction;
if (decodedReactions.has(reaction)) {
decodedReaction = decodedReactions.get(reaction);
} else {
if (_reactions[reaction]) {
_reactions[reaction] += reactions[reaction];
} else {
_reactions[reaction] = reactions[reaction];
}
decodedReaction = decodeReaction(reaction);
decodedReactions.set(reaction, decodedReaction);
}
let emoji = legacies.get(decodedReaction.reaction);
if (emoji) {
_reactions.set(emoji, (_reactions.get(emoji) || 0) + reactions[reaction]);
} else {
_reactions.set(reaction, (_reactions.get(reaction) || 0) + reactions[reaction]);
}
}
const _reactions2 = {} as Record<string, number>;
for (const reaction of Object.keys(_reactions)) {
_reactions2[decodeReaction(reaction).reaction] = _reactions[reaction];
const _reactions2 = new Map();
for (const [reaction, count] of _reactions) {
const decodedReaction = decodedReactions.get(reaction);
_reactions2.set(decodedReaction.reaction, count);
}
return _reactions2;
return Object.fromEntries(_reactions2);
}
export async function toDbReaction(
reaction?: string | null,
reacterHost?: string | null,
): Promise<string> {
if (reaction == null) return await getFallbackReaction();
if (!reaction) return await getFallbackReaction();
reacterHost = toPunyNullable(reacterHost);
// Convert string-type reactions to unicode
if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
// Convert old heart to new
if (reaction === "♥️") return "❤️";
const emoji = legacies.get(reaction) || (reaction === "♥️" ? "❤️" : null);
if (emoji) return emoji;
// Allow unicode reactions
const match = emojiRegex.exec(reaction);
if (match) {
@ -82,7 +78,7 @@ export async function toDbReaction(
if (custom) {
const name = custom[1];
const emoji = await Emojis.findOneBy({
host: reacterHost ?? IsNull(),
host: reacterHost || IsNull(),
name,
});
@ -131,7 +127,7 @@ export function decodeReaction(str: string): DecodedReaction {
}
export function convertLegacyReaction(reaction: string): string {
reaction = decodeReaction(reaction).reaction;
if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
return reaction;
const decoded = decodeReaction(reaction).reaction;
if (legacies.has(decoded)) return legacies.get(decoded)!;
return decoded;
}

View file

@ -1,35 +1,35 @@
<template>
<div v-if="show" ref="el" class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick">
<div v-if="narrow" class="buttons left" @click="openAccountMenu">
<div v-if="show" ref="el" class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick">
<div v-if="narrow" class="buttons left" @click="openAccountMenu">
<MkAvatar v-if="props.displayMyAvatar && $i" class="avatar" :user="$i" :disable-preview="true"/>
</div>
<template v-if="metadata">
<div v-if="!hideTitle" class="titleContainer" @click="showTabsPopup">
<MkAvatar v-if="metadata.avatar" class="avatar" :user="metadata.avatar" :disable-preview="true" :show-indicator="true"/>
<i v-else-if="metadata.icon && !narrow" class="icon" :class="metadata.icon"></i>
<i v-else-if="metadata.icon && !narrow" class="icon" :class="metadata.icon"></i>
<div class="title">
<MkUserName v-if="metadata.userName" :user="metadata.userName" :nowrap="true" class="title"/>
<div v-else-if="metadata.title && !(tabs != null && tabs.length > 0 && narrow)" class="title">{{ metadata.title }}</div>
<div v-if="!narrow && metadata.subtitle" class="subtitle">
{{ metadata.subtitle }}
<div class="title">
<MkUserName v-if="metadata.userName" :user="metadata.userName" :nowrap="true" class="title"/>
<div v-else-if="metadata.title && !(tabs != null && tabs.length > 0 && narrow)" class="title">{{ metadata.title }}</div>
<div v-if="!narrow && metadata.subtitle" class="subtitle">
{{ metadata.subtitle }}
</div>
</div>
</div>
</div>
<div ref="tabsEl" v-if="hasTabs" class="tabs">
<button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = el" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)">
<i v-if="tab.icon" class="icon" :class="tab.icon"></i>
<span v-if="deviceKind !== 'desktop' || isTouchUsing || (!tab.iconOnly && !narrow)" class="title">{{ tab.title }}</span>
</button>
<div ref="tabHighlightEl" class="highlight"></div>
</div>
</template>
<div class="buttons right">
<template v-for="action in actions">
<button v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
<div ref="tabsEl" v-if="hasTabs" class="tabs">
<button v-for="tab in tabs" :ref="(el) => tabRefs[tab.key] = el" v-tooltip.noDelay="tab.title" class="tab _button" :class="{ active: tab.key != null && tab.key === props.tab }" @mousedown="(ev) => onTabMousedown(tab, ev)" @click="(ev) => onTabClick(tab, ev)">
<i v-if="tab.icon" class="icon" :class="tab.icon"></i>
<span class="title">{{ tab.title }}</span>
</button>
<div ref="tabHighlightEl" class="highlight"></div>
</div>
</template>
<div class="buttons right">
<template v-for="action in actions">
<button v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
</template>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
@ -37,10 +37,7 @@ import { computed, onMounted, onUnmounted, ref, inject, watch, shallowReactive,
import tinycolor from 'tinycolor2';
import { popupMenu } from '@/os';
import { scrollToTop } from '@/scripts/scroll';
import { i18n } from '@/i18n';
import { globalEvents } from '@/events';
import { deviceKind } from '@/scripts/device-kind';
import { isTouchUsing } from '@/scripts/touch';
import { injectPageMetadata } from '@/scripts/page-metadata';
import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
@ -153,12 +150,18 @@ onMounted(() => {
if (tabEl && tabHighlightEl) {
// offsetWidth offsetLeft getBoundingClientRect 使
// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
const parentRect = tabsEl.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect();
const left = (rect.left - parentRect.left + tabsEl?.scrollLeft);
tabHighlightEl.style.width = rect.width + 'px';
tabHighlightEl.style.left = left + 'px';
tabsEl.scrollTo({left: left - 80, behavior: "smooth"});
const tabSizeX = tabEl.scrollWidth + 20; // + the tab's padding
tabEl.style = `--width: ${tabSizeX}px`;
setTimeout(() => {
const parentRect = tabsEl.getBoundingClientRect();
const rect = tabEl.getBoundingClientRect();
const left = (rect.left - parentRect.left + tabsEl?.scrollLeft);
tabHighlightEl.style.width = tabSizeX + 'px';
tabHighlightEl.style.transform = `translateX(${left}px)`;
window.requestAnimationFrame(() => {
tabsEl?.scrollTo({left: left - 60, behavior: "smooth"});
})
}, 200);
}
});
}, {
@ -190,7 +193,6 @@ onUnmounted(() => {
-webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px));
border-bottom: solid 0.5px var(--divider);
contain: strict;
height: var(--height);
&.thin {
@ -235,7 +237,7 @@ onUnmounted(() => {
> .buttons {
--margin: 8px;
display: flex;
align-items: center;
align-items: center;
height: var(--height);
margin: 0 var(--margin);
@ -349,16 +351,20 @@ onUnmounted(() => {
font-size: 1em;
overflow-x: auto;
white-space: nowrap;
contain: strict;
> .tab {
display: inline-flex;
align-items: center;
position: relative;
padding: 0 10px;
border-inline: 10px solid transparent;
height: 100%;
font-weight: normal;
opacity: 0.7;
transition: color .2s, opacity .2s;
width: 38px;
--width: 38px;
overflow: hidden;
transition: color .2s, opacity .2s, width .2s;
&:hover {
opacity: 1;
@ -368,20 +374,29 @@ onUnmounted(() => {
opacity: 1;
color: var(--accent);
font-weight: 600;
width: var(--width);
}
&:not(.active) > .title {
opacity: 0;
}
> .icon + .title {
margin-left: 8px;
}
> .title {
transition: opacity .2s;
}
}
> .highlight {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background: var(--accent);
border-radius: 999px;
transition: all 0.2s ease;
transition: width .2s, transform .2s;
transition-timing-function: cubic-bezier(0,0,0,1.2);
pointer-events: none;
}
}

View file

@ -315,12 +315,14 @@ onMounted(() => {
top: calc(var(--stickyTop, 0px) + 16px);
z-index: 1000;
width: 100%;
pointer-events: none;
> button {
display: block;
margin: var(--margin) auto 0 auto;
padding: 8px 16px;
border-radius: 32px;
pointer-events: all;
}
}