🎨 add match type label

This commit is contained in:
kawamataryo 2023-08-19 19:56:48 +09:00
parent 3bec59a08e
commit 160575e016
7 changed files with 138 additions and 48 deletions

View File

@ -1,19 +1,40 @@
import type { ProfileView } from "@atproto/api/dist/client/types/app/bsky/actor/defs" import type { ProfileView } from "@atproto/api/dist/client/types/app/bsky/actor/defs"
import { BSKY_USER_MATCH_TYPE } from "./constants"
export const isSimilarUser = (terms: string[], bskyProfile: ProfileView | undefined) => { export const isSimilarUser = (terms: string[], bskyProfile: ProfileView | undefined): {
if(!bskyProfile) { return false } isSimilar: boolean,
type: typeof BSKY_USER_MATCH_TYPE[keyof typeof BSKY_USER_MATCH_TYPE],
} => {
if (!bskyProfile) {
return {
isSimilar: false,
type: BSKY_USER_MATCH_TYPE.NONE,
}
}
return terms.some(term => { for (const term of terms) {
const lowerCaseName = term.toLocaleLowerCase() const lowerCaseName = term.toLocaleLowerCase()
if(lowerCaseName === bskyProfile?.handle.toLocaleLowerCase().replace("@", "").split('.')[0]) { if (lowerCaseName === bskyProfile?.handle.toLocaleLowerCase().replace("@", "").split('.')[0]) {
return true return {
isSimilar: true,
type: BSKY_USER_MATCH_TYPE.HANDLE,
} }
if(lowerCaseName === bskyProfile.displayName?.toLocaleLowerCase()) {
return true
} }
if(bskyProfile.description?.toLocaleLowerCase().includes(lowerCaseName)) { if (lowerCaseName === bskyProfile.displayName?.toLocaleLowerCase()) {
return true return {
isSimilar: true,
type: BSKY_USER_MATCH_TYPE.DISPLAY_NAME,
}
}
if (bskyProfile.description?.toLocaleLowerCase().includes(lowerCaseName)) {
return {
isSimilar: true,
type: BSKY_USER_MATCH_TYPE.DESCRIPTION,
}
}
}
return {
isSimilar: false,
type: BSKY_USER_MATCH_TYPE.NONE,
} }
return false
})
} }

View File

