fix: preserve user figures in avatar imager
This commit is contained in:
1 parent
49185dd7a9
commit
4a6fcd050e
5 files changed
+12
-251
No files matched your search
@@ -1,89 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import {
|
|
||||||
convertFigurePart,
|
|
||||||
convertFigureString,
|
|
||||||
getNewFormatFigure,
|
|
||||||
} from "./figure-convert";
|
|
||||||
|
|
||||||
describe("convertFigurePart", () => {
|
|
||||||
it("converts head part", () => {
|
|
||||||
expect(convertFigurePart("hr-11782-40-40")).toBe("hd-180-1");
|
|
||||||
expect(convertFigurePart("hr-11782-41-41")).toBe("hd-180-2");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("converts hair part", () => {
|
|
||||||
expect(convertFigurePart("hd-180-7-14")).toBe("hd-180-1");
|
|
||||||
expect(convertFigurePart("hd-180-8-14")).toBe("hd-180-2");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("converts face part", () => {
|
|
||||||
expect(convertFigurePart("ch-11592-66")).toBe("ch-255-66");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("converts shirt part", () => {
|
|
||||||
expect(convertFigurePart("lg-10726-79-1408")).toBe("lg-280-110");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("converts shorts part", () => {
|
|
||||||
expect(convertFigurePart("sh-11764-1408")).toBe("sh-305-62");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("converts shoes part", () => {
|
|
||||||
expect(convertFigurePart("ha-11958-70-1408")).toBe("ha-310-70");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("converts waist part", () => {
|
|
||||||
expect(convertFigurePart("wa-2007-0")).toBe("wa-200-0");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("returns original for unknown parts", () => {
|
|
||||||
expect(convertFigurePart("unknown-part")).toBe("unknown-part");
|
|
||||||
expect(convertFigurePart("hd-999-99")).toBe("hd-999-99");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("convertFigureString", () => {
|
|
||||||
it("converts full figure string from old to new format (with defaults: exclude shoes, waist, dedupe head)", () => {
|
|
||||||
const oldFigure =
|
|
||||||
"hr-11782-40-40.hd-180-7-14.ch-11592-66.lg-10726-79-1408.sh-11764-1408.ha-11958-70-1408.wa-2007-0";
|
|
||||||
const newFigure = "hd-180-1.ch-255-66.lg-280-110.sh-305-62";
|
|
||||||
expect(convertFigureString(oldFigure)).toBe(newFigure);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("includes all parts when options disabled", () => {
|
|
||||||
const oldFigure =
|
|
||||||
"hr-11782-40-40.hd-180-7-14.ch-11592-66.lg-10726-79-1408.sh-11764-1408.ha-11958-70-1408.wa-2007-0";
|
|
||||||
const newFigure =
|
|
||||||
"hd-180-1.hd-180-1.ch-255-66.lg-280-110.sh-305-62.ha-310-70.wa-200-0";
|
|
||||||
expect(
|
|
||||||
convertFigureString(oldFigure, {
|
|
||||||
excludeShoes: false,
|
|
||||||
excludeWaist: false,
|
|
||||||
dedupeHead: false,
|
|
||||||
}),
|
|
||||||
).toBe(newFigure);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles empty string", () => {
|
|
||||||
expect(convertFigureString("")).toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles already converted figure", () => {
|
|
||||||
const newFigure = "hd-180-1.ch-255-66.lg-280-110";
|
|
||||||
expect(convertFigureString(newFigure)).toBe(newFigure);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getNewFormatFigure", () => {
|
|
||||||
it("converts figure using getNewFormatFigure with defaults", () => {
|
|
||||||
const oldFigure = "hr-11782-40-40.hd-180-7-14.ch-11592-66";
|
|
||||||
expect(getNewFormatFigure(oldFigure)).toBe("hd-180-1.ch-255-66");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows custom options", () => {
|
|
||||||
const oldFigure = "hr-11782-40-40.hd-180-7-14.ch-11592-66.ha-11958-70-1408";
|
|
||||||
expect(getNewFormatFigure(oldFigure, { excludeShoes: false })).toBe(
|
|
||||||
"hd-180-1.ch-255-66.ha-310-70",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -37,6 +37,15 @@ describe("avatarImageUrl", () => {
|
|||||||
expect(url.startsWith("https://img.example.com?")).toBe(true);
|
expect(url.startsWith("https://img.example.com?")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves the exact user look instead of replacing it with a default figure", () => {
|
||||||
|
const look =
|
||||||
|
"hr-11782-40-40.hd-180-7-14.ch-11592-66.lg-10726-79-1408.sh-11764-1408.ha-11958-70-1408.wa-2007-0";
|
||||||
|
|
||||||
|
const url = new URL(avatarImageUrl(look));
|
||||||
|
|
||||||
|
expect(url.searchParams.get("figure")).toBe(look);
|
||||||
|
});
|
||||||
|
|
||||||
it("uses & when the base already has a query string", () => {
|
it("uses & when the base already has a query string", () => {
|
||||||
// This test is no longer applicable since avatarImageUrl doesn't accept a base parameter
|
// This test is no longer applicable since avatarImageUrl doesn't accept a base parameter
|
||||||
// The base URL is now resolved from env
|
// The base URL is now resolved from env
|
||||||
|
|||||||
+1
-3
@@ -1,11 +1,9 @@
|
|||||||
import { getNewFormatFigure } from "@/lib/figure-convert";
|
|
||||||
import { getAvatarUrl } from "@/lib/imager";
|
import { getAvatarUrl } from "@/lib/imager";
|
||||||
import type { AvatarOptions } from "@/types/admin";
|
import type { AvatarOptions } from "@/types/admin";
|
||||||
|
|
||||||
/** Build a Habbo avatar-imager URL for a figure string. */
|
/** Build a Habbo avatar-imager URL for a figure string. */
|
||||||
export function avatarImageUrl(look: string, opts: AvatarOptions = {}): string {
|
export function avatarImageUrl(look: string, opts: AvatarOptions = {}): string {
|
||||||
const figure = getNewFormatFigure(look);
|
return getAvatarUrl(look, opts);
|
||||||
return getAvatarUrl(figure, opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** URL-safe slug from a title (lowercase, ascii, hyphenated). */
|
/** URL-safe slug from a title (lowercase, ascii, hyphenated). */
|
||||||
|
|||||||
+2
-4
@@ -8,7 +8,6 @@
|
|||||||
export type { AvatarOptions } from "@/types/admin";
|
export type { AvatarOptions } from "@/types/admin";
|
||||||
|
|
||||||
import { env } from "@/env";
|
import { env } from "@/env";
|
||||||
import { getNewFormatFigure } from "@/lib/figure-convert";
|
|
||||||
import type { AvatarOptions } from "@/types/admin";
|
import type { AvatarOptions } from "@/types/admin";
|
||||||
|
|
||||||
const DEFAULT_IMAGER_URL = "https://epicnabbo.nl/imaging/avatarimage";
|
const DEFAULT_IMAGER_URL = "https://epicnabbo.nl/imaging/avatarimage";
|
||||||
@@ -28,14 +27,13 @@ export function resolveImagerBase(): string {
|
|||||||
/**
|
/**
|
||||||
* Build an avatar image URL using the configured imager.
|
* Build an avatar image URL using the configured imager.
|
||||||
* Uses effect=14 and img_format=apng by default for epicnabbo.nl compatibility.
|
* Uses effect=14 and img_format=apng by default for epicnabbo.nl compatibility.
|
||||||
* Automatically converts old Habbo figure format to new epicnabbo.nl format.
|
* Preserves the user's stored figure so the rendered avatar matches the account.
|
||||||
* Omits default parameters (direction=2, head_direction=3, size=m) for cleaner URLs.
|
* Omits default parameters (direction=2, head_direction=3, size=m) for cleaner URLs.
|
||||||
*/
|
*/
|
||||||
export function getAvatarUrl(
|
export function getAvatarUrl(
|
||||||
figure: string,
|
figure: string,
|
||||||
options: AvatarOptions = {},
|
options: AvatarOptions = {},
|
||||||
): string {
|
): string {
|
||||||
const convertedFigure = getNewFormatFigure(figure);
|
|
||||||
const {
|
const {
|
||||||
size = "m",
|
size = "m",
|
||||||
direction = 2,
|
direction = 2,
|
||||||
@@ -48,7 +46,7 @@ export function getAvatarUrl(
|
|||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
figure: convertedFigure,
|
figure,
|
||||||
img_format,
|
img_format,
|
||||||
effect: String(effect),
|
effect: String(effect),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user