feat(desktop): trending filters

This commit is contained in:
DIYgod 2025-05-03 18:09:11 +08:00
parent eb6a40954f
commit 58ce9f614a
No known key found for this signature in database
7 changed files with 168 additions and 390 deletions

View File

@ -2,7 +2,6 @@ import { isMobile } from "@follow/components/hooks/useMobile.js"
import { EmptyIcon } from "@follow/components/icons/empty.js"
import { ActionButton } from "@follow/components/ui/button/action-button.js"
import { Card, CardContent } from "@follow/components/ui/card/index.jsx"
import { LoadingCircle } from "@follow/components/ui/loading/index.js"
import { ScrollArea } from "@follow/components/ui/scroll-area/ScrollArea.js"
import { ResponsiveSelect } from "@follow/components/ui/select/responsive.js"
import { CategoryMap, RSSHubCategories } from "@follow/constants"
@ -20,7 +19,6 @@ import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/ho
import { useAuthQuery } from "~/hooks/common"
import { Queries } from "~/queries"
import { TrendingButton } from "../trending"
import { RecommendationCard } from "./recommendations-card"
const LanguageOptions = [
@ -69,7 +67,7 @@ export function Recommendations() {
firstLoad = false
const { data, isLoading, isFetching } = rsshubPopular
const { data, isLoading } = rsshubPopular
const keys = useMemo(() => {
if (!data) {
@ -143,10 +141,14 @@ export function Recommendations() {
}
return (
<div className="mt-12 w-full max-w-[800px]">
<div className="center relative flex">
<div className="absolute bottom-0 right-0 flex items-center gap-2">
<span className="text-headline text-text font-medium">Preferred language:</span>
<div className="mt-12 w-full max-w-[800px] space-y-6">
<div className="flex items-center justify-center gap-2 text-center text-xl font-bold">
<i className="i-mgc-grid-2-cute-re text-xl" />
<span>{t("words.categories")}</span>
</div>
<div className="center flex justify-center">
<div className="flex items-center gap-2">
<span className="text-headline text-text font-medium">Language:</span>
<ResponsiveSelect
value={selectedLang}
onValueChange={handleLangChange}
@ -154,20 +156,10 @@ export function Recommendations() {
size="sm"
items={LanguageOptions as any}
/>
<TrendingButton language={selectedLang} />
</div>
{isFetching && (
<div className="pointer-events-none absolute inset-x-0 top-0 flex items-center justify-center gap-8">
<span className="opacity-0" aria-hidden>
{t("discover.popular")}
</span>
<LoadingCircle size="small" className="center flex" />
</div>
)}
</div>
<div className="mt-4 grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4">
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4">
{RSSHubCategories.map((cat) => (
<Card
key={cat}

View File

@ -1,57 +0,0 @@
import type { FeedModel } from "@follow/models"
import { cn } from "@follow/utils/utils"
import { useQuery } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { apiFetch } from "~/lib/api-fetch"
import { FeedCard } from "./feed-card"
export function Trending() {
const { t } = useTranslation()
const { data } = useQuery({
queryKey: ["trending", "eng"],
queryFn: async () => {
return await apiFetch<{
data: {
feed: FeedModel
}[]
}>("/trending/feeds", {
method: "GET",
params: {
// language: "en",
// view: 0,
limit: 20,
},
})
},
})
return (
<div className="mt-4 w-full max-w-[800px]">
<div className="flex items-center justify-center gap-2 text-center text-lg font-medium">
<i className="i-mingcute-trending-up-line text-2xl" />
<span>{t("words.trending")}</span>
</div>
<div className="mt-8 grid grid-cols-2 gap-6">
{data?.data?.map((item, index) => (
<FeedCard
key={item.feed.id}
item={item}
followButtonVariant="ghost"
followButtonClassName="border-accent text-accent px-3 -mr-3"
>
<div
className={cn(
"center absolute -left-5 -top-5 size-12 rounded-br-3xl pl-4 pt-4 text-xs",
index < 3 ? "bg-accent text-white" : "bg-zinc-100",
)}
>
{index + 1}
</div>
</FeedCard>
))}
</div>
</div>
)
}

View File

@ -1,330 +1,170 @@
import { isMobile, useMobile } from "@follow/components/hooks/useMobile.js"
import { PhUsersBold } from "@follow/components/icons/users.jsx"
import { Avatar, AvatarFallback, AvatarImage } from "@follow/components/ui/avatar/index.jsx"
import { ActionButton, Button } from "@follow/components/ui/button/index.js"
import { LoadingWithIcon } from "@follow/components/ui/loading/index.jsx"
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
import { EllipsisHorizontalTextWithTooltip } from "@follow/components/ui/typography/index.js"
import type { FeedModel, Models, UserModel } from "@follow/models"
import { stopPropagation } from "@follow/utils/dom"
import { ResponsiveSelect } from "@follow/components/ui/select/responsive.js"
import { Skeleton } from "@follow/components/ui/skeleton/index.jsx"
import { views } from "@follow/constants"
import type { FeedModel } from "@follow/models"
import { cn } from "@follow/utils/utils"
import { useQuery } from "@tanstack/react-query"
import type { FC } from "react"
import { useState } from "react"
import { useTranslation } from "react-i18next"
import { getTrendingAggregates } from "~/api/trending"
import { DrawerModalLayout } from "~/components/ui/modal/stacked/custom-modal"
import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/hooks"
import { useFollow } from "~/hooks/biz/useFollow"
import { UrlBuilder } from "~/lib/url-builder"
import { FeedIcon } from "~/modules/feed/feed-icon"
import { useGeneralSettingKey } from "~/atoms/settings/general"
import { apiFetch } from "~/lib/api-fetch"
import { usePresentUserProfileModal } from "../profile/hooks"
import { FeedCard } from "../discover/feed-card"
interface TrendingProps {
language: string
}
export const TrendingButton = ({ language, className }: TrendingProps & { className?: string }) => {
const { present } = useModalStack()
const LanguageOptions = [
{
label: "All",
value: "all",
},
{
label: "English",
value: "eng",
},
{
label: "中文",
value: "cmn",
},
]
type Language = (typeof LanguageOptions)[number]["value"]
const rangeOptions = [
{
label: "Today",
value: "1d",
},
{
label: "Three days",
value: "3d",
},
{
label: "This week",
value: "7d",
},
{
label: "This month",
value: "30d",
},
]
type Range = (typeof rangeOptions)[number]["value"]
const viewOptions = [
{
label: "All",
value: "all",
},
...views.map((view) => ({
label: view.name,
value: `${view.view}`,
})),
]
type View = (typeof viewOptions)[number]["value"]
export function Trending() {
const { t } = useTranslation()
return (
<Button
variant={"outline"}
onClick={() => {
present({
title: (
<div className="flex items-center gap-2">
<i className="i-mingcute-trending-up-line text-2xl" />
<span>{t("words.trending")}</span>
</div>
),
content: () => <TrendContent language={language} />,
CustomModalComponent: !isMobile() ? DrawerModalLayout : undefined,
})
}}
buttonClassName={cn("rounded px-2 h-8", className)}
>
<i className="i-mgc-trending-up-cute-re mr-1" />
{t("words.trending")}
</Button>
)
}
const TrendContent: FC<TrendingProps> = ({ language }) => {
const isMobile = useMobile()
const { t: tCommon } = useTranslation("common")
const lang = useGeneralSettingKey("language")
const { data } = useQuery({
queryKey: ["trending", language],
queryFn: () => {
return getTrendingAggregates({ language })
const [selectedLang, setSelectedLang] = useState<Language>(lang.startsWith("zh") ? "all" : "eng")
const [selectedRange, setSelectedRange] = useState<Range>("7d")
const [selectedView, setSelectedView] = useState<View>("all")
const { data, isLoading } = useQuery({
queryKey: ["trending", selectedLang, selectedView, selectedRange],
queryFn: async () => {
return await apiFetch<{
data: {
feed: FeedModel
}[]
}>("/trending/feeds", {
method: "GET",
params: {
language: selectedLang === "all" ? undefined : selectedLang,
view: selectedView === "all" ? undefined : Number(selectedView),
limit: 20,
},
})
},
})
const { t } = useTranslation()
const { dismiss } = useCurrentModal()
if (!data)
return (
<div className={isMobile ? "mx-auto" : "center absolute inset-0"}>
<LoadingWithIcon
icon={<i className="i-mingcute-trending-up-line text-3xl" />}
size="large"
/>
</div>
)
return (
<div className="flex size-full grow flex-col gap-4">
{!isMobile && (
<div className="-mt-4 flex w-full items-center justify-center gap-2 text-2xl">
<i className="i-mingcute-trending-up-line text-3xl" />
<span className="font-bold">{t("words.trending")}</span>
<div className="mt-4 w-full max-w-[800px] space-y-6">
<div className="flex items-center justify-center gap-2 text-center text-xl font-bold">
<i className="i-mgc-trending-up-cute-re text-xl" />
<span>{t("words.trending")}</span>
</div>
<div className="center flex justify-center gap-4">
<div className="flex items-center gap-2">
<span className="text-headline text-text font-medium">Language:</span>
<ResponsiveSelect
value={selectedLang}
onValueChange={(value) => {
setSelectedLang(value as Language)
}}
triggerClassName="w-32 h-8 rounded"
size="sm"
items={LanguageOptions}
/>
</div>
)}
<ActionButton
className="absolute right-4 top-4"
onClick={dismiss}
tooltip={t("words.close", { ns: "common" })}
>
<i className="i-mgc-close-cute-re" />
</ActionButton>
<ScrollArea.ScrollArea
rootClassName="flex h-0 w-[calc(100%+8px)] min-h-[50vh] grow flex-col overflow-visible"
viewportClassName="pb-4 [&>div]:!block"
scrollbarClassName="-mr-6"
>
<TrendingUsers data={data.trendingUsers} />
<TrendingLists data={data.trendingLists} />
<TrendingFeeds data={data.trendingFeeds} />
<TrendingEntries data={data.trendingEntries} />
</ScrollArea.ScrollArea>
<div className="flex items-center gap-2">
<span className="text-headline text-text font-medium">Range:</span>
<ResponsiveSelect
value={selectedRange}
onValueChange={(value: string) => {
setSelectedRange(value as Range)
}}
triggerClassName="w-32 h-8 rounded"
size="sm"
items={rangeOptions}
/>
</div>
<div className="flex items-center gap-2">
<span className="text-headline text-text font-medium">View:</span>
<ResponsiveSelect
value={selectedView}
onValueChange={(value: string) => {
setSelectedView(value as View)
}}
triggerClassName="w-32 h-8 rounded"
size="sm"
items={viewOptions}
renderItem={(item) => <>{tCommon(item.label as any)}</>}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-6">
{isLoading ? (
<>
<Skeleton className="h-[146px]" />
<Skeleton className="h-[146px]" />
<Skeleton className="h-[146px]" />
<Skeleton className="h-[146px]" />
<Skeleton className="h-[146px]" />
<Skeleton className="h-[146px]" />
</>
) : (
data?.data?.map((item, index) => (
<FeedCard
key={item.feed.id}
item={item}
followButtonVariant="ghost"
followButtonClassName="border-accent text-accent px-3 -mr-3"
>
<div
className={cn(
"center absolute -left-5 -top-5 size-12 rounded-br-3xl pl-4 pt-4 text-xs",
index < 3 ? "bg-accent text-white" : "bg-zinc-100",
)}
>
{index + 1}
</div>
</FeedCard>
))
)}
</div>
</div>
)
}
const TrendingLists: FC<{
data: Models.TrendingList[]
}> = ({ data }) => {
const follow = useFollow()
const { t } = useTranslation()
return (
<section className="mt-8 w-full text-left">
<h2 className="my-2 text-xl font-bold">{t("trending.list")}</h2>
<ul className="mt-4 flex flex-col gap-3 pb-4">
{data.map((item) => (
<li key={item.id}>
<button
type="button"
className={"group relative flex w-full min-w-0 items-center pl-2"}
onClick={() => {
follow({ isList: true, id: item.id })
}}
>
<div className="group-hover:bg-theme-item-hover absolute -inset-y-1 inset-x-0 z-[-1] rounded-lg duration-200" />
<FeedIcon feed={item as any} size={40} />
<div className={cn("ml-1 flex w-full flex-col text-left")}>
<div className="flex items-end gap-2">
<div className={cn("truncate text-base font-medium")}>{item.title}</div>
{!!item.subscriberCount && <UserCount count={item.subscriberCount} />}
</div>
{!!item.description && (
<div className={"line-clamp-2 text-xs"}>{item.description}</div>
)}
</div>
</button>
</li>
))}
</ul>
</section>
)
}
const UserCount: Component<{ count: number }> = ({ count, className }) => {
return (
<span className={cn("flex items-center gap-0.5 text-xs tabular-nums opacity-60", className)}>
<PhUsersBold className="size-3" />
<span>{count}</span>
</span>
)
}
interface TopUserAvatarProps {
user: UserModel
position: string
}
const TopUserAvatar: React.FC<TopUserAvatarProps> = ({ user, position }) => (
<div className={`absolute ${position} group flex w-[50px] flex-col`}>
<div className="group-hover:bg-theme-item-hover absolute -inset-x-4 -inset-y-2 rounded-lg duration-200" />
<Avatar className="border-border ring-background block aspect-square size-[50px] overflow-hidden rounded-full border ring-1">
<AvatarImage src={user?.image || undefined} />
<AvatarFallback>{user.name?.slice(0, 2)}</AvatarFallback>
</Avatar>
{user.name && (
<EllipsisHorizontalTextWithTooltip className="mt-2 text-xs font-medium">
<span>{user.name}</span>
</EllipsisHorizontalTextWithTooltip>
)}
</div>
)
const TrendingUsers: FC<{ data: UserModel[] }> = ({ data }) => {
const profile = usePresentUserProfileModal("dialog")
const { t } = useTranslation()
return (
<section className="w-full text-left">
<h2 className="my-2 text-xl font-bold">{t("trending.user")}</h2>
<div className="relative h-[100px]">
<div className="text-accent absolute left-[calc(50%+15px)] top-[8px] rotate-45 text-[20px]">
<i className="i-mgc-vip-2-cute-fi" />
</div>
<div className="text-accent/80 absolute left-[calc(33%+15px)] top-[calc(theme(spacing.3))] rotate-45 text-[20px]">
<i className="i-mgc-vip-2-cute-re" />
</div>
{data.slice(0, 3).map((user, index: number) => (
<button
onFocusCapture={stopPropagation}
type="button"
onClick={() => {
profile(user.id)
}}
key={user.id}
>
<TopUserAvatar
user={user}
position={
index === 0
? "left-1/2 -translate-x-1/2"
: index === 1
? "left-1/3 top-6 -translate-x-1/2"
: "left-2/3 top-6 -translate-x-1/2"
}
/>
</button>
))}
</div>
{data.length > 3 && (
<ul className="mt-8 flex flex-col gap-4 pl-2">
{data.slice(3).map((user) => (
<li key={user.id}>
<button
onFocusCapture={stopPropagation}
className="group relative flex w-full items-center gap-3"
type="button"
onClick={() => {
profile(user.id)
}}
>
<div className="group-hover:bg-theme-item-hover absolute -inset-2 right-0 z-[-1] rounded-lg duration-200" />
<Avatar className="border-border ring-background block aspect-square size-[40px] overflow-hidden rounded-full border ring-1">
<AvatarImage src={user?.image || undefined} />
<AvatarFallback>{user.name?.slice(0, 2)}</AvatarFallback>
</Avatar>
<span className="font-medium">{user.name}</span>
</button>
</li>
))}
</ul>
)}
</section>
)
}
const TrendingFeeds = ({ data }: { data: FeedModel[] }) => {
const follow = useFollow()
const { t } = useTranslation()
return (
<section className="mt-8 w-full text-left">
<h2 className="my-2 text-xl font-bold">{t("trending.feed")}</h2>
<ul className="mt-2 flex flex-col">
{data.map((feed) => {
return (
<li
className={cn(
"hover:bg-theme-item-hover group flex w-full items-center gap-1 rounded-md py-0.5 pl-2 duration-200",
"relative",
)}
key={feed.id}
>
<a
target="_blank"
href={UrlBuilder.shareFeed(feed.id)}
className="flex grow items-center gap-1 py-1"
>
<FeedIcon feed={feed} size={24} className="rounded" />
<div className="flex w-full min-w-0 grow items-center">
<div className={"truncate"}>{feed.title}</div>
</div>
</a>
<div className="pr-2">
<UserCount
className="-mr-2 group-hover:opacity-0"
count={(feed as any).subscriberCount}
/>
<Button
type="button"
buttonClassName={
"absolute inset-y-0.5 right-0 font-medium opacity-0 duration-200 group-hover:opacity-100"
}
onClick={() => {
follow({ isList: false, id: feed.id })
}}
>
{t("feed_form.follow")}
</Button>
</div>
</li>
)
})}
</ul>
</section>
)
}
const TrendingEntries = ({ data }: { data: Models.TrendingEntry[] }) => {
const { t } = useTranslation()
const filteredData = data.filter((entry) => !entry.url.startsWith("https://x.com"))
return (
<section className="mt-8 w-full text-left">
<h2 className="my-2 text-xl font-bold">{t("trending.entry")}</h2>
{filteredData.length > 0 ? (
<ul className="mt-2 list-inside list-disc space-y-1">
{filteredData.map((entry) => {
return (
<li
key={entry.id}
className="marker:text-accent relative grid w-full grid-cols-[1fr_auto] gap-2 whitespace-nowrap py-0.5"
>
<div className="m-0 min-w-0 truncate p-0">
<a
href={entry.url}
target="_blank"
rel="noopener noreferrer"
className="follow-link--underline truncate text-sm"
>
{entry.title}
</a>
</div>
<span className="flex items-center gap-0.5 text-xs tabular-nums opacity-60">
<i className="i-mingcute-book-2-line" />
<span>{entry.readCount}</span>
</span>
</li>
)
})}
</ul>
) : (
<div className="mt-2 text-center text-sm opacity-80">{t("trending.entry_no_results")}</div>
)}
</section>
)
}

View File

@ -16,8 +16,8 @@ import { DiscoverImport } from "~/modules/discover/import"
import { DiscoverInboxList } from "~/modules/discover/inbox-list-form"
import { Recommendations } from "~/modules/discover/recommendations"
import { DiscoverTransform } from "~/modules/discover/transform-form"
import { Trending } from "~/modules/discover/trending"
import { DiscoverUser } from "~/modules/discover/user-form"
import { Trending } from "~/modules/trending"
const tabs: {
name: I18nKeys

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M16.52 2.278c-.679.146-1.118.389-1.77.981-.587.534-1.302 1.304-1.514 1.632-.196.303-.38.752-.459 1.116-.076.351-.085 1.082-.018 1.414a3.8 3.8 0 0 0 .633 1.419c.167.223 1.362 1.426 1.649 1.66.67.549 1.351.791 2.218.788.7-.003 1.314-.176 1.85-.525.603-.392 1.969-1.793 2.238-2.297.223-.416.293-.603.377-1.006.203-.973-.032-2.047-.608-2.782-.237-.302-1.143-1.225-1.574-1.603a3.424 3.424 0 0 0-3.022-.797m-11.1.28a3.561 3.561 0 0 0-2.119 1.23c-.27.327-.541.844-.667 1.272-.091.309-.094.368-.094 1.7 0 1.338.003 1.389.094 1.68.328 1.045.95 1.773 1.898 2.222.516.245.82.299 1.828.325 1.005.025 1.766-.025 2.14-.141A3.53 3.53 0 0 0 10.846 8.5c.183-.588.205-2.699.035-3.36-.258-1.003-.989-1.89-1.896-2.3-.628-.284-.681-.292-2.085-.304-.704-.005-1.37.004-1.48.022m12.474 1.783c.181.09.396.278.942.823.882.883.944.986.944 1.576 0 .597-.061.704-.864 1.52-.356.362-.739.721-.851.798a1.44 1.44 0 0 1-1.448.101c-.21-.101-.378-.245-.953-.819-.774-.773-.921-.993-.969-1.447-.03-.285.062-.681.211-.913.145-.225 1.445-1.514 1.614-1.601.273-.141.454-.178.8-.166.279.01.382.033.574.128M7.52 4.515c.768.062 1.248.441 1.423 1.125.051.199.061.434.05 1.233-.013.986-.013.987-.126 1.231a1.698 1.698 0 0 1-.767.764l-.24.112h-1.1c-1.056 0-1.109-.004-1.328-.092a1.5 1.5 0 0 1-.757-.713c-.163-.33-.211-.823-.177-1.807.03-.842.081-1.014.408-1.368.293-.316.569-.45.993-.483a12.354 12.354 0 0 1 1.621-.002m-1.9 8.527a3.38 3.38 0 0 0-2.098 1.005c-.43.432-.686.868-.888 1.513-.091.291-.094.342-.094 1.68 0 1.332.003 1.391.094 1.7a3.57 3.57 0 0 0 2.426 2.426c.308.09.37.094 1.66.094 1.444 0 1.526-.009 2.079-.222.938-.362 1.681-1.16 2.028-2.178.217-.638.236-2.816.03-3.525a3.484 3.484 0 0 0-.894-1.498c-.609-.609-1.279-.921-2.139-.997a17.814 17.814 0 0 0-2.204.002m10.5 0c-.99.088-1.94.656-2.507 1.498-.521.773-.643 1.407-.6 3.1.026 1.008.08 1.312.325 1.828.449.948 1.177 1.57 2.222 1.898.291.091.342.094 1.68.094 1.332 0 1.391-.003 1.7-.094a3.57 3.57 0 0 0 2.426-2.426c.09-.308.094-.37.094-1.66 0-1.444-.009-1.526-.222-2.079-.294-.763-.912-1.434-1.678-1.821a3.12 3.12 0 0 0-1.24-.34 18.138 18.138 0 0 0-2.2.002m-8.016 2.091c.309.143.617.452.764.767l.112.24v1.1c0 1.056-.004 1.109-.092 1.328a1.505 1.505 0 0 1-.717.76c-.243.119-.311.133-.834.167-.35.023-.8.023-1.165 0-.693-.043-.9-.118-1.225-.442-.323-.324-.4-.535-.443-1.22-.053-.849.006-1.661.143-1.958.124-.269.346-.524.571-.656.329-.193.52-.217 1.622-.208l1.02.009.244.113m10.35-.052c.443.137.771.45.95.908.085.219.09.286.089 1.271 0 1.27-.022 1.356-.449 1.784-.377.377-.526.425-1.41.456-.985.036-1.479-.012-1.809-.175a1.5 1.5 0 0 1-.713-.757c-.088-.219-.092-.272-.092-1.328v-1.1l.112-.24c.193-.414.603-.759 1.018-.857.082-.019.576-.037 1.096-.039.844-.004.974.005 1.208.077" fill="#10161F" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M7.5 3.257c-.977.227-1.629.909-2.89 3.026-1.038 1.743-1.723 3.052-1.956 3.741a2.45 2.45 0 0 0-.165.897c-.022.555.026.818.221 1.21.102.205.232.377.447.591.6.597 1.2.789 2.816.901.307.021.349.033.326.091a7.123 7.123 0 0 0-.12.429 5.473 5.473 0 0 0 1.439 5.239c.795.794 1.747 1.297 2.887 1.524.483.097 1.498.096 1.99-.001 1.768-.347 3.197-1.446 3.964-3.045.373-.778.517-1.441.516-2.38 0-.522-.019-.743-.086-1.058a4.434 4.434 0 0 1-.077-.404c.004-.002.359-.026.788-.051.922-.055 1.365-.144 1.816-.364a2.406 2.406 0 0 0 1.187-1.187c.31-.635.396-1.375.396-3.416 0-2.654-.156-3.37-.895-4.101-.482-.479-.905-.673-1.758-.808-.381-.06-.725-.071-2.366-.07-1.666 0-1.977.009-2.352.072-.785.132-1.237.329-1.66.725a2.346 2.346 0 0 0-.63.889l-.125.289-.095-.168a19.25 19.25 0 0 0-.487-.757c-.857-1.284-1.577-1.813-2.527-1.854a2.432 2.432 0 0 0-.604.04m.746 2.041c.359.244 1.147 1.453 2.327 3.567.381.683.427.787.427.962v.197l-.339.049c-1.011.146-2.099.663-2.861 1.358l-.26.238-1.02-.033c-1.487-.047-1.917-.138-2.038-.431-.138-.334.393-1.464 1.81-3.845C7.255 5.743 7.684 5.2 8 5.2c.056 0 .166.044.246.098m9.952.781c.485.106.631.262.73.785.074.392.074 3.88 0 4.272-.142.749-.306.818-2.048.852l-1.1.022-.4-.396a5.56 5.56 0 0 0-2.1-1.314l-.26-.088V8.676c.001-1.614.027-1.936.183-2.241.121-.237.402-.346 1.057-.41.385-.037 3.729.009 3.938.054m-6.017 5.997c1.308.27 2.289 1.173 2.686 2.472.095.315.107.415.107.952 0 .537-.012.637-.107.952-.368 1.204-1.211 2.047-2.415 2.415-.315.095-.415.107-.952.107-.537 0-.637-.012-.952-.107-1.204-.368-2.047-1.211-2.415-2.415-.095-.315-.107-.415-.107-.952 0-.537.012-.637.107-.952.545-1.785 2.27-2.839 4.048-2.472" fill="#10161F" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -406,6 +406,7 @@
"words.add": "Add",
"words.boost": "Boost",
"words.browser": "Browser",
"words.categories": "Categories",
"words.confirm": "Confirm",
"words.discover": "Discover",
"words.email": "Email",