@ -1,8 +1,11 @@
import type { ProfileView, ViewerState } from "@atproto/api/dist/client/types/app/bsky/actor/defs" import type { ProfileView, ViewerState } from "@atproto/api/dist/client/types/app/bsky/actor/defs"
import { P, match } from "ts-pattern"
import van from 'vanjs-core' import van from 'vanjs-core'
import { BSKY_USER_MATCH_TYPE } from "~lib/constants"
const { a, div, p, img, button, span } = van.tags const { a, div, p, img, button, span } = van.tags
const { svg, path } = van.tagsNS("http://www.w3.org/2000/svg")
export type UserCellBtnLabel = { export type UserCellBtnLabel = {
add: string, add: string,
@ -79,20 +82,48 @@ const Avatar = ({ avatar }: { avatar?: string }) => {
return avatar ? img({ src: avatar, width: "40" }) : div({ class: "no-avatar" }) return avatar ? img({ src: avatar, width: "40" }) : div({ class: "no-avatar" })
} }
const MatchTypeLabel = ({ matchType }: { matchType: typeof BSKY_USER_MATCH_TYPE[keyof typeof BSKY_USER_MATCH_TYPE] }) => {
const [text, labelClass] = match(matchType)
.with(
BSKY_USER_MATCH_TYPE.HANDLE,
() => ["Same handle", "match-type__handle"]
)
.with(
BSKY_USER_MATCH_TYPE.DISPLAY_NAME,
() => ["Same display name", "match-type__display-name"]
)
.with(
BSKY_USER_MATCH_TYPE.DESCRIPTION,
() => ["Included handle or display name in description", "match-type__description"]
)
.run()
return div({ class: `match-type ${labelClass}` },
svg({ fill: "none", width: "12", viewBox: "0 0 24 24", "stroke-width": "3", stroke: "currentColor", class: "w-6 h-6" },
path({ "stroke-linecap": "round", "stroke-linejoin": "round", "d": "M4.5 12.75l6 6 9-13.5" }),
),
text
)
}
export const BskyUserCell = ({ export const BskyUserCell = ({
profile, profile,
statusKey, statusKey,
btnLabel, btnLabel,
matchType,
addAction, addAction,
removeAction, removeAction,
}: { }: {
profile: ProfileView, profile: ProfileView,
statusKey: keyof ViewerState, statusKey: keyof ViewerState,
btnLabel: UserCellBtnLabel, btnLabel: UserCellBtnLabel,
matchType: typeof BSKY_USER_MATCH_TYPE[keyof typeof BSKY_USER_MATCH_TYPE],
addAction: () => Promise<void>, addAction: () => Promise<void>,
removeAction: () => Promise<void> removeAction: () => Promise<void>
}) => { }) => {
return div({ class: "bsky-user-content" }, return div({ class: "bsky-user-content-wrapper" },
MatchTypeLabel({ matchType }),
div({ class: "bsky-user-content" },
div({ class: "icon-section" }, div({ class: "icon-section" },
a({ href: `https://bsky.app/profile/${profile.handle}`, target: "_blank", rel: "noopener" }, a({ href: `https://bsky.app/profile/${profile.handle}`, target: "_blank", rel: "noopener" },
Avatar({ avatar: profile.avatar }), Avatar({ avatar: profile.avatar }),
@ -122,5 +153,5 @@ export const BskyUserCell = ({
), ),
profile.description ? p({ class: "description" }, profile.description) : "", profile.description ? p({ class: "description" }, profile.description) : "",
), ),
) ))
} }

View File

@ -19,11 +19,12 @@ const WarningIcon = () => svg(
}), }),
) )
export const NotFoundCell = () => div({ class: "bsky-user-content bsky-user-content__not-found" }, export const NotFoundCell = () => div({ class: "bsky-user-content-wrapper" },
div({ class: "bsky-user-content bsky-user-content__not-found" },
WarningIcon(), WarningIcon(),
p({ p({
class: "not-found" class: "not-found"
}, },
"No similar users found." "No similar users found."
) )
) ))

View File

@ -23,3 +23,11 @@ export const VIEWER_STATE = {
BLOCKING: "blocking", BLOCKING: "blocking",
FOLLOWING: "following", FOLLOWING: "following",
} as const } as const
export const BSKY_USER_MATCH_TYPE = {
HANDLE: "handle",
DISPLAY_NAME: "display_name",
DESCRIPTION: "description",
NONE: "none",
} as const

View File

