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:
parent
39ab14531a
commit
967946378a
|
@ -86,7 +86,7 @@ export default Component.extend({
|
|||
if (!this.site.mobileView) {
|
||||
return false;
|
||||
}
|
||||
return isPushNotificationsSupported(this.site.mobileView);
|
||||
return isPushNotificationsSupported();
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 }));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue