From d8580204c0a5849f5104f22d00d98cf9db50053e Mon Sep 17 00:00:00 2001 From: btangmu Date: Fri, 5 Apr 2024 13:41:01 -0400 Subject: [PATCH] CLDR-17515 Improve the announcements protocol -Distinguish between -1 for no response yet, and 0 for no announcements yet -Include the most recent ID in the response even if that particular announcement is filtered out, so that the client will not keep getting redundant filtered data when there are no new announcements --- 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]);