From 33b0c1e7ce92c2054b4276b7464a28a29d5844f6 Mon Sep 17 00:00:00 2001 From: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Sat, 7 Dec 2024 11:54:13 +0200 Subject: [PATCH] Make calendar translate on the fly Affects issues: - #3668 - #3346 --- .../json/graphs/GraphJSONCreator.java | 16 ++--- .../json/graphs/calendar/CalendarEntry.java | 15 +++-- .../json/graphs/calendar/CalendarFactory.java | 2 +- .../json/graphs/calendar/PlayerCalendar.java | 17 +++-- .../json/graphs/calendar/ServerCalendar.java | 65 ++++++------------- .../calendar/PlayerSessionCalendar.jsx | 37 ++++++++++- .../components/calendar/ServerCalendar.jsx | 26 +++++++- 7 files changed, 109 insertions(+), 69 deletions(-) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java index 7cf745202..d6160f0a3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONCreator.java @@ -282,7 +282,7 @@ public class GraphJSONCreator { return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.HOURS.toMillis(1L)); } - public String serverCalendarJSON(ServerUUID serverUUID) { + public Map serverCalendarJSON(ServerUUID serverUUID) { Database db = dbSystem.getDatabase(); long now = System.currentTimeMillis(); long twoYearsAgo = now - TimeUnit.DAYS.toMillis(730L); @@ -299,17 +299,17 @@ public class GraphJSONCreator { NavigableMap sessionsPerDay = db.query( SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZoneOffset, serverUUID) ); - return "{\"data\":" + + return Map.of("data", graphs.calendar().serverCalendar( uniquePerDay, newPerDay, playtimePerDay, sessionsPerDay - ).toCalendarSeries() + - ",\"firstDay\":" + 1 + '}'; + ).getEntries(), + "firstDay", 1); } - public String networkCalendarJSON() { + public Map networkCalendarJSON() { Database db = dbSystem.getDatabase(); long now = System.currentTimeMillis(); long twoYearsAgo = now - TimeUnit.DAYS.toMillis(730L); @@ -326,14 +326,14 @@ public class GraphJSONCreator { NavigableMap sessionsPerDay = db.query( SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZoneOffset) ); - return "{\"data\":" + + return Map.of("data", graphs.calendar().serverCalendar( uniquePerDay, newPerDay, playtimePerDay, sessionsPerDay - ).toCalendarSeries() + - ",\"firstDay\":" + 1 + '}'; + ).getEntries(), + "firstDay", 1); } public Map serverWorldPieJSONAsMap(ServerUUID serverUUID) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java index 24571e84f..17ccc6c5b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java @@ -27,17 +27,19 @@ import java.util.Optional; public class CalendarEntry { private final String title; + private final Serializable value; private final Serializable start; private Serializable end; private String color; - private CalendarEntry(String title, Serializable start) { + private CalendarEntry(String title, Serializable value, Serializable start) { this.title = title; + this.value = value; this.start = start; } - public static CalendarEntry of(String title, Serializable start) { - return new CalendarEntry(title, start); + public static CalendarEntry of(String title, Serializable value, Serializable start) { + return new CalendarEntry(title, value, start); } public CalendarEntry withEnd(Serializable end) { @@ -50,10 +52,14 @@ public class CalendarEntry { return this; } - public String getTitle() { + public Serializable getTitle() { return title; } + public Serializable getValue() { + return value; + } + public Serializable getStart() { return start; } @@ -70,6 +76,7 @@ public class CalendarEntry { public String toString() { return "{" + "title:'" + title + '\'' + + ", value:" + value + ", start:'" + start + '\'' + (end != null ? ", end='" + end + '\'' : "") + (color != null ? ", color='" + color + '\'' : "") + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java index 77621b595..eaf1917d3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java @@ -68,7 +68,7 @@ public class CalendarFactory { ) { return new ServerCalendar( uniquePerDay, newPerDay, playtimePerDay, sessionsPerDay, - formatters.iso8601NoClockTZIndependentLong(), formatters.timeAmount(), theme, locale + formatters.iso8601NoClockTZIndependentLong(), theme ); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java index d2bff1e3b..d3b1b62c2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java @@ -72,9 +72,8 @@ public class PlayerCalendar { List entries = new ArrayList<>(); entries.add(CalendarEntry - .of(locale.getString(HtmlLang.LABEL_REGISTERED) + ": " + year.apply(registered), - registered - ).withColor(theme.getValue(ThemeVal.LIGHT_GREEN)) + .of(HtmlLang.LABEL_REGISTERED.getKey(), registered, registered + timeZone.getOffset(registered)) + .withColor(theme.getValue(ThemeVal.LIGHT_GREEN)) ); Map> sessionsByDay = getSessionsByDay(); @@ -87,30 +86,30 @@ public class PlayerCalendar { long playtime = sessions.stream().mapToLong(FinishedSession::getLength).sum(); entries.add(CalendarEntry - .of(locale.getString(HtmlLang.LABEL_PLAYTIME) + ": " + timeAmount.apply(playtime), day) + .of(HtmlLang.LABEL_PLAYTIME.getKey(), playtime, day) .withColor(theme.getValue(ThemeVal.GREEN)) ); - entries.add(CalendarEntry.of(locale.getString(HtmlLang.SIDE_SESSIONS) + ": " + sessionCount, day)); + entries.add(CalendarEntry.of(HtmlLang.SIDE_SESSIONS.getKey(), sessionCount, day) + .withColor(theme.getValue(ThemeVal.TEAL))); } long fiveMinutes = TimeUnit.MINUTES.toMillis(5L); for (FinishedSession session : allSessions) { - String length = timeAmount.apply(session.getLength()); long start = session.getStart(); long end = session.getEnd(); entries.add(CalendarEntry - .of(length + " " + locale.getString(HtmlLang.SESSION), - start + timeZone.getOffset(start)) + .of(HtmlLang.SESSION.getKey(), session.getLength(), start + timeZone.getOffset(start)) .withEnd(end + timeZone.getOffset(end)) + .withColor(theme.getValue(ThemeVal.TEAL)) ); for (PlayerKill kill : session.getExtraData(PlayerKills.class).map(PlayerKills::asList).orElseGet(ArrayList::new)) { long time = kill.getDate(); String victim = kill.getVictim().getName(); entries.add(CalendarEntry - .of(locale.getString(HtmlLang.KILLED) + ": " + victim, time) + .of(HtmlLang.KILLED.getKey(), victim, time) .withEnd(time + fiveMinutes) .withColor(theme.getValue(ThemeVal.RED)) ); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java index 1837bbbb0..bd76a6bbe 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java @@ -17,14 +17,11 @@ package com.djrapitops.plan.delivery.rendering.json.graphs.calendar; import com.djrapitops.plan.delivery.formatting.Formatter; -import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.HtmlLang; import com.djrapitops.plan.settings.theme.Theme; import com.djrapitops.plan.settings.theme.ThemeVal; -import java.util.Map; -import java.util.NavigableMap; -import java.util.SortedMap; +import java.util.*; /** * Utility for creating FullCalendar calendar event array on Player page. @@ -39,9 +36,7 @@ public class ServerCalendar { private final SortedMap playtimePerDay; private final Formatter iso8601TZIndependent; - private final Formatter timeAmount; private final Theme theme; - private final Locale locale; ServerCalendar( SortedMap uniquePerDay, @@ -49,37 +44,26 @@ public class ServerCalendar { SortedMap playtimePerDay, NavigableMap sessionsPerDay, Formatter iso8601TZIndependent, - Formatter timeAmount, - Theme theme, - Locale locale + Theme theme ) { this.uniquePerDay = uniquePerDay; this.newPerDay = newPerDay; this.iso8601TZIndependent = iso8601TZIndependent; - this.timeAmount = timeAmount; this.sessionsPerDay = sessionsPerDay; this.playtimePerDay = playtimePerDay; this.theme = theme; - this.locale = locale; } - public String toCalendarSeries() { - StringBuilder series = new StringBuilder("["); - - series.append("{\"title\": \"badcode\",\"start\":0}"); - appendTimeZoneOffsetData(series); - - return series.append("]").toString(); + public List getEntries() { + List entries = new ArrayList<>(); + appendUniquePlayers(entries); + appendNewPlayers(entries); + appendSessionCounts(entries); + appendPlaytime(entries); + return entries; } - private void appendTimeZoneOffsetData(StringBuilder series) { - appendUniquePlayers(series); - appendNewPlayers(series); - appendSessionCounts(series); - appendPlaytime(series); - } - - private void appendNewPlayers(StringBuilder series) { + private void appendNewPlayers(List entries) { for (Map.Entry entry : newPerDay.entrySet()) { int newPlayers = entry.getValue(); if (newPlayers <= 0) { @@ -89,14 +73,12 @@ public class ServerCalendar { Long key = entry.getKey(); String day = iso8601TZIndependent.apply(key); - series.append(",{\"title\": \"").append(locale.get(HtmlLang.NEW_CALENDAR)).append(" ").append(newPlayers) - .append("\",\"start\":\"").append(day) - .append("\",\"color\": \"").append(theme.getValue(ThemeVal.LIGHT_GREEN)).append('"') - .append("}"); + entries.add(CalendarEntry.of(HtmlLang.NEW_CALENDAR.getKey(), newPlayers, day) + .withColor(theme.getValue(ThemeVal.LIGHT_GREEN))); } } - private void appendUniquePlayers(StringBuilder series) { + private void appendUniquePlayers(List entries) { for (Map.Entry entry : uniquePerDay.entrySet()) { long uniquePlayers = entry.getValue(); if (uniquePlayers <= 0) { @@ -106,14 +88,11 @@ public class ServerCalendar { Long key = entry.getKey(); String day = iso8601TZIndependent.apply(key); - series.append(",{\"title\": \"").append(locale.get(HtmlLang.UNIQUE_CALENDAR)).append(" ").append(uniquePlayers) - .append("\",\"start\":\"").append(day) - .append("\"}"); - + entries.add(CalendarEntry.of(HtmlLang.UNIQUE_CALENDAR.getKey(), uniquePlayers, day)); } } - private void appendPlaytime(StringBuilder series) { + private void appendPlaytime(List entries) { for (Map.Entry entry : playtimePerDay.entrySet()) { long playtime = entry.getValue(); if (playtime <= 0) { @@ -122,14 +101,12 @@ public class ServerCalendar { Long key = entry.getKey(); String day = iso8601TZIndependent.apply(key); - series.append(",{\"title\": \"").append(locale.get(HtmlLang.LABEL_PLAYTIME)).append(": ").append(timeAmount.apply(playtime)) - .append("\",\"start\":\"").append(day) - .append("\",\"color\": \"").append(theme.getValue(ThemeVal.GREEN)).append('"') - .append("}"); + entries.add(CalendarEntry.of(HtmlLang.LABEL_PLAYTIME.getKey(), playtime, day) + .withColor(theme.getValue(ThemeVal.GREEN))); } } - private void appendSessionCounts(StringBuilder series) { + private void appendSessionCounts(List entries) { for (Map.Entry entry : sessionsPerDay.entrySet()) { int sessionCount = entry.getValue(); if (sessionCount <= 0) { @@ -138,10 +115,8 @@ public class ServerCalendar { Long key = entry.getKey(); String day = iso8601TZIndependent.apply(key); - series.append(",{\"title\": \"").append(locale.get(HtmlLang.SIDE_SESSIONS)).append(": ").append(sessionCount) - .append("\",\"start\":\"").append(day) - .append("\",\"color\": \"").append(theme.getValue(ThemeVal.TEAL)).append('"') - .append("}"); + entries.add(CalendarEntry.of(HtmlLang.SIDE_SESSIONS.getKey(), sessionCount, day) + .withColor(theme.getValue(ThemeVal.TEAL))); } } } diff --git a/Plan/react/dashboard/src/components/calendar/PlayerSessionCalendar.jsx b/Plan/react/dashboard/src/components/calendar/PlayerSessionCalendar.jsx index bd9e2414e..e4d49f5b5 100644 --- a/Plan/react/dashboard/src/components/calendar/PlayerSessionCalendar.jsx +++ b/Plan/react/dashboard/src/components/calendar/PlayerSessionCalendar.jsx @@ -1,8 +1,43 @@ import React from "react"; import FullCalendar from '@fullcalendar/react' import dayGridPlugin from '@fullcalendar/daygrid' +import {useTranslation} from "react-i18next"; +import {formatTimeAmount} from "../../util/format/TimeAmountFormat.js"; +import {formatDate, useDatePreferences} from "../text/FormattedDate.jsx"; +import {useTimePreferences} from "../text/FormattedTime.jsx"; const PlayerSessionCalendar = ({series, firstDay}) => { + const {t} = useTranslation(); + + const timePreferences = useTimePreferences(); + const datePreferences = useDatePreferences(); + + const formatDateEasy = date => { + return formatDate(date, datePreferences.offset, datePreferences.pattern, false, datePreferences.recentDaysPattern, t); + } + + const formatTitle = entry => { + switch (entry.title) { + case 'html.label.session': + return formatTimeAmount(timePreferences, entry.value) + ' ' + t(entry.title); + case 'html.label.playtime': + return t(entry.title) + ": " + formatTimeAmount(timePreferences, entry.value) + case 'html.label.registered': + return t(entry.title) + ": " + formatDateEasy(entry.value) + default: + return t(entry.title) + ": " + entry.value; + } + } + + const actualSeries = series.map(entry => { + return { + title: formatTitle(entry), + start: entry.start, + end: entry.end, + color: entry.color + } + }); + return ( { center: '', right: 'dayGridMonth dayGridWeek dayGridDay today prev next' }} - events={(_fetchInfo, successCallback) => successCallback(series)} + events={(_fetchInfo, successCallback) => successCallback(actualSeries)} /> ) } diff --git a/Plan/react/dashboard/src/components/calendar/ServerCalendar.jsx b/Plan/react/dashboard/src/components/calendar/ServerCalendar.jsx index 91bd980ab..2076b59dd 100644 --- a/Plan/react/dashboard/src/components/calendar/ServerCalendar.jsx +++ b/Plan/react/dashboard/src/components/calendar/ServerCalendar.jsx @@ -5,6 +5,8 @@ import interactionPlugin from '@fullcalendar/interaction' import {useTranslation} from "react-i18next"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {faHandPointer} from "@fortawesome/free-regular-svg-icons"; +import {useTimePreferences} from "../text/FormattedTime.jsx"; +import {formatTimeAmount} from "../../util/format/TimeAmountFormat.js"; const ServerCalendar = ({series, firstDay, onSelect}) => { const {t} = useTranslation(); @@ -14,6 +16,28 @@ const ServerCalendar = ({series, firstDay, onSelect}) => { top: "0.5rem", right: "1rem" }; + const timePreferences = useTimePreferences(); + + const formatTitle = entry => { + switch (entry.title) { + case 'html.label.playtime': + return t(entry.title) + ": " + formatTimeAmount(timePreferences, entry.value) + case 'html.calendar.unique': + case 'html.calendar.new': + return t(entry.title) + " " + entry.value + default: + return t(entry.title) + ": " + entry.value; + } + } + + const actualSeries = series.map(entry => { + return { + title: formatTitle(entry), + start: entry.start, + end: entry.end, + color: entry.color + } + }); return (
@@ -38,7 +62,7 @@ const ServerCalendar = ({series, firstDay, onSelect}) => { selectable={Boolean(onSelect)} select={onSelect} unselectAuto={true} - events={(_fetchInfo, successCallback) => successCallback(series)} + events={(_fetchInfo, successCallback) => successCallback(actualSeries)} />
)