mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
Merge pull request #1225 from vincentbavitz/https-open-group
This commit is contained in:
commit
32bf5cd83f
10 changed files with 267 additions and 130 deletions
|
@ -2189,13 +2189,16 @@
|
|||
"message": "You have removed your password."
|
||||
},
|
||||
"publicChatExists": {
|
||||
"message": "You are already connected to this public channel"
|
||||
"message": "You are already connected to this open group"
|
||||
},
|
||||
"connectToServerFail": {
|
||||
"message": "Failed to connect to server. Check URL"
|
||||
},
|
||||
"connectingToServer": {
|
||||
"message": "Connecting to server..."
|
||||
},
|
||||
"connectToServerSuccess": {
|
||||
"message": "Successfully connected to new open group server"
|
||||
"message": "Successfully connected to open group"
|
||||
},
|
||||
"setPasswordFail": {
|
||||
"message": "Failed to set password"
|
||||
|
@ -2248,6 +2251,10 @@
|
|||
"message": "Invalid Pubkey Format",
|
||||
"description": "Error string shown when user types an invalid pubkey format"
|
||||
},
|
||||
"attemptedConnectionTimeout": {
|
||||
"message": "Connection to open group timed out",
|
||||
"description": "Shown in toast when attempted connection to OpenGroup times out"
|
||||
},
|
||||
"lnsMappingNotFound": {
|
||||
"message": "There is no LNS mapping associated with this name",
|
||||
"description": "Shown in toast if user enters an unknown LNS name"
|
||||
|
@ -2370,6 +2377,10 @@
|
|||
"displayName": {
|
||||
"message": "Display Name"
|
||||
},
|
||||
"anonymous": {
|
||||
"message": "Anonymous",
|
||||
"description": "The name of currently unidentified users"
|
||||
},
|
||||
"enterDisplayName": {
|
||||
"message": "Enter a display name"
|
||||
},
|
||||
|
|
|
@ -1104,16 +1104,30 @@
|
|||
window.setMediaPermissions(!mediaPermissions);
|
||||
};
|
||||
|
||||
// attempts a connection to an open group server
|
||||
// Attempts a connection to an open group server
|
||||
window.attemptConnection = async (serverURL, channelId) => {
|
||||
let rawserverURL = serverURL
|
||||
let completeServerURL = serverURL.toLowerCase();
|
||||
const valid = window.libsession.Types.OpenGroup.validate(
|
||||
completeServerURL
|
||||
);
|
||||
if (!valid) {
|
||||
return new Promise((_resolve, reject) => {
|
||||
reject(window.i18n('connectToServerFail'));
|
||||
});
|
||||
}
|
||||
|
||||
// Add http or https prefix to server
|
||||
completeServerURL = window.libsession.Types.OpenGroup.prefixify(
|
||||
completeServerURL
|
||||
);
|
||||
|
||||
const rawServerURL = serverURL
|
||||
.replace(/^https?:\/\//i, '')
|
||||
.replace(/[/\\]+$/i, '');
|
||||
rawserverURL = rawserverURL.toLowerCase();
|
||||
const sslServerURL = `https://${rawserverURL}`;
|
||||
const conversationId = `publicChat:${channelId}@${rawserverURL}`;
|
||||
|
||||
// quickly peak to make sure we don't already have it
|
||||
const conversationId = `publicChat:${channelId}@${rawServerURL}`;
|
||||
|
||||
// Quickly peak to make sure we don't already have it
|
||||
const conversationExists = window.ConversationController.get(
|
||||
conversationId
|
||||
);
|
||||
|
@ -1124,9 +1138,9 @@
|
|||
});
|
||||
}
|
||||
|
||||
// get server
|
||||
// Get server
|
||||
const serverAPI = await window.lokiPublicChatAPI.findOrCreateServer(
|
||||
sslServerURL
|
||||
completeServerURL
|
||||
);
|
||||
// SSL certificate failure or offline
|
||||
if (!serverAPI) {
|
||||
|
@ -1136,14 +1150,14 @@
|
|||
});
|
||||
}
|
||||
|
||||
// create conversation
|
||||
// Create conversation
|
||||
const conversation = await window.ConversationController.getOrCreateAndWait(
|
||||
conversationId,
|
||||
'group'
|
||||
);
|
||||
|
||||
// convert conversation to a public one
|
||||
await conversation.setPublicSource(sslServerURL, channelId);
|
||||
// Convert conversation to a public one
|
||||
await conversation.setPublicSource(completeServerURL, channelId);
|
||||
|
||||
// and finally activate it
|
||||
conversation.getPublicSendData(); // may want "await" if you want to use the API
|
||||
|
|
|
@ -122,6 +122,7 @@ export class LeftPane extends React.Component<Props, State> {
|
|||
|
||||
return (
|
||||
<LeftPaneMessageSection
|
||||
contacts={this.props.contacts}
|
||||
openConversationInternal={openConversationInternal}
|
||||
conversations={conversations}
|
||||
searchResults={searchResults}
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
} from './session/settings/SessionSettings';
|
||||
|
||||
export const MainViewController = {
|
||||
joinChannelStateManager,
|
||||
createClosedGroup,
|
||||
renderMessageView,
|
||||
renderSettingsView,
|
||||
|
@ -40,71 +39,6 @@ export class MessageView extends React.Component {
|
|||
// //////////// Management /////////////
|
||||
// /////////////////////////////////////
|
||||
|
||||
function joinChannelStateManager(
|
||||
thisRef: any,
|
||||
serverURL: string,
|
||||
onSuccess?: any
|
||||
) {
|
||||
// Any component that uses this function MUST have the keys [loading, connectSuccess]
|
||||
// in their State
|
||||
|
||||
// TODO: Make this not hard coded
|
||||
const channelId = 1;
|
||||
thisRef.setState({ loading: true });
|
||||
const connectionResult = window.attemptConnection(serverURL, channelId);
|
||||
|
||||
// Give 5s maximum for promise to revole. Else, throw error.
|
||||
const connectionTimeout = setTimeout(() => {
|
||||
if (!thisRef.state.connectSuccess) {
|
||||
thisRef.setState({ loading: false });
|
||||
window.pushToast({
|
||||
title: window.i18n('connectToServerFail'),
|
||||
type: 'error',
|
||||
id: 'connectToServerFail',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}, window.CONSTANTS.MAX_CONNECTION_DURATION);
|
||||
|
||||
connectionResult
|
||||
.then(() => {
|
||||
clearTimeout(connectionTimeout);
|
||||
|
||||
if (thisRef.state.loading) {
|
||||
thisRef.setState({
|
||||
connectSuccess: true,
|
||||
loading: false,
|
||||
});
|
||||
window.pushToast({
|
||||
title: window.i18n('connectToServerSuccess'),
|
||||
id: 'connectToServerSuccess',
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((connectionError: string) => {
|
||||
clearTimeout(connectionTimeout);
|
||||
thisRef.setState({
|
||||
connectSuccess: true,
|
||||
loading: false,
|
||||
});
|
||||
window.pushToast({
|
||||
title: connectionError,
|
||||
id: 'connectToServerFail',
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function createClosedGroup(
|
||||
groupName: string,
|
||||
groupMembers: Array<ContactType>,
|
||||
|
|
|
@ -29,7 +29,6 @@ export interface Props {
|
|||
|
||||
conversations: Array<ConversationListItemPropsType>;
|
||||
contacts: Array<ConversationType>;
|
||||
|
||||
searchResults?: SearchResultsProps;
|
||||
|
||||
updateSearchTerm: (searchTerm: string) => void;
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
ConversationListItem,
|
||||
PropsData as ConversationListItemPropsType,
|
||||
} from '../ConversationListItem';
|
||||
import { ConversationType } from '../../state/ducks/conversations';
|
||||
import {
|
||||
PropsData as SearchResultsProps,
|
||||
SearchResults,
|
||||
|
@ -28,11 +29,13 @@ import {
|
|||
SessionButtonColor,
|
||||
SessionButtonType,
|
||||
} from './SessionButton';
|
||||
import { OpenGroup } from '../../session/types';
|
||||
|
||||
export interface Props {
|
||||
searchTerm: string;
|
||||
isSecondaryDevice: boolean;
|
||||
|
||||
contacts: Array<ConversationType>;
|
||||
conversations?: Array<ConversationListItemPropsType>;
|
||||
searchResults?: SearchResultsProps;
|
||||
|
||||
|
@ -58,7 +61,6 @@ interface State {
|
|||
loading: boolean;
|
||||
overlay: false | SessionComposeToType;
|
||||
valuePasted: string;
|
||||
connectSuccess: boolean;
|
||||
}
|
||||
|
||||
export class LeftPaneMessageSection extends React.Component<Props, State> {
|
||||
|
@ -72,7 +74,6 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
|
|||
loading: false,
|
||||
overlay: false,
|
||||
valuePasted: '',
|
||||
connectSuccess: false,
|
||||
};
|
||||
|
||||
const conversations = this.getCurrentConversations();
|
||||
|
@ -314,6 +315,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
|
|||
|
||||
const closedGroupElement = (
|
||||
<SessionClosableOverlay
|
||||
contacts={this.props.contacts}
|
||||
overlayMode={SessionClosableOverlayType.ClosedGroup}
|
||||
onChangeSessionID={this.handleOnPaste}
|
||||
onCloseClick={() => {
|
||||
|
@ -438,41 +440,64 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
private handleJoinChannelButtonClick(groupUrl: string) {
|
||||
private async handleJoinChannelButtonClick(serverUrl: string) {
|
||||
const { loading } = this.state;
|
||||
|
||||
if (loading) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// longest TLD is now (20/02/06) 24 characters per https://jasontucker.blog/8945/what-is-the-longest-tld-you-can-get-for-a-domain-name
|
||||
const regexURL = /(http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?/;
|
||||
// Server URL entered?
|
||||
if (serverUrl.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (groupUrl.length <= 0) {
|
||||
// Server URL valid?
|
||||
if (!OpenGroup.validate(serverUrl)) {
|
||||
window.pushToast({
|
||||
title: window.i18n('noServerURL'),
|
||||
type: 'error',
|
||||
id: 'connectToServerFail',
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!regexURL.test(groupUrl)) {
|
||||
// Already connected?
|
||||
if (Boolean(await OpenGroup.getConversation(serverUrl))) {
|
||||
window.pushToast({
|
||||
title: window.i18n('noServerURL'),
|
||||
title: window.i18n('publicChatExists'),
|
||||
id: 'publicChatExists',
|
||||
type: 'error',
|
||||
id: 'connectToServerFail',
|
||||
});
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
MainViewController.joinChannelStateManager(this, groupUrl, () => {
|
||||
this.handleToggleOverlay(undefined);
|
||||
// Connect to server
|
||||
try {
|
||||
await OpenGroup.join(serverUrl, async () => {
|
||||
if (await OpenGroup.serverExists(serverUrl)) {
|
||||
window.pushToast({
|
||||
title: window.i18n('connectingToServer'),
|
||||
id: 'connectToServerSuccess',
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
return true;
|
||||
this.setState({ loading: true });
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
window.pushToast({
|
||||
title: window.i18n('connectToServerFail'),
|
||||
id: 'connectToServerFail',
|
||||
type: 'error',
|
||||
});
|
||||
} finally {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async onCreateClosedGroup(
|
||||
|
|
|
@ -66,20 +66,10 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
public getContacts() {
|
||||
const conversations = window.getConversations() || [];
|
||||
const contactsList = this.props.contacts ?? [];
|
||||
|
||||
const conversationList = conversations.filter((conversation: any) => {
|
||||
return (
|
||||
!conversation.isMe() &&
|
||||
conversation.isPrivate() &&
|
||||
!conversation.isSecondaryDevice() &&
|
||||
!conversation.isBlocked()
|
||||
);
|
||||
});
|
||||
|
||||
return conversationList.map((d: any) => {
|
||||
const lokiProfile = d.getLokiProfile();
|
||||
const name = lokiProfile ? lokiProfile.displayName : 'Anonymous';
|
||||
return contactsList.map((d: any) => {
|
||||
const name = d.name ?? window.i18n('anonymous');
|
||||
|
||||
// TODO: should take existing members into account
|
||||
const existingMember = false;
|
||||
|
@ -90,7 +80,7 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
|
|||
authorProfileName: name,
|
||||
selected: false,
|
||||
authorName: name,
|
||||
authorColor: d.getColor(),
|
||||
authorColor: d.color,
|
||||
checkmarked: false,
|
||||
existingMember,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// This is the Open Group equivalent to the PubKey type.
|
||||
import { ConversationModel } from '../../../js/models/conversations';
|
||||
|
||||
interface OpenGroupParams {
|
||||
server: string;
|
||||
|
@ -8,7 +8,7 @@ interface OpenGroupParams {
|
|||
|
||||
export class OpenGroup {
|
||||
private static readonly serverRegex = new RegExp(
|
||||
'^([\\w-]{2,}.){1,2}[\\w-]{2,}$'
|
||||
'^((https?:\\/\\/){0,1})([\\w-]{2,}\\.){1,2}[\\w-]{2,}$'
|
||||
);
|
||||
private static readonly groupIdRegex = new RegExp(
|
||||
'^publicChat:[0-9]*@([\\w-]{2,}.){1,2}[\\w-]{2,}$'
|
||||
|
@ -18,9 +18,17 @@ export class OpenGroup {
|
|||
public readonly groupId?: string;
|
||||
public readonly conversationId: string;
|
||||
|
||||
/**
|
||||
* An OpenGroup object.
|
||||
* If `params.server` is not valid, this will throw an `Error`.
|
||||
*
|
||||
* @param params.server The server URL. `https` will be prepended if `http` or `https` is not explicitly set
|
||||
* @param params.channel The server channel
|
||||
* @param params.groupId The string corresponding to the server. Eg. `publicChat:1@chat.getsession.org`
|
||||
* @param params.conversationId The conversation ID for the backbone model
|
||||
*/
|
||||
constructor(params: OpenGroupParams) {
|
||||
const strippedServer = params.server.replace('https://', '');
|
||||
this.server = strippedServer;
|
||||
this.server = OpenGroup.prefixify(params.server.toLowerCase());
|
||||
|
||||
// Validate server format
|
||||
const isValid = OpenGroup.serverRegex.test(this.server);
|
||||
|
@ -33,14 +41,29 @@ export class OpenGroup {
|
|||
this.groupId = OpenGroup.getGroupId(this.server, this.channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the URL of an open group server
|
||||
*
|
||||
* @param serverUrl The server URL to validate
|
||||
*/
|
||||
public static validate(serverUrl: string): boolean {
|
||||
return this.serverRegex.test(serverUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to make a new instance of `OpenGroup`.
|
||||
* This does NOT respect `ConversationController` and does not guarentee the conversation's existence.
|
||||
*
|
||||
* @param groupId The string corresponding to the server. Eg. `publicChat:1@chat.getsession.org`
|
||||
* @param conversationId The conversation ID for the backbone model
|
||||
* @returns `OpenGroup` if valid otherwise returns `undefined`.
|
||||
*/
|
||||
public static from(
|
||||
groupId: string,
|
||||
conversationId: string
|
||||
conversationId: string,
|
||||
hasSSL: boolean = true
|
||||
): OpenGroup | undefined {
|
||||
// Returns a new instance from a groupId if it's valid
|
||||
// eg. groupId = 'publicChat:1@chat.getsession.org'
|
||||
|
||||
const server = this.getServer(groupId);
|
||||
const server = this.getServer(groupId, hasSSL);
|
||||
const channel = this.getChannel(groupId);
|
||||
|
||||
// Was groupId successfully utilized?
|
||||
|
@ -55,17 +78,121 @@ export class OpenGroup {
|
|||
conversationId,
|
||||
} as OpenGroupParams;
|
||||
|
||||
if (this.serverRegex.test(server)) {
|
||||
const isValid = OpenGroup.serverRegex.test(server);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new OpenGroup(openGroupParams);
|
||||
}
|
||||
|
||||
private static getServer(groupId: string): string | undefined {
|
||||
const isValid = this.groupIdRegex.test(groupId);
|
||||
/**
|
||||
* Join an open group
|
||||
*
|
||||
* @param server The server URL
|
||||
* @param onLoading Callback function to be called once server begins connecting
|
||||
* @returns `OpenGroup` if connection success or if already connected
|
||||
*/
|
||||
public static async join(
|
||||
server: string,
|
||||
onLoading?: any
|
||||
): Promise<OpenGroup | undefined> {
|
||||
const prefixedServer = OpenGroup.prefixify(server);
|
||||
if (!OpenGroup.validate(server)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return isValid ? groupId.split('@')[1] : undefined;
|
||||
// Make this not hard coded
|
||||
const channel = 1;
|
||||
let conversation;
|
||||
let conversationId;
|
||||
|
||||
// Return OpenGroup if we're already connected
|
||||
conversation = await OpenGroup.getConversation(prefixedServer);
|
||||
if (conversation) {
|
||||
conversationId = conversation?.cid;
|
||||
if (conversationId) {
|
||||
return new OpenGroup({
|
||||
server: prefixedServer,
|
||||
channel: 1,
|
||||
conversationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Try to connect to server
|
||||
try {
|
||||
if (onLoading) {
|
||||
onLoading();
|
||||
}
|
||||
|
||||
conversation = await window.attemptConnection(prefixedServer, channel);
|
||||
conversationId = conversation?.cid;
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
// Do we want to add conversation as a property of OpenGroup?
|
||||
return new OpenGroup({
|
||||
server,
|
||||
channel,
|
||||
conversationId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the conversation model of a server from its URL
|
||||
*
|
||||
* @param server The server URL
|
||||
* @returns BackBone conversation model corresponding to the server if it exists, otherwise `undefined`
|
||||
*/
|
||||
public static async getConversation(
|
||||
server: string
|
||||
): Promise<ConversationModel | undefined> {
|
||||
if (!OpenGroup.validate(server)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prefixedServer = this.prefixify(server);
|
||||
const serverInfo = (await window.lokiPublicChatAPI.findOrCreateServer(
|
||||
prefixedServer
|
||||
)) as any;
|
||||
|
||||
if (!serverInfo?.channels?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return serverInfo.channels[0].conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the server exists.
|
||||
* This does not compare against your conversations with the server.
|
||||
*
|
||||
* @param server The server URL
|
||||
*/
|
||||
public static async serverExists(server: string): Promise<boolean> {
|
||||
if (!OpenGroup.validate(server)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const prefixedServer = this.prefixify(server);
|
||||
return Boolean(
|
||||
await window.lokiPublicChatAPI.findOrCreateServer(prefixedServer)
|
||||
);
|
||||
}
|
||||
|
||||
private static getServer(
|
||||
groupId: string,
|
||||
hasSSL: boolean
|
||||
): string | undefined {
|
||||
const isValid = this.groupIdRegex.test(groupId);
|
||||
const strippedServer = isValid ? groupId.split('@')[1] : undefined;
|
||||
|
||||
// We don't know for sure if the server is https or http when taken from the groupId. Preifx accordingly.
|
||||
return strippedServer
|
||||
? this.prefixify(strippedServer.toLowerCase(), hasSSL)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private static getChannel(groupId: string): number | undefined {
|
||||
|
@ -76,7 +203,22 @@ export class OpenGroup {
|
|||
}
|
||||
|
||||
private static getGroupId(server: string, channel: number): string {
|
||||
// server is already validated in constructor; no need to re-check
|
||||
return `publicChat:${channel}@${server}`;
|
||||
// Server is already validated in constructor; no need to re-check
|
||||
|
||||
// Strip server prefix
|
||||
const prefixRegex = new RegExp('https?:\\/\\/');
|
||||
const strippedServer = server.replace(prefixRegex, '');
|
||||
|
||||
return `publicChat:${channel}@${strippedServer}`;
|
||||
}
|
||||
|
||||
private static prefixify(server: string, hasSSL: boolean = true): string {
|
||||
// Prefix server with https:// if it's not already prefixed with http or https.
|
||||
const hasPrefix = server.match('^https?://');
|
||||
if (hasPrefix) {
|
||||
return server;
|
||||
}
|
||||
|
||||
return `http${hasSSL ? 's' : ''}://${server}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,8 +120,26 @@ export const _getLeftPaneLists = (
|
|||
};
|
||||
}
|
||||
|
||||
// Remove all invalid conversations and conversatons of devices associated with cancelled attempted links
|
||||
if (!conversation.timestamp) {
|
||||
// Add Open Group to list as soon as the name has been set
|
||||
if (
|
||||
conversation.isPublic &&
|
||||
(!conversation.name || conversation.name === 'Unknown group')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show loading icon while fetching messages
|
||||
if (conversation.isPublic && !conversation.timestamp) {
|
||||
conversation.lastMessage = {
|
||||
status: 'sending',
|
||||
text: '',
|
||||
isRss: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Remove all invalid conversations and conversatons of devices associated
|
||||
// with cancelled attempted links
|
||||
if (!conversation.isPublic && !conversation.timestamp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -133,7 +151,7 @@ export const _getLeftPaneLists = (
|
|||
unreadCount += conversation.unreadCount;
|
||||
}
|
||||
|
||||
if (!conversation.activeAt) {
|
||||
if (!conversation.isPublic && !conversation.activeAt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
5
ts/window.d.ts
vendored
5
ts/window.d.ts
vendored
|
@ -7,12 +7,15 @@ import { LokiPublicChatFactoryInterface } from '../js/modules/loki_public_chat_a
|
|||
import { LokiAppDotNetServerInterface } from '../js/modules/loki_app_dot_net_api';
|
||||
import { LokiMessageInterface } from '../js/modules/loki_message_api';
|
||||
import { SwarmPolling } from './session/snode_api/swarmPolling';
|
||||
|
||||
import { LibTextsecure } from '../libtextsecure';
|
||||
import { ConversationType } from '../js/modules/data';
|
||||
|
||||
/*
|
||||
We declare window stuff here instead of global.d.ts because we are importing other declarations.
|
||||
If you import anything in global.d.ts, the type system won't work correctly.
|
||||
*/
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CONSTANTS: any;
|
||||
|
@ -33,7 +36,7 @@ declare global {
|
|||
StubMessageAPI: any;
|
||||
WebAPI: any;
|
||||
Whisper: any;
|
||||
attemptConnection: any;
|
||||
attemptConnection: ConversationType;
|
||||
clearLocalData: any;
|
||||
clipboard: any;
|
||||
confirmationDialog: any;
|
||||
|
|
Loading…
Reference in a new issue