DEV: Make `capabilities` into a service (#18678)

This commit is contained in:
Jarek Radosz 2023-03-27 19:06:36 +02:00 committed by GitHub
parent d563b73202
commit cbabc01e0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 86 additions and 89 deletions

View File

@ -77,6 +77,12 @@ const DEPRECATED_MODULES = new Map(
dropFrom: "3.0.0",
silent: true,
},
"capabilities:main": {
newName: "service:capabilities",
since: "3.1.0.beta4",
dropFrom: "3.2.0.beta1",
silent: true,
},
"current-user:main": {
newName: "service:current-user",
since: "2.9.0.beta7",

View File

@ -1,18 +1,14 @@
import Component from "@glimmer/component";
import { getOwner } from "discourse-common/lib/get-owner";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import showModal from "discourse/lib/show-modal";
export default class SidebarFooter extends Component {
@service capabilities;
@service site;
@service siteSettings;
@service currentUser;
get capabilities() {
return getOwner(this).lookup("capabilities:main");
}
@action
addSection() {
showModal("sidebar-section-form");

View File

@ -20,7 +20,7 @@ export function autoLoadModules(container, registry) {
let context = {
siteSettings: container.lookup("service:site-settings"),
keyValueStore: container.lookup("service:key-value-store"),
capabilities: container.lookup("capabilities:main"),
capabilities: container.lookup("service:capabilities"),
currentUser: container.lookup("service:current-user"),
site: container.lookup("service:site"),
session: container.lookup("service:session"),

View File

@ -5,7 +5,8 @@ import deprecated from "discourse-common/lib/deprecated";
export default {
name: "inject-objects",
after: "export-application-global",
after: "sniff-capabilities",
initialize(container, app) {
// This is required for Ember CLI tests to work
setDefaultOwner(app.__container__);

View File

@ -6,7 +6,7 @@ export default {
initialize(container) {
const site = container.lookup("service:site");
this.capabilities = container.lookup("capabilities:main");
this.capabilities = container.lookup("service:capabilities");
if (!this.capabilities.isIpadOS && !site.mobileView) {
return;

View File

@ -58,7 +58,7 @@ export default {
{ id: "discourse-audio" }
);
const caps = container.lookup("capabilities:main");
const caps = container.lookup("service:capabilities");
if (caps.isSafari || caps.isIOS) {
api.decorateCookedElement(
(elem) => {

View File

@ -7,7 +7,7 @@ export default {
initialize(container) {
const siteSettings = container.lookup("service:site-settings");
const capabilities = container.lookup("capabilities:main");
const capabilities = container.lookup("service:capabilities");
if (siteSettings.composer_media_optimization_image_enabled) {
// NOTE: There are various performance issues with the Canvas

View File

@ -0,0 +1,15 @@
export default {
name: "sniff-capabilities",
after: "export-application-global",
initialize(container) {
const caps = container.lookup("service:capabilities");
const html = document.documentElement;
if (caps.touch) {
html.classList.add("touch", "discourse-touch");
} else {
html.classList.add("no-touch", "discourse-no-touch");
}
},
};

View File

@ -7,7 +7,7 @@ export default {
after: "inject-objects",
initialize(container) {
const caps = container.lookup("capabilities:main");
const caps = container.lookup("service:capabilities");
if (caps.isAppWebview) {
window
.matchMedia("(prefers-color-scheme: dark)")

View File

@ -58,6 +58,9 @@ export default {
app.register("location:discourse-location", DiscourseLocation);
app.inject("controller", "capabilities", "service:capabilities");
app.inject("component", "capabilities", "service:capabilities");
ALL_TARGETS.forEach((t) => {
app.inject(t, "appEvents", "service:app-events");
app.inject(t, "pmTopicTrackingState", "service:pm-topic-tracking-state");

View File

@ -1,61 +0,0 @@
// Initializes an object that lets us know about browser's capabilities
const APPLE_NAVIGATOR_PLATFORMS = /iPhone|iPod|iPad|Macintosh|MacIntel/;
const APPLE_USERAGENTDATA_PLATFORM = /macOS/;
export default {
name: "sniff-capabilities",
initialize(_, app) {
const html = document.querySelector("html");
const touch = navigator.maxTouchPoints > 1 || "ontouchstart" in window;
const ua = navigator.userAgent;
const caps = { touch };
if (touch) {
html.classList.add("touch", "discourse-touch");
} else {
html.classList.add("no-touch", "discourse-no-touch");
}
caps.isAndroid = ua.includes("Android");
caps.isWinphone = ua.includes("Windows Phone");
caps.isOpera = !!window.opera || ua.includes(" OPR/");
caps.isFirefox = ua.includes("Firefox");
caps.isChrome = !!window.chrome && !caps.isOpera;
caps.isSafari =
/Constructor/.test(window.HTMLElement) ||
window.safari?.pushNotification.toString() ===
"[object SafariRemoteNotification]";
caps.isIpadOS = ua.includes("Mac OS") && !/iPhone|iPod/.test(ua) && touch;
caps.isIOS =
(/iPhone|iPod/.test(navigator.userAgent) || caps.isIpadOS) &&
!window.MSStream;
caps.isApple =
APPLE_NAVIGATOR_PLATFORMS.test(navigator.platform) ||
(navigator.userAgentData &&
APPLE_USERAGENTDATA_PLATFORM.test(navigator.userAgentData.platform));
caps.hasContactPicker =
"contacts" in navigator && "ContactsManager" in window;
caps.canVibrate = "vibrate" in navigator;
caps.isPwa =
window.matchMedia("(display-mode: standalone)").matches ||
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");
app.inject("controller", "capabilities", "capabilities:main");
app.inject("component", "capabilities", "capabilities:main");
},
};

View File

@ -0,0 +1,47 @@
import Service from "@ember/service";
const APPLE_NAVIGATOR_PLATFORMS = /iPhone|iPod|iPad|Macintosh|MacIntel/;
const APPLE_USER_AGENT_DATA_PLATFORM = /macOS/;
// Lets us know about browser's capabilities
export default class Capabilities extends Service {
constructor() {
super(...arguments);
const ua = navigator.userAgent;
this.touch = navigator.maxTouchPoints > 1 || "ontouchstart" in window;
this.isAndroid = ua.includes("Android");
this.isWinphone = ua.includes("Windows Phone");
this.isIpadOS =
ua.includes("Mac OS") && !/iPhone|iPod/.test(ua) && this.touch;
this.isIOS =
(/iPhone|iPod/.test(navigator.userAgent) || this.isIpadOS) &&
!window.MSStream;
this.isApple =
APPLE_NAVIGATOR_PLATFORMS.test(navigator.platform) ||
(navigator.userAgentData &&
APPLE_USER_AGENT_DATA_PLATFORM.test(navigator.userAgentData.platform));
this.isOpera = !!window.opera || ua.includes(" OPR/");
this.isFirefox = ua.includes("Firefox");
this.isChrome = !!window.chrome && !this.isOpera;
this.isSafari =
/Constructor/.test(window.HTMLElement) ||
window.safari?.pushNotification.toString() ===
"[object SafariRemoteNotification]";
this.hasContactPicker =
"contacts" in navigator && "ContactsManager" in window;
this.canVibrate = "vibrate" in navigator;
this.isPwa =
window.matchMedia("(display-mode: standalone)").matches ||
window.navigator.standalone ||
document.referrer.includes("android-app://");
this.isiOSPWA = this.isPwa && this.isIOS;
this.wasLaunchedFromDiscourseHub =
window.location.search.includes("discourse_app=1");
this.isAppWebview = window.ReactNativeWebView !== undefined;
}
}

View File

@ -147,7 +147,7 @@ export default class Widget {
this.site = register.lookup("service:site");
this.siteSettings = register.lookup("service:site-settings");
this.currentUser = register.lookup("service:current-user");
this.capabilities = register.lookup("capabilities:main");
this.capabilities = register.lookup("service:capabilities");
this.store = register.lookup("service:store");
this.appEvents = register.lookup("service:app-events");
this.keyValueStore = register.lookup("service:key-value-store");

View File

@ -114,7 +114,7 @@ acceptance(
});
test("button to toggle between mobile and desktop view on touch devices ", async function (assert) {
const capabilities = this.container.lookup("capabilities:main");
const capabilities = this.container.lookup("service:capabilities");
capabilities.touch = true;
await visit("/");

View File

@ -18,7 +18,6 @@ import {
} from "discourse/lib/user-presence";
import isZoomed from "discourse/plugins/chat/discourse/lib/zoom-check";
import { tracked } from "@glimmer/tracking";
import { getOwner } from "discourse-common/lib/get-owner";
const PAGE_SIZE = 50;
const PAST = "past";
@ -26,6 +25,7 @@ const FUTURE = "future";
const READ_INTERVAL_MS = 1000;
export default class ChatLivePane extends Component {
@service capabilities;
@service chat;
@service chatChannelsManager;
@service router;
@ -136,10 +136,6 @@ export default class ChatLivePane extends Component {
}
}
get capabilities() {
return getOwner(this).lookup("capabilities:main");
}
@debounce(100)
fetchMessages(options = {}) {
if (this._selfDeleted) {

View File

@ -1,20 +1,18 @@
import Component from "@glimmer/component";
import { getOwner } from "discourse-common/lib/get-owner";
import { tracked } from "@glimmer/tracking";
import discourseLater from "discourse-common/lib/later";
import { action } from "@ember/object";
import { isTesting } from "discourse-common/config/environment";
import { inject as service } from "@ember/service";
export default class ChatMessageActionsMobile extends Component {
@service capabilities;
@tracked hasExpandedReply = false;
@tracked showFadeIn = false;
messageActions = null;
get capabilities() {
return getOwner(this).lookup("capabilities:main");
}
@action
fadeAndVibrate() {
discourseLater(this.#addFadeIn.bind(this));

View File

@ -16,7 +16,6 @@ import isZoomed from "discourse/plugins/chat/discourse/lib/zoom-check";
import showModal from "discourse/lib/show-modal";
import ChatMessageFlag from "discourse/plugins/chat/discourse/lib/chat-message-flag";
import { tracked } from "@glimmer/tracking";
import { getOwner } from "discourse-common/lib/get-owner";
import ChatMessageReaction from "discourse/plugins/chat/discourse/models/chat-message-reaction";
let _chatMessageDecorators = [];
@ -38,6 +37,7 @@ export default class ChatMessage extends Component {
@service dialog;
@service currentUser;
@service appEvents;
@service capabilities;
@service chat;
@service chatEmojiReactionStore;
@service chatEmojiPickerManager;
@ -482,10 +482,6 @@ export default class ChatMessage extends Component {
this.react(emoji, REACTIONS.add);
}
get capabilities() {
return getOwner(this).lookup("capabilities:main");
}
@action
react(emoji, reactAction) {
if (!this.args.canInteractWithChat) {