From 542ba9ae887dab42ffdabd12e6987f68dbe35ded Mon Sep 17 00:00:00 2001 From: Tom Bishop Date: Mon, 8 Apr 2024 10:11:25 -0400 Subject: [PATCH] CLDR-17515 Improve the announcements protocol (#3615) --- tools/cldr-apps/js/src/esm/cldrAnnounce.mjs | 28 ++++++++--------- .../unicode/cldr/web/api/Announcements.java | 30 ++++++++++++++----- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/tools/cldr-apps/js/src/esm/cldrAnnounce.mjs b/tools/cldr-apps/js/src/esm/cldrAnnounce.mjs index 52a53887643..d9515685457 100644 --- a/tools/cldr-apps/js/src/esm/cldrAnnounce.mjs +++ b/tools/cldr-apps/js/src/esm/cldrAnnounce.mjs @@ -28,7 +28,15 @@ let callbackSetData = null; let callbackSetCounts = null; let callbackSetUnread = null; -let alreadyGotId = 0; +const MOST_RECENT_ID_UNKNOWN = -1; // must be less than zero + +/** + * The most recent announcement ID the back end has told us about + * + * MOST_RECENT_ID_UNKNOWN means the front end hasn't received a response yet; + * 0 means the response from the back end indicated no announcements exist yet + */ +let alreadyGotId = MOST_RECENT_ID_UNKNOWN; /** * Get the number of unread announcements, to display in the main menu @@ -70,11 +78,7 @@ async function refresh(viewCallbackSetData, viewCallbackSetCounts) { if (schedule.tooSoon()) { return; } - let p = null; - if (alreadyGotId) { - p = new URLSearchParams(); - p.append("alreadyGotId", alreadyGotId); - } + const p = new URLSearchParams().append("alreadyGotId", alreadyGotId); const url = cldrAjax.makeApiUrl("announce", p); schedule.setRequestTime(); return await cldrAjax @@ -90,8 +94,8 @@ function setPosts(json) { if (json.unchanged) { return; } + alreadyGotId = json.mostRecentId; thePosts = json; - setAlreadyGotId(thePosts); const totalCount = thePosts.announcements?.length || 0; let checkedCount = 0; for (let announcement of thePosts.announcements) { @@ -112,14 +116,6 @@ function setPosts(json) { return thePosts; } -function setAlreadyGotId(thePosts) { - for (let announcement of thePosts.announcements) { - if (announcement.id > alreadyGotId) { - alreadyGotId = announcement.id; - } - } -} - function canAnnounce() { return cldrStatus.getPermissions()?.userIsManager || false; } @@ -178,7 +174,7 @@ async function combineAndValidateLocales(locs, validateLocCallback) { } function resetSchedule() { - alreadyGotId = 0; + alreadyGotId = MOST_RECENT_ID_UNKNOWN; schedule.reset(); } diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/Announcements.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/Announcements.java index 2301fa37880..1688aefb68e 100644 --- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/Announcements.java +++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/Announcements.java @@ -63,7 +63,7 @@ public Response getAnnouncements( @HeaderParam(Auth.SESSION_HEADER) String sessionString, @QueryParam("alreadyGotId") @Schema(description = "The client already got this announcement ID") - @DefaultValue("0") + @DefaultValue("-1") int alreadyGotId) { CookieSession session = Auth.getSession(sessionString); if (session == null) { @@ -76,25 +76,39 @@ public Response getAnnouncements( if (SurveyMain.isBusted() || !SurveyMain.wasInitCalled() || !SurveyMain.triedToStartUp()) { return STError.surveyNotQuiteReady(); } - Boolean unchanged = - (alreadyGotId != 0 - && alreadyGotId == AnnouncementData.getMostRecentAnnouncementId()); - AnnouncementResponse response = new AnnouncementResponse(session.user, unchanged); + final int mostRecentId = AnnouncementData.getMostRecentAnnouncementId(); + final Boolean unchanged = alreadyGotId != -1 && alreadyGotId == mostRecentId; + final AnnouncementResponse response = + new AnnouncementResponse(session.user, unchanged, mostRecentId); return Response.ok(response).build(); } @Schema(description = "List of announcements") public static final class AnnouncementResponse { - @Schema(description = "unchanged") + @Schema(description = "unchanged (true if request specified ID matching most recent ID)") public boolean unchanged; + /** + * This ID is for the most recent announcement ID even if that announcement is filtered out, + * for example to exclude locales that are not of interest to the user who made the request. + * (The timestamp of the most recent announcement could just as well be used.) This way, the + * client will not endlessly keep getting redundant filtered data when there are no new + * announcements. The client may get redundant filtered data when a new announcement is made + * that doesn't pass the user's filter, but this will not happen more than once per new + * announcement. Further optimization would seem to require the back end to keep track of + * the most recent announcement ID for each filter, or else to repeat the database query for + * each request even if the result would be unchanged -- with dubious benefits. + */ + @Schema(description = "most recent announcement ID (even if filtered out)") + public int mostRecentId; + @Schema(description = "announcements") public Announcement[] announcements; - public AnnouncementResponse(UserRegistry.User user, Boolean unchanged) { + public AnnouncementResponse(UserRegistry.User user, Boolean unchanged, int mostRecentId) { this.unchanged = unchanged; - if (!unchanged) { + this.mostRecentId = mostRecentId; List announcementList = new ArrayList<>(); AnnouncementData.get(user, announcementList); announcements = announcementList.toArray(new Announcement[0]);