Merge pull request #402 from BlackRaincoat/fix/twemoji-server-names

Fix/twemoji server names
This commit is contained in:
Egor
2026-04-22 06:07:25 +03:00
committed by GitHub
10 changed files with 56 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ import {
import { partnerApi } from '../api/partners'; import { partnerApi } from '../api/partners';
import { AdminBackButton } from '../components/admin'; import { AdminBackButton } from '../components/admin';
import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers';
import Twemoji from 'react-twemoji';
// Icons // Icons
const CampaignIcon = () => ( const CampaignIcon = () => (
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}> <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
@@ -108,7 +109,11 @@ function ServerSelector({
> >
{selected.includes(server.squad_uuid) && <CheckIcon />} {selected.includes(server.squad_uuid) && <CheckIcon />}
</div> </div>
<span className="text-sm font-medium">{server.display_name}</span> <span className="text-sm font-medium">
<Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.display_name}
</Twemoji>
</span>
</button> </button>
))} ))}
</div> </div>

View File

@@ -13,6 +13,7 @@ import {
import { AdminBackButton } from '../components/admin'; import { AdminBackButton } from '../components/admin';
import { CheckIcon, CampaignIcon } from '../components/icons'; import { CheckIcon, CampaignIcon } from '../components/icons';
import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers';
import Twemoji from 'react-twemoji';
// Bonus type config // Bonus type config
const bonusTypeConfig: Record< const bonusTypeConfig: Record<
@@ -83,7 +84,11 @@ function ServerSelector({
> >
{selected.includes(server.squad_uuid) && <CheckIcon />} {selected.includes(server.squad_uuid) && <CheckIcon />}
</div> </div>
<span className="text-sm font-medium">{server.display_name}</span> <span className="text-sm font-medium">
<Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.display_name}
</Twemoji>
</span>
</button> </button>
))} ))}
</div> </div>

View File

@@ -11,6 +11,7 @@ import {
import { serversApi } from '../api/servers'; import { serversApi } from '../api/servers';
import { AdminBackButton } from '../components/admin'; import { AdminBackButton } from '../components/admin';
import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers';
import Twemoji from 'react-twemoji';
const getOfferTypeIcon = (offerType: string): string => { const getOfferTypeIcon = (offerType: string): string => {
return OFFER_TYPE_CONFIG[offerType as OfferType]?.icon || '🎁'; return OFFER_TYPE_CONFIG[offerType as OfferType]?.icon || '🎁';
@@ -254,7 +255,11 @@ export default function AdminPromoOfferTemplateEdit() {
}} }}
className="accent-accent-500" className="accent-accent-500"
/> />
<span className="text-dark-200">{server.display_name}</span> <span className="text-dark-200">
<Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.display_name}
</Twemoji>
</span>
{server.country_code && ( {server.country_code && (
<span className="text-dark-500">{server.country_code}</span> <span className="text-dark-500">{server.country_code}</span>
)} )}

View File

