feat(herowars): migrate JSON static imports to HttpClient assets

Replace all TypeScript JSON imports with an HWDataService that loads
6 data files from /assets/files-data/ via HttpClient with shareReplay(1).
Update all 9 consumer components to subscribe asynchronously. Remove
unused qcm-bpa.json import and dead methods from QcmComponent.
Add ADR 0013 documenting the decision.
This commit is contained in:
2026-04-26 23:46:35 +02:00
parent 86c075db10
commit fa00719a3e
23 changed files with 89071 additions and 551 deletions
@@ -14,12 +14,12 @@ import {
HWGuildClan,
HWMember,
} from '@models';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
import { HWDataService } from './hw-data.service';
@Injectable({ providedIn: 'root' })
export class HWClanService {
private apiService = inject(ApiService);
private _dataService = inject(HWDataService);
private _apiDomain = '/herowars';
@@ -87,15 +87,18 @@ export class HWClanService {
}
}
loadClan(): HWGuildClan {
const guildClan: HWGuildClan = {} as HWGuildClan;
Object.assign(guildClan, guildData.clan);
guildClan.sumAverages = this._resetActivity();
guildClan.sumTotal = this._resetActivity();
guildClan.todayAverages = this._resetActivity();
guildClan.todayTotal = this._resetActivity();
return guildClan;
loadClan(): Observable<HWGuildClan> {
return this._dataService.guildData$.pipe(
map((guildData) => {
const guildClan: HWGuildClan = {} as HWGuildClan;
Object.assign(guildClan, guildData.clan);
guildClan.sumAverages = this._resetActivity();
guildClan.sumTotal = this._resetActivity();
guildClan.todayAverages = this._resetActivity();
guildClan.todayTotal = this._resetActivity();
return guildClan;
}),
);
}
loadClanStats(clan: HWGuildClan, members: HWMember[]): HWGuildClan {
@@ -0,0 +1,44 @@
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
import { HWGuildData, HWGuildWarSlots, HWWeekStat } from '@models';
export interface HWGuildStatistics {
stat: (HWWeekStat & { id: string })[];
}
@Injectable({ providedIn: 'root' })
export class HWDataService {
private _http = inject(HttpClient);
readonly guildData$: Observable<HWGuildData> = this._http
.get<HWGuildData>('/assets/files-data/hw-guild-data.json')
.pipe(shareReplay(1));
readonly guildStatistics$: Observable<HWGuildStatistics> = this._http
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.get<HWGuildStatistics>('/assets/files-data/hw-guild-statistics.json')
.pipe(shareReplay(1));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly guildRaids$: Observable<Record<string, Record<string, any>>> = this._http
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.get<Record<string, Record<string, any>>>('/assets/files-data/hw-guild-raids.json')
.pipe(shareReplay(1));
/* eslint-disable @typescript-eslint/no-explicit-any */
readonly guildWarData$: Observable<any> = this._http
.get<any>('/assets/files-data/hw-guild-war.json')
.pipe(shareReplay(1));
readonly guildWarInfo$: Observable<any> = this._http
.get<any>('/assets/files-data/hw-guild-war-info.json')
.pipe(shareReplay(1));
/* eslint-enable @typescript-eslint/no-explicit-any */
readonly guildWarSlots$: Observable<{ slots: HWGuildWarSlots }> = this._http
.get<{ slots: HWGuildWarSlots }>('/assets/files-data/hw-guild-war-slots.json')
.pipe(shareReplay(1));
}
@@ -1,6 +1,6 @@
import { Injectable, inject } from '@angular/core';
import { HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiService, UtilitiesService } from '@services';
@@ -14,13 +14,12 @@ import {
HWMemberPageData,
HWMemberStat,
} from '@models';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
import guildStatistics from 'src/files-data/hw-guild-statistics.json'; // page Overview -> Statistics
import { HWDataService } from './hw-data.service';
@Injectable({ providedIn: 'root' })
export class HWMemberService {
private _apiService = inject(ApiService);
private _utilitiesService = inject(UtilitiesService);
private _dataService = inject(HWDataService);
private _apiDomain = '/herowars';
@@ -76,83 +75,79 @@ export class HWMemberService {
}
}
loadMembers(clan: HWGuildClan): HWMember[] {
const guildMembers: HWMember[] = [];
const now = new Date();
const oneDayInSec = 24 * 60 * 60;
const daysToKick = parseInt(guildData.clan.daysToKick);
for (const data of Object.entries(guildData.clan.members)) {
const member: HWMember = {} as HWMember;
Object.assign(member, data[1]);
if (guildData.clan.warriors.indexOf(parseInt(member.id)) !== -1) {
member.champion = true;
} else {
member.champion = false;
}
member.heroes = { power: 0, teams: [] };
member.titans = { power: 0, teams: [] };
guildMembers.push(member);
}
guildMembers.map((data: HWMember) => {
const last = new Date(parseInt(data.lastLoginTime) * 1000);
const exp = new Date(last.getTime() + daysToKick * oneDayInSec * 1000);
const diff = Math.floor((exp.getTime() - now.getTime()) / 1000);
const daysLeft = Math.floor(diff / oneDayInSec);
let timeLeft = diff;
if (daysLeft >= 1) {
timeLeft = diff - daysLeft * oneDayInSec;
}
data.inactivity = {
daysLeft: daysLeft,
timeLeft: timeLeft,
dropDelay: `${daysLeft}d, ${this._utilitiesService.secondsToDuration(timeLeft)}`,
dropDate: Math.floor(exp.getTime() / 1000),
};
data.raids = [];
data.raidsInfo = {
variationAvg: 0,
variationSum: 0,
};
data.stat = {} as HWMemberStat;
let index = guildData.membersStat.findIndex((stat) => stat.userId === data.id);
if (index !== -1) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
const { userId, ...memberStatWithoutUserId } = guildData.membersStat[index] as any;
Object.assign(data.stat, memberStatWithoutUserId);
}
index = guildStatistics.stat.findIndex((stat) => stat.id === data.id);
if (index !== -1) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
const { id, ...weekStatSource } = guildStatistics.stat[index] as any;
const stat: HWWeekStat = {} as HWWeekStat;
Object.assign(stat, weekStatSource);
data.weekStat = stat;
data.adventureSum = guildStatistics.stat[index].adventureStat.reduce(
(accumulator, currentValue) => accumulator + currentValue,
0,
);
data.clanGiftsSum = guildStatistics.stat[index].clanGifts.reduce(
(accumulator, currentValue) => accumulator + currentValue,
0,
);
data.clanWarSum = guildStatistics.stat[index].clanWarStat.reduce(
(accumulator, currentValue) => accumulator + currentValue,
0,
);
} else {
data.weekStat = {} as HWWeekStat;
data.adventureSum = 0;
data.clanGiftsSum = 0;
data.clanWarSum = 0;
}
data.score = data.stat.dungeonActivitySum + data.stat.activitySum + data.stat.prestigeSum;
data.warGifts = ((clan.giftsCount / 2) * ((data.clanWarSum * 10) / 100)) / 20;
data.scoreGifts = 0;
data.rewards = 0;
return data;
});
return guildMembers;
loadMembers(clan: HWGuildClan): Observable<HWMember[]> {
return combineLatest([this._dataService.guildData$, this._dataService.guildStatistics$]).pipe(
map(([guildData, guildStatistics]) => {
const guildMembers: HWMember[] = [];
const now = new Date();
const oneDayInSec = 24 * 60 * 60;
const daysToKick = parseInt(guildData.clan.daysToKick);
for (const data of Object.entries(guildData.clan.members)) {
const member: HWMember = {} as HWMember;
Object.assign(member, data[1]);
member.champion = guildData.clan.warriors.indexOf(parseInt(member.id)) !== -1;
member.heroes = { power: 0, teams: [] };
member.titans = { power: 0, teams: [] };
guildMembers.push(member);
}
guildMembers.map((data: HWMember) => {
const last = new Date(parseInt(data.lastLoginTime) * 1000);
const exp = new Date(last.getTime() + daysToKick * oneDayInSec * 1000);
const diff = Math.floor((exp.getTime() - now.getTime()) / 1000);
const daysLeft = Math.floor(diff / oneDayInSec);
let timeLeft = diff;
if (daysLeft >= 1) {
timeLeft = diff - daysLeft * oneDayInSec;
}
data.inactivity = {
daysLeft: daysLeft,
timeLeft: timeLeft,
dropDelay: `${daysLeft}d, ${this._utilitiesService.secondsToDuration(timeLeft)}`,
dropDate: Math.floor(exp.getTime() / 1000),
};
data.raids = [];
data.raidsInfo = { variationAvg: 0, variationSum: 0 };
data.stat = {} as HWMemberStat;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let index = guildData.membersStat.findIndex((stat) => (stat as any).userId === data.id);
if (index !== -1) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
const { userId, ...memberStatWithoutUserId } = guildData.membersStat[index] as any;
Object.assign(data.stat, memberStatWithoutUserId);
}
index = guildStatistics.stat.findIndex((stat) => stat.id === data.id);
if (index !== -1) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
const { id, ...weekStatSource } = guildStatistics.stat[index] as any;
const stat: HWWeekStat = {} as HWWeekStat;
Object.assign(stat, weekStatSource);
data.weekStat = stat;
data.adventureSum = guildStatistics.stat[index].adventureStat.reduce(
(acc: number, cur: number) => acc + cur,
0,
);
data.clanGiftsSum = guildStatistics.stat[index].clanGifts.reduce(
(acc: number, cur: number) => acc + cur,
0,
);
data.clanWarSum = guildStatistics.stat[index].clanWarStat.reduce(
(acc: number, cur: number) => acc + cur,
0,
);
} else {
data.weekStat = {} as HWWeekStat;
data.adventureSum = 0;
data.clanGiftsSum = 0;
data.clanWarSum = 0;
}
data.score = data.stat.dungeonActivitySum + data.stat.activitySum + data.stat.prestigeSum;
data.warGifts = ((clan.giftsCount / 2) * ((data.clanWarSum * 10) / 100)) / 20;
data.scoreGifts = 0;
data.rewards = 0;
return data;
});
return guildMembers;
}),
);
}
}
+2 -2
View File
@@ -1,3 +1,3 @@
export * from './hw-clan.service';
export * from './hw-member.service';
export * from './hw-data.service';
export * from './hw-member.service';