Skip to content

Commit

Permalink
Rework achievement sort to better support all locales
Browse files Browse the repository at this point in the history
  • Loading branch information
candela97 committed Mar 18, 2024
1 parent 51d0964 commit 2e83910
Showing 1 changed file with 50 additions and 86 deletions.
136 changes: 50 additions & 86 deletions src/js/Content/Features/Community/ProfileStats/FAchievementSort.ts
Original file line number Diff line number Diff line change
@@ -1,124 +1,88 @@
import {Localization} from "../../../../modulesCore";
import {Feature, Sortbox} from "../../../modulesContent";
import {HTML, Localization} from "../../../../modulesCore";
import {Feature, RequestData, Sortbox} from "../../../modulesContent";
import type {CProfileStats} from "./CProfileStats";
import {DateTime, type DateTimeOptions} from "luxon";
import {Page} from "../../Page";
import {DateTime} from "luxon";

export default class FAchievementSort extends Feature<CProfileStats> {

private container: Element|null = null;
private bookmark: Element|null = null;
private unlockedAchievements: Element[] = [];
private achievementsFetched: boolean = false;

private defaultSort: Element[] = [];
private unlockedMap: Map<Element, number> = new Map();

private dateFormat: string|null = null;
private dateOptions: DateTimeOptions|undefined = undefined;

override async checkPrerequisites(): Promise<boolean> {

await this.loadDateFormat();

// Check if we support current locale
if (!this.dateFormat) {
return false;
}
override checkPrerequisites(): boolean {

// Check if the user has unlocked more than 1 achievement
return document.querySelectorAll("#personalAchieve .achieveUnlockTime").length > 1;
}

override async apply(): Promise<void> {
if (!this.dateFormat) {
return;
}
override apply(): void {

this.container = document.getElementById("personalAchieve");
if (this.container === null) {
throw new Error("Did not find #personalAchieve node");
}
this.unlockedAchievements = Array.from(document.querySelectorAll(".achieveRow"))
.filter(el => !!el.querySelector(".achieveUnlockTime"));

const tabs = document.getElementById("tabs");
if (tabs === null) {
throw new Error("Did not find #tabs");
}
this.unlockedAchievements.forEach((ach, i) => { ach.dataset.esSortdefault = `${i}`; });

// Insert an empty div before the first unlocked achievement as an anchor for sorted nodes
this.bookmark = document.createElement("div");
(document.querySelector(".achieveRow") as Element).before(this.bookmark);

const sortbox = Sortbox.get(
(document.getElementById("tabs") as Element).before(Sortbox.get(
"achievements",
[
["default", Localization.str.theworddefault],
["time", Localization.str.date_unlocked],
],
"default_ASC",
(sortBy: "time"|"default", reversed: boolean) => { this._sortRows(sortBy, reversed); },
);
if (sortbox === null) {
throw new Error("Failed to create Sortbox");
}
));
}

tabs.insertAdjacentElement("beforebegin", sortbox);
private async _sortRows(sortBy: "time"|"default", reversed: boolean) {

this.bookmark = document.createElement("div");
const achieveRow = document.querySelector(".achieveRow");
if (!achieveRow) {
throw new Error("Could not create bookmark");
}
achieveRow.insertAdjacentElement("beforebegin", this.bookmark);

const nodes = this.container.querySelectorAll(".achieveUnlockTime");
for (let node of nodes) {
if (!node.firstChild?.textContent) { continue; }
const achieveRow = node.closest(".achieveRow");

if (!achieveRow) { continue; }
const unlockedTime = DateTime.fromFormat(
node.firstChild.textContent.trim(),
this.dateFormat,
this.dateOptions
);

this.defaultSort.push(achieveRow);
this.unlockedMap.set(achieveRow, unlockedTime.toUnixInteger());
}
}
const property = `esSort${sortBy}`;

private async loadDateFormat(): Promise<void> {
let language = await Page.runInPageContext(() => window.g_strLanguage, [], true);
if (sortBy === "time" && !this.achievementsFetched) {

switch(language) {
case "english":
this.dateFormat = "'Unlocked' d LLL, yyyy '@' h:mma";
return;
this.achievementsFetched = true;

case "czech":
this.dateFormat = "'Odemčeno' d. LLL. yyyy v H.mm";
this.dateOptions = {locale: "cs"};
return;
const url = new URL(window.location.origin + window.location.pathname);
url.searchParams.set("l", "english");

// TODO add support for more locales
}
}
const data = await RequestData.getHttp(url.toString());
const dom = HTML.toDom(data);
const nodes = dom.querySelectorAll(".achieveRow");

private async _sortRows(sortBy: "time"|"default", reversed: boolean) {
if (!this.container || !this.bookmark) {
return;
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const unlockedText = node.querySelector(".achieveUnlockTime")?.firstChild?.textContent?.trim();
if (!unlockedText) { continue; }

let sortedNodes: Element[] = [];
const unlockedTime = DateTime.fromFormat(
unlockedText,
"'Unlocked' d LLL, yyyy '@' h:mma",
{"locale": "en-US"}
);

if (sortBy === "default") {
sortedNodes = [...this.defaultSort];
} else if (sortBy === "time") {
sortedNodes = [...this.unlockedMap.keys()]
.sort((a, b) => (this.unlockedMap.get(a) ?? 0) - (this.unlockedMap.get(b) ?? 0));
(document.querySelector(`.achieveRow[data-es-sortdefault="${i}"]`) as Element)
.dataset[property] = `${unlockedTime.toUnixInteger()}`;
}
}

this.unlockedAchievements.sort(this._getSortFunc(sortBy, property));

if (reversed) {
sortedNodes.reverse();
this.unlockedAchievements.reverse();
}

for (let node of sortedNodes) {
this.bookmark.insertAdjacentElement("beforebegin", node);
this.unlockedAchievements.forEach(ach => this.bookmark.before(ach));
}

private _getSortFunc(sortBy: "time"|"default", property) {
switch (sortBy) {
case "default":
return (a, b) => Number(a.dataset[property] ?? 0) - Number(b.dataset[property] ?? 0);
case "time":
return (a, b) => Number(b.dataset[property] ?? 0) - Number(a.dataset[property] ?? 0);
}
}
}

0 comments on commit 2e83910

Please sign in to comment.