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", dropFrom: "3.0.0",
silent: true, silent: true,
}, },
"capabilities:main": {
newName: "service:capabilities",
since: "3.1.0.beta4",
dropFrom: "3.2.0.beta1",
silent: true,
},
"current-user:main": { "current-user:main": {
newName: "service:current-user", newName: "service:current-user",
since: "2.9.0.beta7", since: "2.9.0.beta7",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ export default {
initialize(container) { initialize(container) {
const siteSettings = container.lookup("service:site-settings"); 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) { if (siteSettings.composer_media_optimization_image_enabled) {
// NOTE: There are various performance issues with the Canvas // 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", after: "inject-objects",
initialize(container) { initialize(container) {
const caps = container.lookup("capabilities:main"); const caps = container.lookup("service:capabilities");
if (caps.isAppWebview) { if (caps.isAppWebview) {
window window
.matchMedia("(prefers-color-scheme: dark)") .matchMedia("(prefers-color-scheme: dark)")

View File

@ -58,6 +58,9 @@ export default {
app.register("location:discourse-location", DiscourseLocation); app.register("location:discourse-location", DiscourseLocation);
app.inject("controller", "capabilities", "service:capabilities");
app.inject("component", "capabilities", "service:capabilities");
ALL_TARGETS.forEach((t) => { ALL_TARGETS.forEach((t) => {
app.inject(t, "appEvents", "service:app-events"); app.inject(t, "appEvents", "service:app-events");
app.inject(t, "pmTopicTrackingState", "service:pm-topic-tracking-state"); 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.site = register.lookup("service:site");
this.siteSettings = register.lookup("service:site-settings"); this.siteSettings = register.lookup("service:site-settings");
this.currentUser = register.lookup("service:current-user"); 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.store = register.lookup("service:store");
this.appEvents = register.lookup("service:app-events"); this.appEvents = register.lookup("service:app-events");
this.keyValueStore = register.lookup("service:key-value-store"); 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) { 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; capabilities.touch = true;
await visit("/"); await visit("/");

View File

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

View File

@ -1,20 +1,18 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { getOwner } from "discourse-common/lib/get-owner";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import discourseLater from "discourse-common/lib/later"; import discourseLater from "discourse-common/lib/later";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { isTesting } from "discourse-common/config/environment"; import { isTesting } from "discourse-common/config/environment";
import { inject as service } from "@ember/service";
export default class ChatMessageActionsMobile extends Component { export default class ChatMessageActionsMobile extends Component {
@service capabilities;
@tracked hasExpandedReply = false; @tracked hasExpandedReply = false;
@tracked showFadeIn = false; @tracked showFadeIn = false;
messageActions = null; messageActions = null;
get capabilities() {
return getOwner(this).lookup("capabilities:main");
}
@action @action
fadeAndVibrate() { fadeAndVibrate() {
discourseLater(this.#addFadeIn.bind(this)); 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 showModal from "discourse/lib/show-modal";
import ChatMessageFlag from "discourse/plugins/chat/discourse/lib/chat-message-flag"; import ChatMessageFlag from "discourse/plugins/chat/discourse/lib/chat-message-flag";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { getOwner } from "discourse-common/lib/get-owner";
import ChatMessageReaction from "discourse/plugins/chat/discourse/models/chat-message-reaction"; import ChatMessageReaction from "discourse/plugins/chat/discourse/models/chat-message-reaction";
let _chatMessageDecorators = []; let _chatMessageDecorators = [];
@ -38,6 +37,7 @@ export default class ChatMessage extends Component {
@service dialog; @service dialog;
@service currentUser; @service currentUser;
@service appEvents; @service appEvents;
@service capabilities;
@service chat; @service chat;
@service chatEmojiReactionStore; @service chatEmojiReactionStore;
@service chatEmojiPickerManager; @service chatEmojiPickerManager;
@ -482,10 +482,6 @@ export default class ChatMessage extends Component {
this.react(emoji, REACTIONS.add); this.react(emoji, REACTIONS.add);
} }
get capabilities() {
return getOwner(this).lookup("capabilities:main");
}
@action @action
react(emoji, reactAction) { react(emoji, reactAction) {
if (!this.args.canInteractWithChat) { if (!this.args.canInteractWithChat) {