mirror of
https://github.com/discourse/discourse.git
synced 2025-05-17 15:54:38 +08:00
DEV: Make capabilities
into a service (#18678)
This commit is contained in:
parent
d563b73202
commit
cbabc01e0e
@ -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",
|
||||||
|
@ -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");
|
||||||
|
@ -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"),
|
||||||
|
@ -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__);
|
||||||
|
@ -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;
|
||||||
|
@ -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) => {
|
||||||
|
@ -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
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
@ -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)")
|
||||||
|
@ -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");
|
||||||
|
@ -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");
|
|
||||||
},
|
|
||||||
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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");
|
||||||
|
@ -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("/");
|
||||||
|
@ -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) {
|
||||||
|
@ -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));
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user