Add public profiles and cosmetic shop
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 7m10s
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 7m10s
This commit is contained in:
@@ -43,13 +43,26 @@ export const profileUpdateSchema = z
|
||||
displayName: displayNameSchema.optional(),
|
||||
glowColor: glowColorSchema.optional(),
|
||||
avatarPath: z.string().max(128).nullable().optional(),
|
||||
bio: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(280, "Use at most 280 characters")
|
||||
.refine(
|
||||
(value) =>
|
||||
!/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069<>]/u.test(
|
||||
value,
|
||||
),
|
||||
"Use plain text without hidden controls or markup",
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
(value) =>
|
||||
value.displayName !== undefined ||
|
||||
value.glowColor !== undefined ||
|
||||
value.avatarPath !== undefined,
|
||||
value.avatarPath !== undefined ||
|
||||
value.bio !== undefined,
|
||||
"Choose a profile change",
|
||||
);
|
||||
export const initialPasswordSchema = z
|
||||
|
||||
@@ -568,6 +568,8 @@ export type Database = {
|
||||
profiles: {
|
||||
Row: AccountStatusFields & {
|
||||
avatar_path: string | null;
|
||||
bio: string;
|
||||
profile_effect: string | null;
|
||||
glow_color: string;
|
||||
bug_finder: boolean;
|
||||
custom_prefix: string | null;
|
||||
@@ -580,6 +582,8 @@ export type Database = {
|
||||
};
|
||||
Insert: Partial<AccountStatusFields> & {
|
||||
avatar_path?: string | null;
|
||||
bio?: string;
|
||||
profile_effect?: string | null;
|
||||
glow_color?: string;
|
||||
bug_finder?: boolean;
|
||||
custom_prefix?: string | null;
|
||||
@@ -592,6 +596,8 @@ export type Database = {
|
||||
};
|
||||
Update: Partial<AccountStatusFields> & {
|
||||
avatar_path?: string | null;
|
||||
bio?: string;
|
||||
profile_effect?: string | null;
|
||||
glow_color?: string;
|
||||
bug_finder?: boolean;
|
||||
custom_prefix?: string | null;
|
||||
@@ -782,6 +788,18 @@ export type Database = {
|
||||
[_ in never]: never;
|
||||
};
|
||||
Functions: {
|
||||
cosmetics_shop_state: {
|
||||
Args: { p_user: string };
|
||||
Returns: Json;
|
||||
};
|
||||
buy_profile_cosmetic: {
|
||||
Args: { p_user: string; p_cosmetic_id: string };
|
||||
Returns: Json;
|
||||
};
|
||||
equip_profile_cosmetic: {
|
||||
Args: { p_user: string; p_cosmetic_id: string | null };
|
||||
Returns: Json;
|
||||
};
|
||||
admin_account_action: {
|
||||
Args: {
|
||||
p_reviewer: string;
|
||||
|
||||
@@ -10,6 +10,55 @@
|
||||
"Guide": "Как играть",
|
||||
"Docs": "Документация",
|
||||
"My account": "Мой аккаунт",
|
||||
"Profile cosmetics": "Косметика профиля",
|
||||
"Browse profile cosmetics": "Открыть магазин косметики",
|
||||
"View public profile": "Открыть публичный профиль",
|
||||
"About you": "О себе",
|
||||
"Tell other explorers a little about yourself.": "Расскажите другим исследователям немного о себе.",
|
||||
"Shown on your public profile · Maximum 280 characters.": "Отображается в публичном профиле · Не более 280 символов.",
|
||||
"Save public profile": "Сохранить публичный профиль",
|
||||
"Public profile saved.": "Публичный профиль сохранён.",
|
||||
"A name and description for other explorers to see.": "Имя и описание, которые увидят другие исследователи.",
|
||||
"Back to leaderboard": "Назад к рейтингу",
|
||||
"MINEGEO EXPLORER": "ИССЛЕДОВАТЕЛЬ MINEGEO",
|
||||
"Exploring since {date}": "Исследует миры с {date}",
|
||||
"About this explorer": "Об исследователе",
|
||||
"This explorer hasn’t added a description yet.": "Исследователь пока не добавил описание.",
|
||||
"Profile stats": "Статистика профиля",
|
||||
"Completed journeys": "Завершённые экспедиции",
|
||||
"Published maps": "Опубликованные карты",
|
||||
"Only public account details and published maps appear here.": "Здесь отображаются только публичные данные аккаунта и опубликованные карты.",
|
||||
"THE EXPLORER’S WARDROBE": "ГАРДЕРОБ ИССЛЕДОВАТЕЛЯ",
|
||||
"Leave a little sparkle.": "Добавь немного сияния.",
|
||||
"Earn GeoCoins in solo expeditions and dress up your profile portrait.": "Зарабатывай GeoCoins в одиночных экспедициях и укрась портрет профиля.",
|
||||
"GeoCoins balance": "Баланс GeoCoins",
|
||||
"YOUR BALANCE": "ТВОЙ БАЛАНС",
|
||||
"GeoCoins": "GeoCoins",
|
||||
"Complete a public solo game to earn 10–20 GeoCoins. Rewards are capped at 100 every 24 hours.": "Заверши одиночную игру на публичной карте и получи 10–20 GeoCoins. Максимум — 100 за каждые 24 часа.",
|
||||
"common": "обычный",
|
||||
"rare": "редкий",
|
||||
"epic": "эпический",
|
||||
"legendary": "легендарный",
|
||||
"Equipped": "Надето",
|
||||
"Cosmetic purchased and equipped.": "Эффект куплен и надет.",
|
||||
"Cosmetic equipped.": "Эффект надет.",
|
||||
"Profile effect removed.": "Эффект профиля снят.",
|
||||
"Not enough GeoCoins": "Недостаточно GeoCoins.",
|
||||
"Cosmetic is not available": "Эта косметика сейчас недоступна.",
|
||||
"Cosmetic not owned": "Сначала купи этот эффект.",
|
||||
"Buy · {price} GeoCoins": "Купить · {price} GeoCoins",
|
||||
"Unequip effect": "Снять эффект",
|
||||
"Equip effect": "Надеть эффект",
|
||||
"Buying…": "Покупка…",
|
||||
"Cosmetics use GeoCoins for now. Payment options may be added later.": "Пока косметика покупается за GeoCoins. Позже могут появиться другие способы оплаты.",
|
||||
"Redstone Pulse": "Импульс редстоуна",
|
||||
"A redstone signal ripples around your portrait.": "Вокруг портрета пробегает импульс редстоуна.",
|
||||
"Ender Orbit": "Орбита Края",
|
||||
"Two ender sparks circle your portrait.": "Вокруг портрета вращаются две искры Края.",
|
||||
"Nether Flame": "Пламя Незера",
|
||||
"A warm nether flame flickers along the frame.": "По рамке мерцает жаркое пламя Незера.",
|
||||
"Aurora Crown": "Корона сияния",
|
||||
"A living aurora sweeps around your portrait.": "Вокруг портрета переливается северное сияние.",
|
||||
"Log in": "Войти",
|
||||
"Tools & downloads": "Инструменты",
|
||||
"Moderation": "Модерация",
|
||||
|
||||
@@ -36,6 +36,7 @@ export const prefixUpdateSchema = z
|
||||
.strict();
|
||||
export type ProfileAppearance = {
|
||||
avatar_path?: string | null;
|
||||
profile_effect?: string | null;
|
||||
name_glow?: boolean;
|
||||
glow_color?: string;
|
||||
role?: string;
|
||||
@@ -44,7 +45,7 @@ export type ProfileAppearance = {
|
||||
custom_prefix_icon?: string;
|
||||
};
|
||||
export const appearanceColumns =
|
||||
"avatar_path,name_glow,glow_color,role,bug_finder,custom_prefix,custom_prefix_icon" as const;
|
||||
"avatar_path,profile_effect,name_glow,glow_color,role,bug_finder,custom_prefix,custom_prefix_icon" as const;
|
||||
export type BadgeUser = ProfileAppearance & {
|
||||
id: string;
|
||||
display_name: string;
|
||||
|
||||
8
shared/profile-cosmetic-ids.ts
Normal file
8
shared/profile-cosmetic-ids.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const profileCosmeticIds = [
|
||||
"redstone-pulse",
|
||||
"ender-orbit",
|
||||
"nether-flame",
|
||||
"aurora-crown",
|
||||
] as const;
|
||||
|
||||
export type ProfileCosmeticId = (typeof profileCosmeticIds)[number];
|
||||
41
shared/profile-cosmetics.ts
Normal file
41
shared/profile-cosmetics.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { z } from "zod";
|
||||
import { profileCosmeticIds } from "./profile-cosmetic-ids";
|
||||
|
||||
export const profileCosmeticIdSchema = z.enum(profileCosmeticIds);
|
||||
export type { ProfileCosmeticId } from "./profile-cosmetic-ids";
|
||||
|
||||
export const profileCosmeticActionSchema = z.discriminatedUnion("action", [
|
||||
z
|
||||
.object({
|
||||
action: z.literal("purchase"),
|
||||
cosmeticId: profileCosmeticIdSchema,
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
action: z.literal("equip"),
|
||||
cosmeticId: profileCosmeticIdSchema.nullable(),
|
||||
})
|
||||
.strict(),
|
||||
]);
|
||||
|
||||
export const cosmeticShopStateSchema = z
|
||||
.object({
|
||||
balance: z.number().int().nonnegative(),
|
||||
owned: z.array(profileCosmeticIdSchema),
|
||||
equipped: profileCosmeticIdSchema.nullable(),
|
||||
catalog: z.array(
|
||||
z
|
||||
.object({
|
||||
id: profileCosmeticIdSchema,
|
||||
name: z.string().min(1).max(48),
|
||||
description: z.string().min(1).max(160),
|
||||
price: z.number().int().positive(),
|
||||
rarity: z.enum(["common", "rare", "epic", "legendary"]),
|
||||
})
|
||||
.strict(),
|
||||
),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export type CosmeticShopState = z.infer<typeof cosmeticShopStateSchema>;
|
||||
Reference in New Issue
Block a user