FIX: iOS do not have working push notifications yet (#15888)

Stop Discourse from prompting for push notification on latest iOS beta
where the navigators exposes a broken Push object.

We had some feature detection functions that where outside our
pre-initializer that it dedicated for this stuff. All feature detection
now lives on sniff-capabilities file.

Also removed some old browser detection from the push notifications
code, and simplified the function signature because of it.

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This commit is contained in:
Rafael dos Santos Silva 2022-03-03 16:19:46 -03:00 committed by GitHub
parent 39ab14531a
commit 967946378a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 48 additions and 66 deletions

View File

@ -86,7 +86,7 @@ export default Component.extend({
if (!this.site.mobileView) {
return false;
}
return isPushNotificationsSupported(this.site.mobileView);
return isPushNotificationsSupported();
},
actions: {

View File

@ -1,4 +1,4 @@
import { isAppWebview, postRNWebviewMessage } from "discourse/lib/utilities";
import { postRNWebviewMessage } from "discourse/lib/utilities";
import MobileScrollDirection from "discourse/mixins/mobile-scroll-direction";
import MountWidget from "discourse/components/mount-widget";
import Scrolling from "discourse/mixins/scrolling";
@ -32,7 +32,7 @@ const FooterNavComponent = MountWidget.extend(
this._super(...arguments);
this.appEvents.on("page:changed", this, "_routeChanged");
if (isAppWebview()) {
if (this.capabilities.isAppWebview) {
this.appEvents.on("modal:body-shown", this, "_modalOn");
this.appEvents.on("modal:body-dismissed", this, "_modalOff");
}
@ -52,7 +52,7 @@ const FooterNavComponent = MountWidget.extend(
this._super(...arguments);
this.appEvents.off("page:changed", this, "_routeChanged");
if (isAppWebview()) {
if (this.capabilities.isAppWebview) {
this.appEvents.off("modal:body-shown", this, "_modalOn");
this.appEvents.off("modal:body-removed", this, "_modalOff");
}

View File

@ -37,15 +37,12 @@ export default Component.extend({
@discourseComputed("deferredInstallPromptEvent", "bannerDismissed")
showPWAInstallBanner(deferredInstallPromptEvent, bannerDismissed) {
const launchedFromDiscourseHub =
window.location.search.indexOf("discourse_app=1") !== -1;
return (
this.capabilities.isAndroid &&
this.get("currentUser.trust_level") > 0 &&
deferredInstallPromptEvent && // Pass the browser engagement checks
!window.matchMedia("(display-mode: standalone)").matches && // Not be in the installed PWA already
!launchedFromDiscourseHub && // not launched via official app
!this.capabilities.isAppWebview && // not launched via official app
!bannerDismissed // Have not a previously dismissed install banner
);
},

View File

@ -1,4 +1,3 @@
import { isAppWebview, isiOSPWA } from "discourse/lib/utilities";
import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
@ -24,6 +23,6 @@ export default Controller.extend({
@discourseComputed
showFooterNav() {
return isAppWebview() || isiOSPWA();
return this.capabilities.isAppWebview || this.capabilities.isiOSPWA;
},
});

View File

@ -148,9 +148,9 @@ export default {
);
initDesktopNotifications(bus, appEvents);
if (isPushNotificationsEnabled(user, site.mobileView)) {
if (isPushNotificationsEnabled(user)) {
disableDesktopNotifications();
registerPushNotifications(user, site.mobileView, router, appEvents);
registerPushNotifications(user, router, appEvents);
} else {
unsubscribePushNotifications(user);
}

View File

@ -1,4 +1,4 @@
import { isAppWebview, postRNWebviewMessage } from "discourse/lib/utilities";
import { postRNWebviewMessage } from "discourse/lib/utilities";
import { later } from "@ember/runloop";
// Send bg color to webview so iOS status bar matches site theme
@ -6,8 +6,9 @@ export default {
name: "webview-background",
after: "inject-objects",
initialize() {
if (isAppWebview()) {
initialize(container) {
const caps = container.lookup("capabilities:main");
if (caps.isAppWebview) {
window
.matchMedia("(prefers-color-scheme: dark)")
.addListener(this.updateAppBackground);

View File

@ -1,6 +1,5 @@
import { bind } from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce";
import { isAppWebview } from "discourse/lib/utilities";
import { later, run, throttle } from "@ember/runloop";
import {
nextTopicUrl,
@ -12,6 +11,7 @@ import domUtils from "discourse-common/utils/dom-utils";
import { INPUT_DELAY } from "discourse-common/config/environment";
import { ajax } from "discourse/lib/ajax";
import { headerOffset } from "discourse/lib/offset-calculator";
import { helperContext } from "discourse-common/lib/helpers";
let extraKeyboardShortcutsHelp = {};
function addExtraKeyboardShortcutHelp(help) {
@ -856,13 +856,13 @@ export default {
},
webviewKeyboardBack() {
if (isAppWebview()) {
if (helperContext().capabilities.isAppWebview) {
window.history.back();
}
},
webviewKeyboardForward() {
if (isAppWebview()) {
if (helperContext().capabilities.isAppWebview) {
window.history.forward();
}
},

View File

@ -1,6 +1,5 @@
import {
escapeExpression,
isAppWebview,
postRNWebviewMessage,
} from "discourse/lib/utilities";
import I18n from "I18n";
@ -64,7 +63,7 @@ export default function (elem, siteSettings) {
});
}
if (isAppWebview()) {
if (caps.isAppWebview) {
postRNWebviewMessage(
"headerBg",
$(".mfp-bg").css("background-color")
@ -77,7 +76,7 @@ export default function (elem, siteSettings) {
beforeClose() {
this.wrap.off("click.pinhandler");
this.wrap.removeClass("mfp-force-scrollbars");
if (isAppWebview()) {
if (caps.isAppWebview) {
postRNWebviewMessage(
"headerBg",
$(".d-header").css("background-color")

View File

@ -1,11 +1,13 @@
import { Promise } from "rsvp";
import { isAppleDevice } from "discourse/lib/utilities";
import { helperContext } from "discourse-common/lib/helpers";
// Chrome and Firefox use a native method to do Image -> Bitmap Array (it happens of the main thread!)
// Safari < 15 uses the `<img async>` element due to https://bugs.webkit.org/show_bug.cgi?id=182424
// Safari > 15 still uses `<img async>` due to their buggy createImageBitmap not handling EXIF rotation
async function fileToDrawable(file) {
if ("createImageBitmap" in self && !isAppleDevice()) {
const caps = helperContext().capabilities;
if ("createImageBitmap" in self && !caps.isApple) {
return await createImageBitmap(file);
} else {
const url = URL.createObjectURL(file);

View File

@ -1,5 +1,6 @@
import KeyValueStore from "discourse/lib/key-value-store";
import { ajax } from "discourse/lib/ajax";
import { helperContext } from "discourse-common/lib/helpers";
export const keyValueStore = new KeyValueStore("discourse_push_notifications_");
@ -17,19 +18,6 @@ function sendSubscriptionToServer(subscription, sendConfirmation) {
});
}
function userAgentVersionChecker(agent, version, mobileView) {
const uaMatch = navigator.userAgent.match(
new RegExp(`${agent}\/(\\d+)\\.\\d`)
);
if (uaMatch && mobileView) {
return false;
}
if (!uaMatch || parseInt(uaMatch[1], 10) < version) {
return false;
}
return true;
}
function resetIdle() {
if (
"controller" in navigator.serviceWorker &&
@ -49,40 +37,36 @@ function setupActivityListeners(appEvents) {
appEvents.on("page:changed", resetIdle);
}
export function isPushNotificationsSupported(mobileView) {
export function isPushNotificationsSupported() {
let caps = helperContext().capabilities;
if (
!(
"serviceWorker" in navigator &&
typeof ServiceWorkerRegistration !== "undefined" &&
typeof Notification !== "undefined" &&
"showNotification" in ServiceWorkerRegistration.prototype &&
"PushManager" in window
"PushManager" in window &&
!caps.isAppWebview &&
!caps.isIOS
)
) {
return false;
}
if (
!userAgentVersionChecker("Firefox", 44, mobileView) &&
!userAgentVersionChecker("Chrome", 50)
) {
return false;
}
return true;
}
export function isPushNotificationsEnabled(user, mobileView) {
export function isPushNotificationsEnabled(user) {
return (
user &&
!user.isInDoNotDisturb() &&
isPushNotificationsSupported(mobileView) &&
isPushNotificationsSupported() &&
keyValueStore.getItem(userSubscriptionKey(user))
);
}
export function register(user, mobileView, router, appEvents) {
if (!isPushNotificationsSupported(mobileView)) {
export function register(user, router, appEvents) {
if (!isPushNotificationsSupported()) {
return;
}
if (Notification.permission === "denied" || !user) {
@ -114,8 +98,8 @@ export function register(user, mobileView, router, appEvents) {
});
}
export function subscribe(callback, applicationServerKey, mobileView) {
if (!isPushNotificationsSupported(mobileView)) {
export function subscribe(callback, applicationServerKey) {
if (!isPushNotificationsSupported()) {
return;
}
@ -138,8 +122,8 @@ export function subscribe(callback, applicationServerKey, mobileView) {
});
}
export function unsubscribe(user, callback, mobileView) {
if (!isPushNotificationsSupported(mobileView)) {
export function unsubscribe(user, callback) {
if (!isPushNotificationsSupported()) {
return;
}

View File

@ -1,8 +1,10 @@
import { isAppleDevice } from "discourse/lib/utilities";
import { helperContext } from "discourse-common/lib/helpers";
import positioningWorkaround from "discourse/lib/safari-hacks";
export default function (element) {
if (isAppleDevice() && positioningWorkaround.touchstartEvent) {
const caps = helperContext().capabilities;
if (caps.isApple && positioningWorkaround.touchstartEvent) {
positioningWorkaround.touchstartEvent(element);
} else {
element.focus();

View File

@ -436,19 +436,10 @@ export function areCookiesEnabled() {
}
}
export function isiOSPWA() {
let caps = helperContext().capabilities;
return window.matchMedia("(display-mode: standalone)").matches && caps.isIOS;
}
export function prefersReducedMotion() {
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
export function isAppWebview() {
return window.ReactNativeWebView !== undefined;
}
export function postRNWebviewMessage(prop, value) {
if (window.ReactNativeWebView !== undefined) {
window.ReactNativeWebView.postMessage(JSON.stringify({ [prop]: value }));

View File

@ -46,6 +46,13 @@ export default {
window.navigator.standalone ||
document.referrer.includes("android-app://");
caps.isiOSPWA = caps.isPwa && caps.isIOS;
caps.wasLaunchedFromDiscourseHub = window.location.search.includes(
"discourse_app=1"
);
caps.isAppWebview = window.ReactNativeWebView !== undefined;
// Inject it
app.register("capabilities:main", caps, { instantiate: false });
app.inject("view", "capabilities", "capabilities:main");

View File

@ -1,4 +1,4 @@
import { isAppWebview, postRNWebviewMessage } from "discourse/lib/utilities";
import { postRNWebviewMessage } from "discourse/lib/utilities";
import { createWidget } from "discourse/widgets/widget";
createWidget("footer-nav", {
@ -27,7 +27,7 @@ createWidget("footer-nav", {
})
);
if (isAppWebview()) {
if (this.capabilities.isAppWebview) {
buttons.push(
this.attach("flat-button", {
action: "share",