@ -3,6 +3,7 @@ import van from "vanjs-core"
import { ReloadButton } from "./components/ReloadBtn" import { ReloadButton } from "./components/ReloadBtn"
import { NotFoundCell } from "./components/NotFoundCell" import { NotFoundCell } from "./components/NotFoundCell"
import { BskyUserCell, type UserCellBtnLabel } from "./components/BskyUserCell" import { BskyUserCell, type UserCellBtnLabel } from "./components/BskyUserCell"
import type { BSKY_USER_MATCH_TYPE } from "./constants"
export const getUserCells = ({ queryParam, filterInsertedElement }: { queryParam: string, filterInsertedElement: boolean }) => { export const getUserCells = ({ queryParam, filterInsertedElement }: { queryParam: string, filterInsertedElement: boolean }) => {
const userCells = document.querySelectorAll(queryParam); const userCells = document.querySelectorAll(queryParam);
@ -12,7 +13,7 @@ export const getUserCells = ({ queryParam, filterInsertedElement }: { queryParam
return Array.from(userCells).filter((userCell) => { return Array.from(userCells).filter((userCell) => {
const nextElement = userCell.nextElementSibling const nextElement = userCell.nextElementSibling
if (!nextElement) { return true } if (!nextElement) { return true }
return nextElement.classList.contains("bsky-user-content") === false return nextElement.classList.contains("bsky-user-content-wrapper") === false
}) })
} else { } else {
return Array.from(userCells) return Array.from(userCells)
@ -32,11 +33,12 @@ export const getAccountNameAndDisplayName = (userCell: Element) => {
return { twAccountName, twDisplayName, twAccountNameRemoveUnderscore } return { twAccountName, twDisplayName, twAccountNameRemoveUnderscore }
} }
export const insertBskyProfileEl = ({ dom, profile, statusKey, btnLabel, addAction, removeAction }: { export const insertBskyProfileEl = ({ dom, profile, statusKey, btnLabel, matchType, addAction, removeAction }: {
dom: Element, dom: Element,
profile: ProfileView, profile: ProfileView,
statusKey: keyof ViewerState, statusKey: keyof ViewerState,
btnLabel: UserCellBtnLabel, btnLabel: UserCellBtnLabel,
matchType: typeof BSKY_USER_MATCH_TYPE[keyof typeof BSKY_USER_MATCH_TYPE],
addAction: () => Promise<void>, addAction: () => Promise<void>,
removeAction: () => Promise<void> removeAction: () => Promise<void>
}) => { }) => {
@ -44,6 +46,7 @@ export const insertBskyProfileEl = ({ dom, profile, statusKey, btnLabel, addActi
profile, profile,
statusKey, statusKey,
btnLabel, btnLabel,
matchType,
addAction, addAction,
removeAction, removeAction,
})) }))

View File

@ -55,6 +55,7 @@ export const searchAndInsertBskyUsers = async (
] ]
let targetAccount = null let targetAccount = null
let matchType = null
// Loop over search parameters and break if a user is found // Loop over search parameters and break if a user is found
for (const term of searchTerms) { for (const term of searchTerms) {
@ -63,7 +64,7 @@ export const searchAndInsertBskyUsers = async (
limit: 1, limit: 1,
}) })
const isUserFound = isSimilarUser([ const { isSimilar: isUserFound, type } = isSimilarUser([
twAccountName, twAccountName,
twAccountNameRemoveUnderscore, twAccountNameRemoveUnderscore,
twDisplayName, twDisplayName,
@ -71,6 +72,7 @@ export const searchAndInsertBskyUsers = async (
if (isUserFound) { if (isUserFound) {
targetAccount = searchResult targetAccount = searchResult
matchType = type
break; // Stop searching when a user is found break; // Stop searching when a user is found
} }
} }
@ -82,6 +84,7 @@ export const searchAndInsertBskyUsers = async (
profile: targetAccount, profile: targetAccount,
statusKey, statusKey,
btnLabel, btnLabel,
matchType,
addAction: async () => { addAction: async () => {
const result = await addQuery(targetAccount.did); const result = await addQuery(targetAccount.did);
bskyUserUrlMap.set(targetAccount.did, result.uri) bskyUserUrlMap.set(targetAccount.did, result.uri)

View File

@ -6,6 +6,29 @@
--bsky-primary-hover-color: #2563eb; --bsky-primary-hover-color: #2563eb;
} }
.bsky-user-content-wrapper .match-type {
color: var(--bsky-primary-color);
padding: 2px 14px 2px 14px;
font-size: 10px;
font-weight: bold;
width: fit-content;
display: flex;
gap: 4px;
border-radius: 10px 10px 0px 0px;
}
.bsky-user-content-wrapper .match-type.match-type__handle {
background-color: #ffd700;
}
.bsky-user-content-wrapper .match-type.match-type__display-name {
background-color: #FFA07A;
}
.bsky-user-content-wrapper .match-type.match-type__description {
background-color: #D3D3D3;
}
.bsky-user-content { .bsky-user-content {
background: rgb(2,0,36); background: rgb(2,0,36);
background: var(--bsky-primary-color); background: var(--bsky-primary-color);