import {addDays} from "date-fns"; import $ from "jquery"; import render_bankruptcy_alert_content from "../templates/navbar_alerts/bankruptcy.hbs"; import render_configure_email_alert_content from "../templates/navbar_alerts/configure_outgoing_email.hbs"; import render_desktop_notifications_alert_content from "../templates/navbar_alerts/desktop_notifications.hbs"; import render_insecure_desktop_app_alert_content from "../templates/navbar_alerts/insecure_desktop_app.hbs"; import render_navbar_alert_wrapper from "../templates/navbar_alerts/navbar_alert_wrapper.hbs"; import render_profile_incomplete_alert_content from "../templates/navbar_alerts/profile_incomplete.hbs"; import render_server_needs_upgrade_alert_content from "../templates/navbar_alerts/server_needs_upgrade.hbs"; import * as compose_ui from "./compose_ui"; import {localstorage} from "./localstorage"; import * as notifications from "./notifications"; import {page_params} from "./page_params"; import * as unread_ops from "./unread_ops"; import * as unread_ui from "./unread_ui"; import * as util from "./util"; /* This is called by resize.js, and thus indirectly when we trigger * resize events in the logic below. */ export function resize_app() { const navbar_alerts_wrapper_height = $("#navbar_alerts_wrapper").height(); $("body > .app").height("calc(100% - " + navbar_alerts_wrapper_height + "px)"); // the floating recipient bar is usually positioned right below // the `.header` element (including padding). const frb_top = navbar_alerts_wrapper_height + $(".header").height() + Number.parseInt($(".header").css("paddingBottom"), 10); $("#floating_recipient_bar").css("top", frb_top + "px"); // If the compose-box is in expanded state, // reset its height as well. if (compose_ui.is_full_size()) { compose_ui.set_compose_box_top(true); } } const show_step = function ($process, step) { $process .find("[data-step]") .hide() .filter("[data-step=" + step + "]") .show(); }; const get_step = function ($process) { return $process.find("[data-step]:visible").data("step"); }; export function should_show_notifications(ls) { // if the user said to never show banner on this computer again, it will // be stored as `true` so we want to negate that. if (localstorage.supported() && ls.get("dontAskForNotifications") === true) { return false; } return ( // Spectators cannot receive desktop notifications, so never // request permissions to send them. !page_params.is_spectator && // notifications *basically* don't work on any mobile platforms, so don't // event show the banners. This prevents trying to access things that // don't exist like `Notification.permission`. !util.is_mobile() && // if permission has not been granted yet. !notifications.granted_desktop_notifications_permission() && // if permission is allowed to be requested (e.g. not in "denied" state). notifications.permission_state() !== "denied" ); } export function should_show_server_upgrade_notification(ls) { // We do not show the server upgrade nag for a week after the user // clicked "dismiss". if (!localstorage.supported() || ls.get("lastUpgradeNagDismissalTime") === undefined) { return true; } const last_notification_dismissal_time = ls.get("lastUpgradeNagDismissalTime"); const upgrade_nag_dismissal_duration = addDays(new Date(last_notification_dismissal_time), 7); // show the notification only if the time duration is completed. return Date.now() > upgrade_nag_dismissal_duration; } export function dismiss_upgrade_nag(ls) { $(".alert[data-process='server-needs-upgrade'").hide(); if (localstorage.supported()) { ls.set("lastUpgradeNagDismissalTime", Date.now()); } } export function check_profile_incomplete() { if (!page_params.is_admin) { return false; } // Eventually, we might also check page_params.realm_icon_source, // but it feels too aggressive to ask users to do change that // since their organization might not have a logo yet. if ( page_params.realm_description === "" || /^Organization imported from [A-Za-z]+[!.]$/.test(page_params.realm_description) ) { return true; } return false; } export function show_profile_incomplete(is_profile_incomplete) { if (is_profile_incomplete) { // Note that this will be a noop unless we'd already displayed // the notice in this session. This seems OK, given that // this is meant to be a one-time task for administrators. $("[data-process='profile-incomplete']").show(); } else { $("[data-process='profile-incomplete']").hide(); } } export function initialize() { const ls = localstorage(); if (page_params.insecure_desktop_app) { open({ data_process: "insecure-desktop-app", custom_class: "red", rendered_alert_content_html: render_insecure_desktop_app_alert_content(), }); } else if (page_params.server_needs_upgrade) { if (should_show_server_upgrade_notification(ls)) { open({ data_process: "server-needs-upgrade", custom_class: "red", rendered_alert_content_html: render_server_needs_upgrade_alert_content(), }); } } else if (page_params.warn_no_email === true && page_params.is_admin) { // if email has not been set up and the user is the admin, // display a warning to tell them to set up an email server. open({ data_process: "email-server", custom_class: "red", rendered_alert_content_html: render_configure_email_alert_content(), }); } else if (should_show_notifications(ls)) { open({ data_process: "notifications", rendered_alert_content_html: render_desktop_notifications_alert_content(), }); } else if (unread_ui.should_display_bankruptcy_banner()) { const unread_msgs_count = page_params.unread_msgs.count; open({ data_process: "bankruptcy", custom_class: "bankruptcy", rendered_alert_content_html: render_bankruptcy_alert_content({unread_msgs_count}), }); } else if (check_profile_incomplete()) { open({ data_process: "profile-incomplete", rendered_alert_content_html: render_profile_incomplete_alert_content(), }); } // Configure click handlers. $(".request-desktop-notifications").on("click", function (e) { e.preventDefault(); $(this).closest(".alert").hide(); notifications.request_desktop_notifications_permission(); $(window).trigger("resize"); }); $(".reject-notifications").on("click", function () { $(this).closest(".alert").hide(); ls.set("dontAskForNotifications", true); $(window).trigger("resize"); }); $(".accept-bankruptcy").on("click", function (e) { e.preventDefault(); const $process = $(this).closest("[data-process]"); show_step($process, 2); setTimeout(unread_ops.mark_all_as_read, 1000); $(window).trigger("resize"); }); $(".dismiss-upgrade-nag").on("click", (e) => { e.preventDefault(); e.stopPropagation(); dismiss_upgrade_nag(ls); }); $("#navbar_alerts_wrapper").on("click", ".alert .close, .alert .exit", function (e) { e.stopPropagation(); const $process = $(e.target).closest("[data-process]"); if (get_step($process) === 1 && $process.data("process") === "notifications") { show_step($process, 2); } else { $(this).closest(".alert").hide(); } $(window).trigger("resize"); }); // Treat Enter with links in the navbar alerts UI focused like a click., $("#navbar_alerts_wrapper").on("keyup", ".alert-link[role=button]", function (e) { e.stopPropagation(); if (e.key === "Enter") { $(this).trigger("click"); } }); } export function open(args) { const rendered_alert_wrapper_html = render_navbar_alert_wrapper(args); // Note: We only support one alert being rendered at a time; as a // result, we just replace the alert area in the DOM with the // indicated alert. We do this to avoid bad UX, as it'd look weird // to have more than one alert visible at a time. $("#navbar_alerts_wrapper").html(rendered_alert_wrapper_html); $(window).trigger("resize"); }