2020-07-17 13:05:13 +00:00
|
|
|
import isURL from 'validator/lib/isURL';
|
2020-07-17 19:44:35 +00:00
|
|
|
import differenceInDays from 'date-fns/differenceInDays';
|
2020-07-22 15:31:27 +00:00
|
|
|
import getTranslations from './i18n/extendedTribeProfile';
|
2020-07-17 11:50:29 +00:00
|
|
|
import requestCreator from './libs/requestCreator';
|
2020-07-18 10:02:35 +00:00
|
|
|
import {
|
|
|
|
setPage,
|
|
|
|
getPage,
|
|
|
|
generatePaginationItems,
|
|
|
|
getContainerStyles,
|
|
|
|
} from './utils/pagination';
|
2020-07-22 14:19:44 +00:00
|
|
|
import renderTodaysStats from './common/renderTodaysStats';
|
|
|
|
import showEnnoblementsPopup from './common/showEnnoblementsPopup';
|
|
|
|
import showHistoryPopup from './common/showHistoryPopup';
|
2020-07-20 12:07:57 +00:00
|
|
|
import showPopup from './utils/showPopup';
|
2020-07-17 11:50:29 +00:00
|
|
|
import getIDFromURL from './utils/getIDFromURL';
|
|
|
|
import getCurrentServer from './utils/getCurrentServer';
|
|
|
|
import { setItem, getItem } from './utils/localStorage';
|
|
|
|
import formatDate from './utils/formatDate';
|
2020-07-17 13:05:13 +00:00
|
|
|
import { formatPlayerURL } from './utils/twstats';
|
2020-07-17 19:44:35 +00:00
|
|
|
import { formatPlayerURL as formatPlayerURLTribalWars } from './utils/tribalwars';
|
2020-07-17 11:50:29 +00:00
|
|
|
|
|
|
|
// ==UserScript==
|
|
|
|
// @name Extended Tribe Profile
|
|
|
|
// @namespace https://github.com/tribalwarshelp/scripts
|
|
|
|
// @updateURL https://raw.githubusercontent.com/tribalwarshelp/scripts/master/dist/extendedTribeProfile.js
|
|
|
|
// @downloadURL https://raw.githubusercontent.com/tribalwarshelp/scripts/master/dist/extendedTribeProfile.js
|
2020-08-04 13:57:29 +00:00
|
|
|
// @version 1.0.8
|
2020-07-17 11:50:29 +00:00
|
|
|
// @description Extended Tribe Profile
|
|
|
|
// @author Kichiyaki http://dawid-wysokinski.pl/
|
2020-07-19 19:35:07 +00:00
|
|
|
// @match *://*/game.php*screen=info_ally*
|
2020-07-17 11:50:29 +00:00
|
|
|
// @grant none
|
|
|
|
// @run-at document-end
|
|
|
|
// ==/UserScript==
|
|
|
|
|
|
|
|
const SERVER = getCurrentServer();
|
|
|
|
const TRIBE_ID = getIDFromURL(window.location.search);
|
|
|
|
const LOCAL_STORAGE_KEY = 'kichiyaki_extended_tribe_profile' + TRIBE_ID;
|
|
|
|
const TRIBE_QUERY = `
|
2020-07-17 13:05:13 +00:00
|
|
|
query tribe($server: String!, $id: Int!, $playerFilter: PlayerFilter!, $dailyTribeStatsFilter: DailyTribeStatsFilter!) {
|
2020-07-17 11:50:29 +00:00
|
|
|
tribe(server: $server, id: $id) {
|
|
|
|
id
|
|
|
|
bestRank
|
|
|
|
bestRankAt
|
|
|
|
mostPoints
|
|
|
|
mostPointsAt
|
|
|
|
mostVillages
|
|
|
|
mostVillagesAt
|
|
|
|
createdAt
|
|
|
|
dominance
|
|
|
|
}
|
2020-07-17 12:16:07 +00:00
|
|
|
dailyTribeStats(server: $server, filter: $dailyTribeStatsFilter) {
|
|
|
|
items {
|
|
|
|
rank
|
|
|
|
rankAtt
|
|
|
|
rankDef
|
|
|
|
rankTotal
|
|
|
|
points
|
|
|
|
scoreAtt
|
|
|
|
scoreAtt
|
|
|
|
scoreDef
|
|
|
|
scoreTotal
|
|
|
|
villages
|
2020-07-17 14:13:09 +00:00
|
|
|
members
|
2020-07-17 12:16:07 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-17 13:05:13 +00:00
|
|
|
players(server: $server, filter: $playerFilter) {
|
|
|
|
items {
|
|
|
|
id
|
|
|
|
rankAtt
|
|
|
|
rankDef
|
|
|
|
rankSup
|
|
|
|
rankTotal
|
|
|
|
scoreAtt
|
|
|
|
scoreAtt
|
|
|
|
scoreDef
|
|
|
|
scoreSup
|
|
|
|
scoreTotal
|
|
|
|
dailyGrowth
|
|
|
|
}
|
|
|
|
}
|
2020-07-17 11:50:29 +00:00
|
|
|
}
|
|
|
|
`;
|
2020-07-17 13:43:28 +00:00
|
|
|
const ENNOBLEMENTS_QUERY = `
|
|
|
|
query ennoblements($server: String!, $filter: EnnoblementFilter!) {
|
|
|
|
ennoblements(server: $server, filter: $filter) {
|
|
|
|
total
|
|
|
|
items {
|
|
|
|
village {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
x
|
|
|
|
y
|
|
|
|
}
|
|
|
|
oldOwner {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
oldOwnerTribe {
|
|
|
|
id
|
|
|
|
tag
|
|
|
|
}
|
|
|
|
newOwner {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
newOwnerTribe {
|
|
|
|
id
|
|
|
|
tag
|
|
|
|
}
|
|
|
|
ennobledAt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
const ENNOBLEMENTS_PER_PAGE = 15;
|
2020-07-17 14:13:09 +00:00
|
|
|
const TRIBE_HISTORY_AND_TRIBE_DAILY_STATS_QUERY = `
|
|
|
|
query tribeHistoryAndTribeDailyStats($server: String!,
|
|
|
|
$tribeHistoryFilter: TribeHistoryFilter!,
|
|
|
|
$dailyTribeStatsFilter: DailyTribeStatsFilter!) {
|
|
|
|
tribeHistory(server: $server, filter: $tribeHistoryFilter) {
|
|
|
|
total
|
|
|
|
items {
|
|
|
|
totalVillages
|
|
|
|
points
|
|
|
|
rank
|
|
|
|
scoreAtt
|
|
|
|
rankAtt
|
|
|
|
scoreDef
|
|
|
|
rankDef
|
|
|
|
scoreTotal
|
|
|
|
rankTotal
|
|
|
|
createDate
|
|
|
|
totalMembers
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dailyTribeStats(server: $server, filter: $dailyTribeStatsFilter) {
|
|
|
|
items {
|
|
|
|
points
|
|
|
|
scoreAtt
|
|
|
|
scoreDef
|
|
|
|
scoreTotal
|
|
|
|
villages
|
|
|
|
createDate
|
|
|
|
members
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
const TRIBE_HISTORY_PER_PAGE = 15;
|
2020-07-17 19:44:35 +00:00
|
|
|
const TRIBE_MEMBERS_DAILY_STATS_QUERY = `
|
|
|
|
query tribeMembersDailyStats($server: String!,
|
|
|
|
$filter: DailyPlayerStatsFilter!) {
|
|
|
|
dailyPlayerStats(server: $server, filter: $filter) {
|
|
|
|
items {
|
|
|
|
player {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
points
|
|
|
|
scoreAtt
|
|
|
|
scoreDef
|
|
|
|
scoreSup
|
|
|
|
scoreTotal
|
|
|
|
villages
|
|
|
|
createDate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
let MEMBERS_GROWTH_MODE = 'points';
|
2020-07-18 10:02:35 +00:00
|
|
|
const TRIBE_CHANGES_QUERY = `
|
|
|
|
query tribeChanges($server: String!, $filter: TribeChangeFilter!) {
|
|
|
|
tribeChanges(server: $server, filter: $filter) {
|
|
|
|
total
|
|
|
|
items {
|
|
|
|
player {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
newTribe {
|
|
|
|
id
|
|
|
|
tag
|
|
|
|
}
|
|
|
|
createdAt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
const TRIBE_CHANGES_PAGINATION_CONTAINER_ID = 'tribeChangesPagination';
|
|
|
|
const TRIBE_CHANGES_PER_PAGE = 15;
|
2020-07-17 11:50:29 +00:00
|
|
|
const profileInfoTBody = document.querySelector(
|
|
|
|
'#content_value > table:nth-child(3) > tbody > tr > td:nth-child(1) > table > tbody'
|
|
|
|
);
|
2020-07-17 13:43:28 +00:00
|
|
|
const actionsContainer = profileInfoTBody;
|
2020-07-17 12:16:07 +00:00
|
|
|
const otherElementsContainer = document.querySelector(
|
|
|
|
'#content_value > table:nth-child(3) > tbody > tr > td:nth-child(2)'
|
|
|
|
);
|
2020-07-17 13:05:13 +00:00
|
|
|
const membersContainer = document.querySelector(
|
|
|
|
'#content_value > table.vis > tbody'
|
|
|
|
);
|
2020-07-22 15:31:27 +00:00
|
|
|
const translations = getTranslations();
|
2020-07-17 11:50:29 +00:00
|
|
|
|
|
|
|
const loadDataFromCache = () => {
|
|
|
|
return getItem(LOCAL_STORAGE_KEY);
|
|
|
|
};
|
|
|
|
|
|
|
|
const cacheTribeData = (data = {}) => {
|
|
|
|
setItem(LOCAL_STORAGE_KEY, data);
|
|
|
|
};
|
|
|
|
|
2020-08-04 08:49:09 +00:00
|
|
|
const getMemberIDs = () => {
|
2020-07-17 13:05:13 +00:00
|
|
|
const ids = [];
|
|
|
|
membersContainer.querySelectorAll('a').forEach((a) => {
|
|
|
|
const href = a.getAttribute('href');
|
|
|
|
if (href.includes('info_player')) {
|
|
|
|
ids.push(getIDFromURL(href));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return ids;
|
|
|
|
};
|
|
|
|
|
2020-08-04 08:49:09 +00:00
|
|
|
const getMemberNames = () => {
|
|
|
|
const ids = [];
|
|
|
|
membersContainer.querySelectorAll('a').forEach((a) => {
|
|
|
|
if (a.getAttribute('href').includes('info_player')) {
|
|
|
|
ids.push(a.innerText.trim());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return ids;
|
|
|
|
};
|
|
|
|
|
2020-07-17 11:50:29 +00:00
|
|
|
const loadData = async () => {
|
2020-08-04 08:49:09 +00:00
|
|
|
const memberIDs = getMemberIDs();
|
2020-07-17 11:50:29 +00:00
|
|
|
const data = await requestCreator({
|
|
|
|
query: TRIBE_QUERY,
|
|
|
|
variables: {
|
|
|
|
server: SERVER,
|
|
|
|
id: TRIBE_ID,
|
2020-07-17 12:16:07 +00:00
|
|
|
dailyTribeStatsFilter: {
|
|
|
|
sort: 'createDate DESC',
|
|
|
|
limit: 1,
|
|
|
|
tribeID: [TRIBE_ID],
|
|
|
|
},
|
2020-07-17 13:05:13 +00:00
|
|
|
playerFilter: {
|
|
|
|
sort: 'rank ASC',
|
2020-08-04 08:49:09 +00:00
|
|
|
limit: memberIDs.length,
|
|
|
|
id: memberIDs,
|
2020-07-17 13:05:13 +00:00
|
|
|
},
|
2020-07-17 11:50:29 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
cacheTribeData(data);
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
|
|
|
const renderTr = ({ title, data, id }) => {
|
|
|
|
let tr = document.querySelector('#' + id);
|
|
|
|
if (!tr) {
|
|
|
|
tr = document.createElement('tr');
|
|
|
|
tr.id = id;
|
|
|
|
tr.appendChild(document.createElement('td'));
|
|
|
|
tr.appendChild(document.createElement('td'));
|
|
|
|
profileInfoTBody.append(tr);
|
|
|
|
}
|
|
|
|
tr.children[0].innerHTML = title;
|
|
|
|
tr.children[1].innerHTML = data;
|
|
|
|
};
|
|
|
|
|
2020-07-17 13:05:13 +00:00
|
|
|
const extendMembersData = (players) => {
|
|
|
|
membersContainer.parentElement.style.width = '100%';
|
|
|
|
const heading = membersContainer.querySelector('tr:first-child');
|
|
|
|
if (heading.children.length !== 11) {
|
2020-07-22 15:31:27 +00:00
|
|
|
[
|
|
|
|
translations.oda,
|
|
|
|
translations.odd,
|
|
|
|
translations.ods,
|
|
|
|
translations.od,
|
|
|
|
translations.dailyGrowth,
|
|
|
|
translations.playerLinks,
|
|
|
|
].forEach((v) => {
|
2020-07-17 13:05:13 +00:00
|
|
|
const th = document.createElement('th');
|
|
|
|
th.innerHTML = v;
|
|
|
|
heading.appendChild(th);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
membersContainer.querySelectorAll('tr').forEach((tr) => {
|
|
|
|
const a = tr.querySelector('a');
|
|
|
|
if (!a) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const playerID = getIDFromURL(a.getAttribute('href'));
|
|
|
|
const player = players.items.find((p) => p.id === playerID);
|
|
|
|
if (player) {
|
|
|
|
[
|
|
|
|
[player.scoreAtt, player.rankAtt],
|
|
|
|
[player.scoreDef, player.rankDef],
|
|
|
|
[player.scoreSup, player.rankSup],
|
|
|
|
[player.scoreTotal, player.rankTotal],
|
|
|
|
player.dailyGrowth,
|
|
|
|
[formatPlayerURL(SERVER, player.id), 'TWStats'],
|
|
|
|
].forEach((data, index) => {
|
|
|
|
let td = tr.children[5 + index];
|
|
|
|
if (!td) {
|
|
|
|
td = document.createElement('td');
|
|
|
|
tr.appendChild(td);
|
|
|
|
}
|
|
|
|
if (Array.isArray(data)) {
|
|
|
|
if (typeof data[0] === 'number') {
|
|
|
|
td.innerHTML = `${data[0].toLocaleString()} (<strong>${
|
|
|
|
data[1]
|
|
|
|
}</strong>)`;
|
|
|
|
} else if (isURL(data[0])) {
|
2020-07-22 15:31:27 +00:00
|
|
|
td.innerHTML = `<a target="_blank" rel="noopener noreferrer" href="${data[0]}">${data[1]}</a>`;
|
2020-07-17 13:05:13 +00:00
|
|
|
}
|
|
|
|
} else if (typeof data === 'number') {
|
|
|
|
td.innerHTML = data.toLocaleString();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const render = ({ tribe, dailyTribeStats, players }) => {
|
2020-07-17 11:50:29 +00:00
|
|
|
[
|
|
|
|
{
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.createdAt + ':',
|
2020-07-17 11:50:29 +00:00
|
|
|
data: formatDate(tribe.createdAt),
|
|
|
|
id: 'created_at',
|
|
|
|
},
|
|
|
|
{
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.dominance + ':',
|
2020-07-17 11:50:29 +00:00
|
|
|
data: tribe.dominance.toFixed(2) + '%',
|
|
|
|
id: 'dominance',
|
|
|
|
},
|
|
|
|
{
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.bestRank + ':',
|
2020-07-17 11:50:29 +00:00
|
|
|
data: tribe.bestRank + ' ' + `(${formatDate(tribe.bestRankAt)})`,
|
|
|
|
id: 'best_rank',
|
|
|
|
},
|
|
|
|
{
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.mostPoints + ':',
|
2020-07-17 11:50:29 +00:00
|
|
|
data:
|
|
|
|
tribe.mostPoints.toLocaleString() +
|
|
|
|
' ' +
|
|
|
|
`(${formatDate(tribe.mostPointsAt)})`,
|
|
|
|
id: 'most_points',
|
|
|
|
},
|
|
|
|
{
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.mostVillages + ':',
|
2020-07-17 11:50:29 +00:00
|
|
|
data: tribe.mostVillages + ' ' + `(${formatDate(tribe.mostVillagesAt)})`,
|
|
|
|
id: 'most_villages',
|
|
|
|
},
|
|
|
|
].forEach((data) => {
|
|
|
|
renderTr(data);
|
|
|
|
});
|
2020-07-17 12:16:07 +00:00
|
|
|
|
|
|
|
if (dailyTribeStats && dailyTribeStats.items.length > 0) {
|
|
|
|
renderTodaysStats(otherElementsContainer, dailyTribeStats.items[0]);
|
|
|
|
}
|
2020-07-17 13:05:13 +00:00
|
|
|
|
|
|
|
if (players && players.items.length > 0) {
|
|
|
|
extendMembersData(players);
|
|
|
|
}
|
2020-07-17 11:50:29 +00:00
|
|
|
};
|
|
|
|
|
2020-07-17 13:43:28 +00:00
|
|
|
const handleShowTribeEnnoblementsClick = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const page = getPage(e.target);
|
|
|
|
if (!isNaN(page)) {
|
|
|
|
const data = await requestCreator({
|
|
|
|
query: ENNOBLEMENTS_QUERY,
|
|
|
|
variables: {
|
|
|
|
filter: {
|
|
|
|
or: {
|
|
|
|
oldOwnerTribeID: [TRIBE_ID],
|
|
|
|
newOwnerTribeID: [TRIBE_ID],
|
|
|
|
},
|
|
|
|
offset: ENNOBLEMENTS_PER_PAGE * (page - 1),
|
|
|
|
limit: ENNOBLEMENTS_PER_PAGE,
|
|
|
|
sort: 'ennobledAt DESC',
|
|
|
|
},
|
|
|
|
server: SERVER,
|
|
|
|
},
|
|
|
|
});
|
2020-07-20 12:07:57 +00:00
|
|
|
showEnnoblementsPopup(e, data.ennoblements, {
|
2020-07-17 13:43:28 +00:00
|
|
|
currentPage: page,
|
|
|
|
limit: ENNOBLEMENTS_PER_PAGE,
|
|
|
|
onPageChange: handleShowTribeEnnoblementsClick,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-17 14:13:09 +00:00
|
|
|
const handleShowTribeHistoryClick = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const page = getPage(e.target);
|
|
|
|
if (!isNaN(page)) {
|
|
|
|
try {
|
|
|
|
const filter = {
|
|
|
|
tribeID: [TRIBE_ID],
|
|
|
|
offset: TRIBE_HISTORY_PER_PAGE * (page - 1),
|
|
|
|
limit: TRIBE_HISTORY_PER_PAGE,
|
|
|
|
sort: 'createDate DESC',
|
|
|
|
};
|
|
|
|
const { tribeHistory, dailyTribeStats } = await requestCreator({
|
|
|
|
query: TRIBE_HISTORY_AND_TRIBE_DAILY_STATS_QUERY,
|
|
|
|
variables: {
|
|
|
|
server: SERVER,
|
|
|
|
tribeHistoryFilter: filter,
|
|
|
|
dailyTribeStatsFilter: {
|
|
|
|
...filter,
|
|
|
|
offset: filter.offset + 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2020-07-20 12:07:57 +00:00
|
|
|
showHistoryPopup(e, tribeHistory, dailyTribeStats, {
|
2020-07-17 14:13:09 +00:00
|
|
|
currentPage: page,
|
|
|
|
limit: TRIBE_HISTORY_PER_PAGE,
|
|
|
|
tribe: true,
|
|
|
|
onPageChange: handleShowTribeHistoryClick,
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.log('cannot load tribe history', error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-17 19:44:35 +00:00
|
|
|
const getMembersGrowthTdStyle = (value) => {
|
|
|
|
const statIncreaseStyle = 'color: #000; background-color: #0f0';
|
|
|
|
const statDecreaseStyle = 'color: #000; background-color: #f00';
|
|
|
|
const defaultStyle = 'color: #000; background-color: #808080';
|
|
|
|
|
|
|
|
return value > 0
|
|
|
|
? statIncreaseStyle
|
|
|
|
: value < 0
|
|
|
|
? statDecreaseStyle
|
|
|
|
: defaultStyle;
|
|
|
|
};
|
|
|
|
|
|
|
|
const mapMembersGrowthTdValue = (i) => {
|
|
|
|
switch (MEMBERS_GROWTH_MODE) {
|
|
|
|
case 'points':
|
|
|
|
return i.points;
|
|
|
|
case 'villages':
|
|
|
|
return i.villages;
|
|
|
|
case 'od':
|
|
|
|
return i.scoreTotal;
|
|
|
|
case 'oda':
|
|
|
|
return i.scoreAtt;
|
|
|
|
case 'odd':
|
|
|
|
return i.scoreDef;
|
|
|
|
case 'ods':
|
|
|
|
return i.scoreSup;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const buildMembersGrowthTBody = (stats) => {
|
|
|
|
const dates = [
|
|
|
|
...new Set(stats.items.map((item) => item.createDate)),
|
|
|
|
].reverse();
|
|
|
|
|
|
|
|
return `
|
|
|
|
<tbody>
|
|
|
|
<tr>
|
2020-07-22 15:31:27 +00:00
|
|
|
<th>${translations.player}</th>
|
2020-07-17 19:44:35 +00:00
|
|
|
${dates
|
|
|
|
.map((date) => {
|
|
|
|
return `<th>${formatDate(date, {
|
|
|
|
year: 'numeric',
|
|
|
|
month: '2-digit',
|
|
|
|
day: '2-digit',
|
|
|
|
})}</th>`;
|
|
|
|
})
|
|
|
|
.join('')}
|
2020-07-22 15:31:27 +00:00
|
|
|
<th>${translations.total}</th>
|
2020-07-17 19:44:35 +00:00
|
|
|
</tr>
|
2020-08-04 08:49:09 +00:00
|
|
|
${getMemberIDs()
|
2020-07-17 19:44:35 +00:00
|
|
|
.map((id) => {
|
|
|
|
const filtered = stats.items
|
|
|
|
.filter((item) => item.player && item.player.id === id)
|
|
|
|
.reverse();
|
|
|
|
let player = undefined;
|
|
|
|
if (filtered.length > 0) {
|
|
|
|
player = filtered[0].player;
|
|
|
|
}
|
|
|
|
const tds = [];
|
|
|
|
let total = 0;
|
|
|
|
for (let date of dates) {
|
|
|
|
const i = filtered.find((i) => i.createDate === date);
|
|
|
|
let val = 0;
|
|
|
|
if (i) {
|
|
|
|
val = mapMembersGrowthTdValue(i);
|
|
|
|
}
|
|
|
|
total += val;
|
|
|
|
tds.push(
|
2020-07-18 09:35:23 +00:00
|
|
|
`<td style="${getMembersGrowthTdStyle(
|
|
|
|
val
|
|
|
|
)}">${val.toLocaleString()}</td>`
|
2020-07-17 19:44:35 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
return `<tr>
|
|
|
|
<td>
|
|
|
|
${
|
|
|
|
player
|
|
|
|
? `<a href="${formatPlayerURLTribalWars(id)}">${
|
|
|
|
player.name
|
|
|
|
}</a>`
|
|
|
|
: '-'
|
|
|
|
}
|
|
|
|
</td>
|
|
|
|
${tds.join('')}
|
2020-07-18 09:35:23 +00:00
|
|
|
<td style="${getMembersGrowthTdStyle(
|
|
|
|
total
|
|
|
|
)}"><strong>${total.toLocaleString()}</strong></td>
|
2020-07-17 19:44:35 +00:00
|
|
|
</tr>`;
|
|
|
|
})
|
|
|
|
.join('')}
|
|
|
|
</tbody>
|
|
|
|
`;
|
|
|
|
};
|
|
|
|
|
|
|
|
const MEMBERS_GROWTH_TABLE_ID = 'membersGrowth';
|
|
|
|
const MEMBERS_GROWTH_FORM = MEMBERS_GROWTH_TABLE_ID + 'Form';
|
|
|
|
|
|
|
|
const createChangeTypeHandler = (stats) => (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
MEMBERS_GROWTH_MODE = e.target[0].value;
|
|
|
|
document.querySelector(
|
|
|
|
'#' + MEMBERS_GROWTH_TABLE_ID
|
|
|
|
).innerHTML = buildMembersGrowthTBody(stats);
|
|
|
|
};
|
|
|
|
|
|
|
|
const renderMembersGrowthPopup = (e, stats) => {
|
|
|
|
const formOptions = [
|
2020-07-22 15:31:27 +00:00
|
|
|
['points', translations.points],
|
|
|
|
['villages', translations.villages],
|
|
|
|
['od', translations.opponentsDefeated],
|
|
|
|
['oda', translations.opponentsDefeatedAsAttacker],
|
|
|
|
['odd', translations.opponentsDefeatedAsDefender],
|
|
|
|
['ods', translations.opponentsDefeatedAsSupporter],
|
2020-07-18 09:35:23 +00:00
|
|
|
].map(
|
|
|
|
(v) =>
|
|
|
|
`<option ${
|
|
|
|
MEMBERS_GROWTH_MODE === v[0] ? 'selected="selected"' : ''
|
|
|
|
} value="${v[0]}">${v[1]}</option>`
|
|
|
|
);
|
2020-07-17 19:44:35 +00:00
|
|
|
const html = `
|
|
|
|
<form id="${MEMBERS_GROWTH_FORM}">
|
|
|
|
<select>
|
|
|
|
${formOptions.join('')}
|
|
|
|
</select>
|
2020-07-22 15:31:27 +00:00
|
|
|
<button type="submit">${translations.change}</button>
|
2020-07-17 19:44:35 +00:00
|
|
|
</form>
|
|
|
|
<table id="${MEMBERS_GROWTH_TABLE_ID}" class="vis" style="border-collapse: separate; border-spacing: 2px; width: 100%;">
|
|
|
|
${buildMembersGrowthTBody(stats)}
|
|
|
|
</table>
|
|
|
|
`;
|
|
|
|
|
2020-07-20 12:07:57 +00:00
|
|
|
showPopup({
|
2020-07-17 19:44:35 +00:00
|
|
|
e,
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.membersGrowth,
|
2020-07-17 19:44:35 +00:00
|
|
|
id: 'mg',
|
|
|
|
html,
|
|
|
|
});
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector('#' + MEMBERS_GROWTH_FORM)
|
|
|
|
.addEventListener('submit', createChangeTypeHandler(stats));
|
|
|
|
};
|
|
|
|
|
|
|
|
const loadMembersGrowthData = async ({ createDateLTE, createDateGT } = {}) => {
|
2020-08-04 08:49:09 +00:00
|
|
|
const memberIDs = getMemberIDs();
|
2020-07-17 19:44:35 +00:00
|
|
|
const limit =
|
2020-08-04 08:49:09 +00:00
|
|
|
memberIDs.length * differenceInDays(createDateLTE, createDateGT);
|
2020-07-17 19:44:35 +00:00
|
|
|
const filter = {
|
2020-08-04 08:49:09 +00:00
|
|
|
playerID: memberIDs,
|
2020-07-17 19:44:35 +00:00
|
|
|
limit,
|
|
|
|
sort: 'createDate DESC',
|
|
|
|
createDateLTE,
|
|
|
|
createDateGT,
|
|
|
|
};
|
|
|
|
const data = await requestCreator({
|
|
|
|
query: TRIBE_MEMBERS_DAILY_STATS_QUERY,
|
|
|
|
variables: {
|
|
|
|
filter,
|
|
|
|
server: SERVER,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleShowMembersGrowthClick = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const createDateGT = new Date();
|
|
|
|
createDateGT.setDate(createDateGT.getDate() - 7);
|
|
|
|
const data = await loadMembersGrowthData({
|
|
|
|
createDateLTE: new Date(),
|
|
|
|
createDateGT,
|
|
|
|
});
|
|
|
|
renderMembersGrowthPopup(e, data.dailyPlayerStats);
|
|
|
|
};
|
|
|
|
|
2020-07-18 10:02:35 +00:00
|
|
|
const renderTribeChanges = (e, currentPage, tribeChanges) => {
|
|
|
|
const paginationItems = generatePaginationItems({
|
|
|
|
total: tribeChanges.total,
|
|
|
|
limit: TRIBE_CHANGES_PER_PAGE,
|
|
|
|
currentPage,
|
|
|
|
});
|
|
|
|
|
|
|
|
const html = `
|
|
|
|
<div style="${getContainerStyles()}" id="${TRIBE_CHANGES_PAGINATION_CONTAINER_ID}">
|
|
|
|
${paginationItems.join('')}
|
|
|
|
</div>
|
|
|
|
<table class="vis" style="border-collapse: separate; border-spacing: 2px; width: 100%;">
|
|
|
|
<tbody>
|
|
|
|
<tr>
|
|
|
|
<th>
|
2020-07-22 15:31:27 +00:00
|
|
|
${translations.date}
|
2020-07-18 10:02:35 +00:00
|
|
|
</th>
|
|
|
|
<th>
|
2020-07-22 15:31:27 +00:00
|
|
|
${translations.player}
|
2020-07-18 10:02:35 +00:00
|
|
|
</th>
|
|
|
|
<th>
|
2020-07-22 15:31:27 +00:00
|
|
|
${translations.act}
|
2020-07-18 10:02:35 +00:00
|
|
|
</th>
|
|
|
|
</tr>
|
|
|
|
${tribeChanges.items
|
|
|
|
.map((tribeChange) => {
|
|
|
|
let rowHTML =
|
|
|
|
'<tr>' + `<td>${formatDate(tribeChange.createdAt)}</td>`;
|
|
|
|
if (tribeChange.player) {
|
|
|
|
rowHTML += `<td><a href="${formatPlayerURLTribalWars(
|
|
|
|
tribeChange.player.id
|
|
|
|
)}">${tribeChange.player.name}</a></td>`;
|
|
|
|
} else {
|
|
|
|
rowHTML += '<td>-</td>';
|
|
|
|
}
|
|
|
|
rowHTML += `<td><strong>${
|
|
|
|
tribeChange.newTribe && tribeChange.newTribe.id === TRIBE_ID
|
2020-07-22 15:31:27 +00:00
|
|
|
? translations.joined
|
|
|
|
: translations.left
|
2020-07-18 10:02:35 +00:00
|
|
|
}</strong></td>`;
|
|
|
|
return rowHTML + '</tr>';
|
|
|
|
})
|
|
|
|
.join('')}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
`;
|
|
|
|
|
2020-07-20 12:07:57 +00:00
|
|
|
showPopup({
|
2020-07-18 10:02:35 +00:00
|
|
|
e,
|
2020-07-22 15:31:27 +00:00
|
|
|
title: translations.tribeChanges,
|
2020-07-18 10:02:35 +00:00
|
|
|
id: 'tribeChanges',
|
|
|
|
html,
|
|
|
|
});
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelectorAll('#' + TRIBE_CHANGES_PAGINATION_CONTAINER_ID + ' a')
|
|
|
|
.forEach((el) => {
|
|
|
|
el.addEventListener('click', handleShowTribeChangesClick);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleShowTribeChangesClick = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const page = getPage(e.target);
|
|
|
|
if (!isNaN(page)) {
|
|
|
|
const data = await requestCreator({
|
|
|
|
query: TRIBE_CHANGES_QUERY,
|
|
|
|
variables: {
|
|
|
|
filter: {
|
|
|
|
or: {
|
|
|
|
oldTribeID: [TRIBE_ID],
|
|
|
|
newTribeID: [TRIBE_ID],
|
|
|
|
},
|
|
|
|
offset: TRIBE_CHANGES_PER_PAGE * (page - 1),
|
|
|
|
limit: TRIBE_CHANGES_PER_PAGE,
|
|
|
|
sort: 'createdAt DESC',
|
|
|
|
},
|
|
|
|
server: SERVER,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
renderTribeChanges(e, page, data.tribeChanges);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-04 08:49:09 +00:00
|
|
|
const handleGenerateMailingListClick = (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const members = getMemberNames();
|
|
|
|
const chunks = [];
|
|
|
|
for (let i = 0; i < members.length; i += 50) {
|
|
|
|
chunks.push(members.slice(i, i + 50));
|
|
|
|
}
|
|
|
|
|
|
|
|
let html = '';
|
|
|
|
chunks.forEach((names, index) => {
|
|
|
|
html += `<h3 style="margin-bottom: 5px;">${index + 1}.</h3>
|
|
|
|
<textarea cols=30 rows=8 readonly style="margin-bottom: 15px;">${names.join(
|
|
|
|
';'
|
|
|
|
)}</textarea>`;
|
|
|
|
});
|
|
|
|
|
|
|
|
Dialog.show('mailinglist', html);
|
|
|
|
};
|
|
|
|
|
2020-08-04 13:57:29 +00:00
|
|
|
const loadVillages = async (variables, total = false) => {
|
|
|
|
try {
|
|
|
|
const data = await requestCreator({
|
|
|
|
variables,
|
|
|
|
query: `
|
|
|
|
query villages($server: String!, $filter: VillageFilter!) {
|
|
|
|
villages(server: $server, filter: $filter) {
|
|
|
|
${total ? 'total' : ''}
|
|
|
|
items {
|
|
|
|
id
|
|
|
|
x
|
|
|
|
y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
});
|
|
|
|
if (data && data.villages && Array.isArray(data.villages.items)) {
|
|
|
|
return data.villages;
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.log('load villages', error);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
total: 0,
|
|
|
|
items: [],
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const showLoadingDialog = (current = 0, total = 0) => {
|
|
|
|
if (!current || !total) {
|
|
|
|
return Dialog.show('loading', '<strong>Loading...</strong>');
|
|
|
|
}
|
|
|
|
return Dialog.show(
|
|
|
|
'loading',
|
|
|
|
`Loaded: <strong>${current}</strong>/<strong>${total}</strong>`
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleExportTribeVillagesFormSubmit = async (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
let limit = parseInt(e.target[4].value);
|
|
|
|
const variables = {
|
|
|
|
filter: {
|
|
|
|
xLTE: parseInt(e.target[0].value),
|
|
|
|
xGTE: parseInt(e.target[1].value),
|
|
|
|
yLTE: parseInt(e.target[2].value),
|
|
|
|
yGTE: parseInt(e.target[3].value),
|
|
|
|
limit: isNaN(limit) || !limit ? 0 : limit,
|
|
|
|
playerID: getMemberIDs(),
|
|
|
|
sort: 'id ASC',
|
|
|
|
},
|
|
|
|
server: SERVER,
|
|
|
|
};
|
|
|
|
showLoadingDialog();
|
|
|
|
|
|
|
|
let { total, items } = await loadVillages(variables, true);
|
|
|
|
const length = items.length;
|
|
|
|
if (limit !== 0 && limit < total) {
|
|
|
|
total = limit;
|
|
|
|
}
|
|
|
|
if (isNaN(limit) || !limit || limit > length) {
|
|
|
|
for (let offset = length; offset < total; offset += length) {
|
|
|
|
showLoadingDialog(offset, total);
|
|
|
|
const more = await loadVillages({
|
|
|
|
...variables,
|
|
|
|
filter: {
|
|
|
|
...variables.filter,
|
|
|
|
offset,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
items = [...items, ...more.items];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Dialog.show(
|
|
|
|
'exportTribeVillages',
|
|
|
|
`
|
|
|
|
<textarea cols=60 rows=8 readonly>${items
|
|
|
|
.map((item) => `${item.x}|${item.y}`)
|
|
|
|
.join(' ')}</textarea>
|
|
|
|
`
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleExportTribeVillagesClick = (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
const FORM_ID = 'etvForm';
|
|
|
|
|
|
|
|
const html = `
|
|
|
|
<div style="display: flex; align-items: center; justify-content: center;">
|
|
|
|
<form id="${FORM_ID}">
|
|
|
|
<div>
|
|
|
|
<label>X <= </label>
|
|
|
|
<input type="number" min="0" value="1000" required />
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<label>X >= </label>
|
|
|
|
<input type="number" min="0" value="0" required />
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<label>Y <= </label>
|
|
|
|
<input type="number" min="0" value="1000" required />
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<label>Y >= </label>
|
|
|
|
<input type="number" min="0" value="0" required />
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<label>Limit: </label>
|
|
|
|
<input type="number" min="0" value="0" required />
|
|
|
|
</div>
|
|
|
|
<button type="submit">Export</button>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
|
|
|
Dialog.show('exportTribeVillages', html);
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector('#' + FORM_ID)
|
|
|
|
.addEventListener('submit', handleExportTribeVillagesFormSubmit);
|
|
|
|
};
|
|
|
|
|
2020-07-17 13:43:28 +00:00
|
|
|
const wrapAction = (action) => {
|
|
|
|
const actionWrapperTd = document.createElement('td');
|
|
|
|
actionWrapperTd.colSpan = '2';
|
|
|
|
actionWrapperTd.append(action);
|
|
|
|
const actionWrapperTr = document.createElement('tr');
|
|
|
|
actionWrapperTr.appendChild(actionWrapperTd);
|
|
|
|
return actionWrapperTr;
|
|
|
|
};
|
|
|
|
|
|
|
|
const renderActions = () => {
|
|
|
|
const showEnnoblements = document.createElement('a');
|
|
|
|
showEnnoblements.href = '#';
|
|
|
|
setPage(showEnnoblements, '1');
|
2020-07-22 15:31:27 +00:00
|
|
|
showEnnoblements.innerHTML = translations.action.showEnnoblements;
|
2020-07-17 13:43:28 +00:00
|
|
|
showEnnoblements.addEventListener('click', handleShowTribeEnnoblementsClick);
|
|
|
|
actionsContainer.appendChild(wrapAction(showEnnoblements));
|
2020-07-17 14:13:09 +00:00
|
|
|
|
|
|
|
const showHistory = document.createElement('a');
|
|
|
|
showHistory.href = '#';
|
|
|
|
setPage(showHistory, '1');
|
2020-07-22 15:31:27 +00:00
|
|
|
showHistory.innerHTML = translations.action.showHistory;
|
2020-07-17 14:13:09 +00:00
|
|
|
showHistory.addEventListener('click', handleShowTribeHistoryClick);
|
|
|
|
actionsContainer.appendChild(wrapAction(showHistory));
|
2020-07-17 19:44:35 +00:00
|
|
|
|
2020-07-18 10:02:35 +00:00
|
|
|
const showTribeChanges = document.createElement('a');
|
|
|
|
showTribeChanges.href = '#';
|
|
|
|
setPage(showTribeChanges, '1');
|
2020-07-22 15:31:27 +00:00
|
|
|
showTribeChanges.innerHTML = translations.action.showTribeChanges;
|
2020-07-18 10:02:35 +00:00
|
|
|
showTribeChanges.addEventListener('click', handleShowTribeChangesClick);
|
|
|
|
actionsContainer.appendChild(wrapAction(showTribeChanges));
|
|
|
|
|
2020-07-17 19:44:35 +00:00
|
|
|
const showMembersGrowth = document.createElement('a');
|
|
|
|
showMembersGrowth.href = '#';
|
2020-07-22 15:31:27 +00:00
|
|
|
showMembersGrowth.innerHTML = translations.action.showMembersGrowth;
|
2020-07-17 19:44:35 +00:00
|
|
|
showMembersGrowth.addEventListener('click', handleShowMembersGrowthClick);
|
|
|
|
actionsContainer.appendChild(wrapAction(showMembersGrowth));
|
2020-08-04 08:49:09 +00:00
|
|
|
|
|
|
|
const generateMailingList = document.createElement('a');
|
|
|
|
generateMailingList.href = '#';
|
|
|
|
generateMailingList.innerHTML = translations.action.generateMailingList;
|
|
|
|
generateMailingList.addEventListener('click', handleGenerateMailingListClick);
|
|
|
|
actionsContainer.appendChild(wrapAction(generateMailingList));
|
2020-08-04 13:57:29 +00:00
|
|
|
|
|
|
|
const exportVillages = document.createElement('a');
|
|
|
|
exportVillages.href = '#';
|
|
|
|
exportVillages.innerHTML = translations.action.exportVillages;
|
|
|
|
exportVillages.addEventListener('click', handleExportTribeVillagesClick);
|
|
|
|
actionsContainer.appendChild(wrapAction(exportVillages));
|
2020-07-17 13:43:28 +00:00
|
|
|
};
|
|
|
|
|
2020-07-17 11:50:29 +00:00
|
|
|
(async function () {
|
|
|
|
try {
|
2020-07-17 13:05:13 +00:00
|
|
|
document.querySelector('#content_value > table:nth-child(3)').style.width =
|
|
|
|
'100%';
|
2020-07-20 12:07:57 +00:00
|
|
|
|
2020-07-17 13:43:28 +00:00
|
|
|
renderActions();
|
2020-07-17 13:05:13 +00:00
|
|
|
|
2020-07-17 11:50:29 +00:00
|
|
|
const dataFromCache = loadDataFromCache();
|
|
|
|
if (dataFromCache && dataFromCache.tribe) {
|
|
|
|
render(dataFromCache);
|
|
|
|
}
|
|
|
|
|
|
|
|
const dataFromAPI = await loadData();
|
|
|
|
if (dataFromAPI) {
|
|
|
|
render(dataFromAPI);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.log('extended tribe profile', error);
|
|
|
|
}
|
|
|
|
})();
|