@@ -12,6 +12,7 @@ import {
} from '../api/adminRemnawave'; } from '../api/adminRemnawave';
import { usePlatform } from '../platform/hooks/usePlatform'; import { usePlatform } from '../platform/hooks/usePlatform';
import { formatUptime } from '../utils/format'; import { formatUptime } from '../utils/format';
import Twemoji from 'react-twemoji';
import { import {
ServerIcon, ServerIcon,
ChartIcon, ChartIcon,
@@ -225,7 +226,9 @@ function SquadCard({ squad, onClick }: SquadCardProps) {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-lg">{getCountryFlag(squad.country_code)}</span> <span className="text-lg">{getCountryFlag(squad.country_code)}</span>
<h3 className="truncate font-medium text-dark-100"> <h3 className="truncate font-medium text-dark-100">
{squad.display_name || squad.name} <Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{squad.display_name || squad.name}
</Twemoji>
</h3> </h3>
{squad.is_synced ? ( {squad.is_synced ? (
<span className="rounded-full bg-success-500/20 px-2 py-0.5 text-xs text-success-400"> <span className="rounded-full bg-success-500/20 px-2 py-0.5 text-xs text-success-400">

View File

@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { adminRemnawaveApi, SquadWithLocalInfo } from '../api/adminRemnawave'; import { adminRemnawaveApi, SquadWithLocalInfo } from '../api/adminRemnawave';
import { AdminBackButton } from '../components/admin'; import { AdminBackButton } from '../components/admin';
import { ServerIcon, UsersIcon, CheckIcon, XIcon } from '../components/icons'; import { ServerIcon, UsersIcon, CheckIcon, XIcon } from '../components/icons';
import Twemoji from 'react-twemoji';
// Country flags helper // Country flags helper
const getCountryFlag = (code: string | null | undefined): string => { const getCountryFlag = (code: string | null | undefined): string => {
if (!code) return '🌍'; if (!code) return '🌍';
@@ -112,7 +113,9 @@ export default function AdminRemnawaveSquadDetail() {
</div> </div>
<div> <div>
<h1 className="text-xl font-semibold text-dark-100"> <h1 className="text-xl font-semibold text-dark-100">
{squad.display_name || squad.name} <Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{squad.display_name || squad.name}
</Twemoji>
</h1> </h1>
<p className="text-sm text-dark-400">{squad.name}</p> <p className="text-sm text-dark-400">{squad.name}</p>
</div> </div>

View File

@@ -6,6 +6,7 @@ import { serversApi, ServerUpdateRequest } from '../api/servers';
import { AdminBackButton } from '../components/admin'; import { AdminBackButton } from '../components/admin';
import { ServerIcon } from '../components/icons'; import { ServerIcon } from '../components/icons';
import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers';
import Twemoji from 'react-twemoji';
// Country flags (simple emoji mapping) // Country flags (simple emoji mapping)
const getCountryFlag = (code: string | null): string => { const getCountryFlag = (code: string | null): string => {
@@ -139,7 +140,11 @@ export default function AdminServerEdit() {
</div> </div>
<div> <div>
<h1 className="text-xl font-semibold text-dark-100">{t('admin.servers.edit')}</h1> <h1 className="text-xl font-semibold text-dark-100">{t('admin.servers.edit')}</h1>
<p className="text-sm text-dark-400">{server.display_name}</p> <p className="text-sm text-dark-400">
<Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.display_name}
</Twemoji>
</p>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { serversApi, ServerListItem } from '../api/servers'; import { serversApi, ServerListItem } from '../api/servers';
import { SyncIcon, EditIcon, CheckIcon, XIcon, UsersIcon, GiftIcon } from '../components/icons'; import { SyncIcon, EditIcon, CheckIcon, XIcon, UsersIcon, GiftIcon } from '../components/icons';
import { usePlatform } from '../platform/hooks/usePlatform'; import { usePlatform } from '../platform/hooks/usePlatform';
import Twemoji from 'react-twemoji';
// BackIcon // BackIcon
const BackIcon = () => ( const BackIcon = () => (
@@ -145,7 +146,11 @@ export default function AdminServers() {
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="mb-1 flex items-center gap-2"> <div className="mb-1 flex items-center gap-2">
<span className="text-lg">{getCountryFlag(server.country_code)}</span> <span className="text-lg">{getCountryFlag(server.country_code)}</span>
<h3 className="truncate font-medium text-dark-100">{server.display_name}</h3> <h3 className="truncate font-medium text-dark-100">
<Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.display_name}
</Twemoji>
</h3>
{server.is_trial_eligible && ( {server.is_trial_eligible && (
<span className="rounded bg-success-500/20 px-2 py-0.5 text-xs text-success-400"> <span className="rounded bg-success-500/20 px-2 py-0.5 text-xs text-success-400">
{t('admin.servers.trial')} {t('admin.servers.trial')}

View File

@@ -13,6 +13,7 @@ import {
} from '../api/tariffs'; } from '../api/tariffs';
import { AdminBackButton } from '../components/admin'; import { AdminBackButton } from '../components/admin';
import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers';
import Twemoji from 'react-twemoji';
// Icons // Icons
const PlusIcon = () => ( const PlusIcon = () => (
@@ -782,7 +783,11 @@ export default function AdminTariffCreate() {
> >
{isSelected && <CheckIcon />} {isSelected && <CheckIcon />}
</div> </div>
<span className="flex-1 text-sm font-medium">{server.display_name}</span> <span className="flex-1 text-sm font-medium">
<Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.display_name}
</Twemoji>
</span>
{server.country_code && ( {server.country_code && (
<span className="text-xs text-dark-500">{server.country_code}</span> <span className="text-xs text-dark-500">{server.country_code}</span>
)} )}

View File

@@ -24,6 +24,7 @@ import {
getInsufficientBalanceError, getInsufficientBalanceError,
getFlagEmoji, getFlagEmoji,
} from '../utils/subscriptionHelpers'; } from '../utils/subscriptionHelpers';
import Twemoji from 'react-twemoji';
/** Isolated countdown so 1s interval doesn't re-render the whole page */ /** Isolated countdown so 1s interval doesn't re-render the whole page */
const CountdownTimer = memo(function CountdownTimer({ const CountdownTimer = memo(function CountdownTimer({
@@ -980,7 +981,9 @@ export default function Subscription() {
{server.country_code && ( {server.country_code && (
<span className="text-xs">{getFlagEmoji(server.country_code)}</span> <span className="text-xs">{getFlagEmoji(server.country_code)}</span>
)} )}
{server.name} <Twemoji options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}>
{server.name}
</Twemoji>
</span> </span>
))} ))}
</div> </div>

View File

@@ -19,6 +19,7 @@ import InsufficientBalancePrompt from '../components/InsufficientBalancePrompt';
import { useCurrency } from '../hooks/useCurrency'; import { useCurrency } from '../hooks/useCurrency';
import { useCloseOnSuccessNotification } from '../store/successNotification'; import { useCloseOnSuccessNotification } from '../store/successNotification';
import { CheckIcon } from '../components/icons'; import { CheckIcon } from '../components/icons';
import Twemoji from 'react-twemoji';
import { import {
getErrorMessage, getErrorMessage,
getInsufficientBalanceError, getInsufficientBalanceError,
@@ -1795,7 +1796,13 @@ export default function SubscriptionPurchase() {
{selectedServers.includes(server.uuid) && <CheckIcon />} {selectedServers.includes(server.uuid) && <CheckIcon />}
</div> </div>
<div> <div>
<div className="font-medium text-dark-100">{server.name}</div> <div className="font-medium text-dark-100">
<Twemoji
options={{ className: 'twemoji', folder: 'svg', ext: '.svg' }}
>
{server.name}
</Twemoji>
</div>
<div className="flex flex-wrap items-center gap-x-2 gap-y-1"> <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
<span className="text-sm text-accent-400"> <span className="text-sm text-accent-400">
{formatPrice(promoServer.price)} {formatPrice(promoServer.price)}