Give a space with no icon a colored tile with its initials (#428)
This commit is contained in:
parent
3bd821efc1
commit
7b145c525d
7 changed files with 60 additions and 30 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import {hash, formatTimestampAsTime} from "@welshman/lib"
|
import {formatTimestampAsTime} from "@welshman/lib"
|
||||||
import type {Maybe} from "@welshman/lib"
|
import type {Maybe} from "@welshman/lib"
|
||||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {Thunks} from "@welshman/app"
|
import {Thunks} from "@welshman/app"
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte"
|
import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte"
|
||||||
import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte"
|
import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte"
|
||||||
import {app, profiles, user} from "@app/core"
|
import {app, profiles, user} from "@app/core"
|
||||||
import {colors} from "@app/theme"
|
import {colorFor} from "@app/theme"
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
const profileDisplay = $profiles.display(event.pubkey).$
|
const profileDisplay = $profiles.display(event.pubkey).$
|
||||||
const thunks = $app.use(Thunks).history
|
const thunks = $app.use(Thunks).history
|
||||||
const thunk = $app.use(Thunks).merge($thunks.filter(t => t.event.id === event.id))
|
const thunk = $app.use(Thunks).merge($thunks.filter(t => t.event.id === event.id))
|
||||||
const [_, colorValue] = colors[hash(event.pubkey) % colors.length]
|
const colorValue = colorFor(event.pubkey)
|
||||||
|
|
||||||
const reply = () => replyTo(event)
|
const reply = () => replyTo(event)
|
||||||
const edit = canEdit?.(event) ? () => onEdit?.(event) : undefined
|
const edit = canEdit?.(event) ? () => onEdit?.(event) : undefined
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Server from "@assets/icons/server.svg?dataurl"
|
|
||||||
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
||||||
import {relays} from "@app/core"
|
import {relays} from "@app/core"
|
||||||
|
import {relayIconColor, relayIconInitials} from "@app/relays"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
url: string
|
url: string
|
||||||
|
|
@ -12,10 +12,20 @@
|
||||||
const {url, size = 7, ...props}: Props = $props()
|
const {url, size = 7, ...props}: Props = $props()
|
||||||
|
|
||||||
const relay = $relays.one(url)
|
const relay = $relays.one(url)
|
||||||
|
|
||||||
|
const rem = $derived(size * 0.25)
|
||||||
|
|
||||||
|
const fallbackStyle = $derived(
|
||||||
|
`width: ${rem}rem; height: ${rem}rem; min-width: ${rem}rem; min-height: ${rem}rem; font-size: ${rem * 0.4}rem; background-color: ${relayIconColor(url)}; color: white`,
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $relay?.icon}
|
{#if $relay?.icon}
|
||||||
<ImageIcon {size} alt="" src={$relay?.icon} class="rounded-full {props.class}" />
|
<ImageIcon {size} alt="" src={$relay?.icon} class="rounded-full {props.class}" />
|
||||||
{:else}
|
{:else}
|
||||||
<ImageIcon size={size - 2} alt="" src={Server} class={props.class} />
|
<div
|
||||||
|
class="flex select-none items-center justify-center rounded-full font-bold {props.class}"
|
||||||
|
style={fallbackStyle}>
|
||||||
|
{relayIconInitials(url)}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div
|
<div
|
||||||
class="flex justify-center items-center h-12 w-12 min-w-12 rounded-full border-2 border-solid bg-surface-more border-line">
|
class="flex justify-center items-center h-12 w-12 min-w-12 rounded-full border-2 border-solid bg-surface-more border-line">
|
||||||
<RelayIcon {url} size={10} class="rounded-full" />
|
<RelayIcon {url} size={10} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if !hideFavorites && $userSpaceUrls.includes(url)}
|
{#if !hideFavorites && $userSpaceUrls.includes(url)}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import {readable} from "svelte/store"
|
import {readable} from "svelte/store"
|
||||||
import {
|
import {
|
||||||
hash,
|
|
||||||
gte,
|
gte,
|
||||||
now,
|
now,
|
||||||
uniq,
|
uniq,
|
||||||
|
|
@ -40,7 +39,7 @@
|
||||||
import RoomItemContent from "@app/components/RoomItemContent.svelte"
|
import RoomItemContent from "@app/components/RoomItemContent.svelte"
|
||||||
import {profiles, thunks, user} from "@app/core"
|
import {profiles, thunks, user} from "@app/core"
|
||||||
import {noThunks, thunksByEventId} from "@app/thunks"
|
import {noThunks, thunksByEventId} from "@app/thunks"
|
||||||
import {colors} from "@app/theme"
|
import {colorFor} from "@app/theme"
|
||||||
import {ENABLE_ZAPS} from "@app/env"
|
import {ENABLE_ZAPS} from "@app/env"
|
||||||
import type {FeedContext} from "@app/feeds"
|
import type {FeedContext} from "@app/feeds"
|
||||||
import {deriveEvent, deriveEventsForUrl} from "@app/repository"
|
import {deriveEvent, deriveEventsForUrl} from "@app/repository"
|
||||||
|
|
@ -71,7 +70,7 @@
|
||||||
const today = formatTimestampAsDate(now())
|
const today = formatTimestampAsDate(now())
|
||||||
const profileDisplay = $profiles.display(event.pubkey, [url]).$
|
const profileDisplay = $profiles.display(event.pubkey, [url]).$
|
||||||
const thunk = $derived($thunks.merge($thunksByEventId.get(event.id) ?? noThunks))
|
const thunk = $derived($thunks.merge($thunksByEventId.get(event.id) ?? noThunks))
|
||||||
const [_, colorValue] = colors[hash(event.pubkey) % colors.length]
|
const colorValue = colorFor(event.pubkey)
|
||||||
|
|
||||||
const qTag = matchTag(tagSpec("q"), event.tags)
|
const qTag = matchTag(tagSpec("q"), event.tags)
|
||||||
const isQuoteOnly = Boolean(
|
const isQuoteOnly = Boolean(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {throttled} from "@welshman/store"
|
||||||
import {checkRelayHasLivekit} from "$lib/livekit"
|
import {checkRelayHasLivekit} from "$lib/livekit"
|
||||||
import {app} from "@app/core"
|
import {app} from "@app/core"
|
||||||
import {relaysMostlyRestricted} from "@app/policies"
|
import {relaysMostlyRestricted} from "@app/policies"
|
||||||
|
import {colorFor} from "@app/theme"
|
||||||
|
|
||||||
export const encodeRelay = (url: string) =>
|
export const encodeRelay = (url: string) =>
|
||||||
encodeURIComponent(
|
encodeURIComponent(
|
||||||
|
|
@ -16,6 +17,23 @@ export const encodeRelay = (url: string) =>
|
||||||
|
|
||||||
export const decodeRelay = (url: string) => normalizeRelayUrl(decodeURIComponent(url))
|
export const decodeRelay = (url: string) => normalizeRelayUrl(decodeURIComponent(url))
|
||||||
|
|
||||||
|
const relayIconHost = (url: string) =>
|
||||||
|
normalizeRelayUrl(url)
|
||||||
|
.replace(/^wss?:\/\//, "")
|
||||||
|
.replace(/[:/].*$/, "")
|
||||||
|
|
||||||
|
// Darkened so white initials clear 4.5:1 on every color in the palette.
|
||||||
|
export const relayIconColor = (url: string) =>
|
||||||
|
`color-mix(in oklab, ${colorFor(relayIconHost(url))} 80%, black)`
|
||||||
|
|
||||||
|
export const relayIconInitials = (url: string) => {
|
||||||
|
const parts = relayIconHost(url).split(".")
|
||||||
|
const hasSubdomain = parts.length > 2
|
||||||
|
const [first = "", second = ""] = parts
|
||||||
|
|
||||||
|
return (hasSubdomain ? first.slice(0, 1) + second.slice(0, 1) : first.slice(0, 2)).toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
export const deriveSocket = (url: string) => {
|
export const deriveSocket = (url: string) => {
|
||||||
const socket = app.get().pool.get(url)
|
const socket = app.get().pool.get(url)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,34 @@
|
||||||
import twColors from "tailwindcss/colors"
|
import twColors from "tailwindcss/colors"
|
||||||
import {derived, readable} from "svelte/store"
|
import {derived, readable} from "svelte/store"
|
||||||
|
import {hash} from "@welshman/lib"
|
||||||
import {synced} from "@welshman/store"
|
import {synced} from "@welshman/store"
|
||||||
import {FL_THEME} from "@app/env"
|
import {FL_THEME} from "@app/env"
|
||||||
import {kv} from "@app/storage"
|
import {kv} from "@app/storage"
|
||||||
|
|
||||||
export const colors = [
|
const colors = [
|
||||||
["amber", twColors.amber[600]],
|
twColors.amber[600],
|
||||||
["blue", twColors.blue[600]],
|
twColors.blue[600],
|
||||||
["cyan", twColors.cyan[600]],
|
twColors.cyan[600],
|
||||||
["emerald", twColors.emerald[600]],
|
twColors.emerald[600],
|
||||||
["fuchsia", twColors.fuchsia[600]],
|
twColors.fuchsia[600],
|
||||||
["green", twColors.green[600]],
|
twColors.green[600],
|
||||||
["indigo", twColors.indigo[600]],
|
twColors.indigo[600],
|
||||||
["sky", twColors.sky[600]],
|
twColors.sky[600],
|
||||||
["lime", twColors.lime[600]],
|
twColors.lime[600],
|
||||||
["orange", twColors.orange[600]],
|
twColors.orange[600],
|
||||||
["pink", twColors.pink[600]],
|
twColors.pink[600],
|
||||||
["purple", twColors.purple[600]],
|
twColors.purple[600],
|
||||||
["red", twColors.red[600]],
|
twColors.red[600],
|
||||||
["rose", twColors.rose[600]],
|
twColors.rose[600],
|
||||||
["sky", twColors.sky[600]],
|
twColors.sky[600],
|
||||||
["teal", twColors.teal[600]],
|
twColors.teal[600],
|
||||||
["violet", twColors.violet[600]],
|
twColors.violet[600],
|
||||||
["yellow", twColors.yellow[600]],
|
twColors.yellow[600],
|
||||||
["zinc", twColors.zinc[600]],
|
twColors.zinc[600],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const colorFor = (value: string) => colors[hash(value) % colors.length]
|
||||||
|
|
||||||
// Every theme with token values in lib/components/theme.css
|
// Every theme with token values in lib/components/theme.css
|
||||||
export const flThemes = ["clay", "flat", "navy"]
|
export const flThemes = ["clay", "flat", "navy"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="card flex flex-col gap-4">
|
<div class="card flex flex-col gap-4">
|
||||||
<div class="relative flex gap-4">
|
<div class="relative flex gap-4">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<RelayIcon {url} size={14} class="rounded-full" />
|
<RelayIcon {url} size={14} />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex min-w-0 flex-col">
|
<div class="flex min-w-0 flex-col">
|
||||||
<h1 class="truncate min-w-0 whitespace-nowrap">
|
<h1 class="truncate min-w-0 whitespace-nowrap">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue