
* fix not using babel * wip * added tests, moved to map, polifill-like to convert between object and map * basic structure setup * working, found new bug * change buttons * fix login issue * remove logging code * address CR comments * remove custom function in favor of airbnb shim * fix linting * fix PM requested changes * [MM-25323] fix basic auth cancelling * fix crash when multiple request were made * address UX comments, added external link for user convenience
196 lines
5.4 KiB
JavaScript
196 lines
5.4 KiB
JavaScript
// Copyright (c) 2015-2016 Yuya Ochiai
|
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
import url from 'url';
|
|
|
|
import electron, {remote} from 'electron';
|
|
import log from 'electron-log';
|
|
import {isUri, isHttpUri, isHttpsUri} from 'valid-url';
|
|
|
|
function getDomain(inputURL) {
|
|
const parsedURL = url.parse(inputURL);
|
|
return `${parsedURL.protocol}//${parsedURL.host}`;
|
|
}
|
|
|
|
function isValidURL(testURL) {
|
|
return Boolean(isHttpUri(testURL) || isHttpsUri(testURL));
|
|
}
|
|
|
|
function isValidURI(testURL) {
|
|
return Boolean(isUri(testURL));
|
|
}
|
|
|
|
function parseURL(inputURL) {
|
|
if (!inputURL) {
|
|
return null;
|
|
}
|
|
if (inputURL instanceof URL) {
|
|
return inputURL;
|
|
}
|
|
try {
|
|
return new URL(inputURL);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function getHost(inputURL) {
|
|
const parsedURL = parseURL(inputURL);
|
|
if (parsedURL) {
|
|
return parsedURL.origin;
|
|
}
|
|
throw new Error(`Couldn't parse url: ${inputURL}`);
|
|
}
|
|
|
|
// isInternalURL determines if the target url is internal to the application.
|
|
// - currentURL is the current url inside the webview
|
|
// - basename is the global export from the Mattermost application defining the subpath, if any
|
|
function isInternalURL(targetURL, currentURL, basename = '/') {
|
|
if (targetURL.host !== currentURL.host) {
|
|
return false;
|
|
}
|
|
|
|
if (!(targetURL.pathname || '/').startsWith(basename)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function getServerInfo(serverUrl) {
|
|
const parsedServer = parseURL(serverUrl);
|
|
if (!parsedServer) {
|
|
return null;
|
|
}
|
|
|
|
// does the server have a subpath?
|
|
const pn = parsedServer.pathname.toLowerCase();
|
|
const subpath = pn.endsWith('/') ? pn.toLowerCase() : `${pn}/`;
|
|
return {origin: parsedServer.origin, subpath, url: parsedServer};
|
|
}
|
|
|
|
function isTeamUrl(serverUrl, inputUrl, withApi) {
|
|
const parsedURL = parseURL(inputUrl);
|
|
const server = getServerInfo(serverUrl);
|
|
if (!parsedURL || !server || (!equalUrlsIgnoringSubpath(server, parsedURL))) {
|
|
return null;
|
|
}
|
|
|
|
const nonTeamUrlPaths = ['plugins', 'signup', 'login', 'admin', 'channel', 'post', 'oauth', 'admin_console'];
|
|
if (withApi) {
|
|
nonTeamUrlPaths.push('api');
|
|
}
|
|
return !(nonTeamUrlPaths.some((testPath) => (
|
|
parsedURL.pathname.toLowerCase().startsWith(`${server.subpath}${testPath}/`) ||
|
|
parsedURL.pathname.toLowerCase().startsWith(`/${testPath}/`))));
|
|
}
|
|
|
|
function isPluginUrl(serverUrl, inputURL) {
|
|
const server = getServerInfo(serverUrl);
|
|
const parsedURL = parseURL(inputURL);
|
|
if (!parsedURL || !server) {
|
|
return false;
|
|
}
|
|
return (
|
|
equalUrlsIgnoringSubpath(server, parsedURL) &&
|
|
(parsedURL.pathname.toLowerCase().startsWith(`${server.subpath}plugins/`) ||
|
|
parsedURL.pathname.toLowerCase().startsWith('/plugins/')));
|
|
}
|
|
|
|
function getServer(inputURL, teams) {
|
|
const parsedURL = parseURL(inputURL);
|
|
if (!parsedURL) {
|
|
return null;
|
|
}
|
|
let parsedServerUrl;
|
|
let secondOption = null;
|
|
for (let i = 0; i < teams.length; i++) {
|
|
parsedServerUrl = parseURL(teams[i].url);
|
|
|
|
// check server and subpath matches (without subpath pathname is \ so it always matches)
|
|
if (equalUrlsWithSubpath(parsedServerUrl, parsedURL)) {
|
|
return {name: teams[i].name, url: parsedServerUrl, index: i};
|
|
}
|
|
if (equalUrlsIgnoringSubpath(parsedServerUrl, parsedURL)) {
|
|
// in case the user added something on the path that doesn't really belong to the server
|
|
// there might be more than one that matches, but we can't differentiate, so last one
|
|
// is as good as any other in case there is no better match (e.g.: two subpath servers with the same origin)
|
|
// e.g.: https://community.mattermost.com/core
|
|
secondOption = {name: teams[i].name, url: parsedServerUrl, index: i};
|
|
}
|
|
}
|
|
return secondOption;
|
|
}
|
|
|
|
function getDisplayBoundaries() {
|
|
const {screen} = electron;
|
|
|
|
const displays = screen.getAllDisplays();
|
|
|
|
return displays.map((display) => {
|
|
return {
|
|
maxX: display.workArea.x + display.workArea.width,
|
|
maxY: display.workArea.y + display.workArea.height,
|
|
minX: display.workArea.x,
|
|
minY: display.workArea.y,
|
|
maxWidth: display.workArea.width,
|
|
maxHeight: display.workArea.height,
|
|
};
|
|
});
|
|
}
|
|
|
|
// next two functions are defined to clarify intent
|
|
function equalUrlsWithSubpath(url1, url2) {
|
|
return url1.origin === url2.origin && url2.pathname.toLowerCase().startsWith(url1.pathname.toLowerCase());
|
|
}
|
|
|
|
function equalUrlsIgnoringSubpath(url1, url2) {
|
|
return url1.origin.toLowerCase() === url2.origin.toLowerCase();
|
|
}
|
|
|
|
const dispatchNotification = async (title, body, silent, handleClick) => {
|
|
let permission;
|
|
const appIconURL = `file:///${remote.app.getAppPath()}/assets/appicon_48.png`;
|
|
if (Notification.permission === 'default') {
|
|
permission = await Notification.requestPermission();
|
|
} else {
|
|
permission = Notification.permission;
|
|
}
|
|
|
|
if (permission !== 'granted') {
|
|
log.error('Notifications not granted');
|
|
return null;
|
|
}
|
|
|
|
const notification = new Notification(title, {
|
|
body,
|
|
tag: body,
|
|
icon: appIconURL,
|
|
requireInteraction: false,
|
|
silent
|
|
});
|
|
|
|
notification.onclick = handleClick;
|
|
|
|
notification.onerror = () => {
|
|
log.error('Notification failed to show');
|
|
};
|
|
|
|
return notification;
|
|
};
|
|
|
|
export default {
|
|
getDomain,
|
|
isValidURL,
|
|
isValidURI,
|
|
isInternalURL,
|
|
parseURL,
|
|
getServer,
|
|
getServerInfo,
|
|
isTeamUrl,
|
|
isPluginUrl,
|
|
getDisplayBoundaries,
|
|
dispatchNotification,
|
|
getHost,
|
|
};
|