Add files
This commit is contained in:
commit
bb80829159
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":[],"names":[],"mappings":"","file":"509bba001a76711dd089.css","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,79 @@
|
|||
/* @flow */
|
||||
|
||||
import Flux from 'flux';
|
||||
import Store from './lib/flux/Store';
|
||||
import type {Action} from './flow/Action';
|
||||
|
||||
type Interceptor = (payload: mixed) => boolean;
|
||||
|
||||
export class Dispatcher<Action> extends Flux.Dispatcher<Action> {
|
||||
_interceptor: ?Interceptor;
|
||||
_afterDispatch: ?Function;
|
||||
|
||||
// Super hacky but to avoid forking and rewriting Facebook's Flux Dispatcher
|
||||
// this is a way to hook into the dispatch call's cleanup method to trigger
|
||||
// all the stores' at the same time. This makes it so setState is going to
|
||||
// be the same on all emits and `shouldComponentUpdate1 will be even more
|
||||
// effective.
|
||||
_stopDispatching(...args: Array<any>) {
|
||||
try {
|
||||
Store.emitChanges();
|
||||
} finally {
|
||||
super._stopDispatching(...args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this in situations where a user action should trigger a Dispatch but
|
||||
* an action that occurs during a dispatch does not need to trigger it.
|
||||
*/
|
||||
maybeDispatch(payload: Action) {
|
||||
if (!this.isDispatching()) {
|
||||
this.dispatch(payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to Dispatch in a situation where you have no other choice but to
|
||||
* defer it to the next event loop tick. This should be used in VERY special cases
|
||||
* because it can result in performance issues.
|
||||
*/
|
||||
dirtyDispatch(payload: Action) {
|
||||
if (this.isDispatching()) {
|
||||
setImmediate(this.dispatch.bind(this, payload));
|
||||
} else {
|
||||
this.dispatch(payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a payload to all registered callbacks.
|
||||
*/
|
||||
dispatch(payload: Action) {
|
||||
if (this._interceptor == null || !this._interceptor(payload)) {
|
||||
super.dispatch(payload);
|
||||
this._afterDispatch && this._afterDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set interceptor that might consume payloads.
|
||||
*/
|
||||
setInterceptor(interceptor: ?Interceptor) {
|
||||
this._interceptor = interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback to run after a dispatch.
|
||||
*/
|
||||
setAfterDispatch(callback: ?Function) {
|
||||
this._afterDispatch = callback;
|
||||
}
|
||||
}
|
||||
|
||||
export default (new Dispatcher(): Dispatcher<Action>);
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/Dispatcher.js
|
|
@ -0,0 +1,29 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export function fontScaleTo(fontScale: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.ACCESSIBILITY_FONT_SCALE_TO,
|
||||
fontScale,
|
||||
});
|
||||
}
|
||||
|
||||
export function zoomTo(zoom: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.ACCESSIBILITY_ZOOM_TO,
|
||||
zoom,
|
||||
});
|
||||
}
|
||||
|
||||
export function resetToDefault() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.ACCESSIBILITY_RESET_TO_DEFAULT,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/AccessibilityActionCreators.js
|
|
@ -0,0 +1,17 @@
|
|||
let AlertActionCreators = {};
|
||||
if (__SDK__) {
|
||||
AlertActionCreators = {
|
||||
show() {},
|
||||
};
|
||||
} else if (__IOS__) {
|
||||
AlertActionCreators = require('./ios/AlertActionCreators');
|
||||
} else {
|
||||
AlertActionCreators = require('./web/AlertActionCreators');
|
||||
}
|
||||
|
||||
export default AlertActionCreators;
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/AlertActionCreators.js
|
|
@ -0,0 +1,237 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, InputModes} from '../Constants';
|
||||
import MediaEngineStore from '../stores/MediaEngineStore';
|
||||
import type {InputModeOptions} from '../flow/Client';
|
||||
import {MediaEngineImplementations} from '../lib/MediaEngine';
|
||||
import type {Application} from '../lib/native/rpc_server/RPCTypes';
|
||||
|
||||
let AudioActionCreators = {
|
||||
isNotSupported() {
|
||||
return false;
|
||||
},
|
||||
|
||||
enable() {
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_ENABLE});
|
||||
},
|
||||
};
|
||||
|
||||
if (__WEB__ && !__SDK__) {
|
||||
AudioActionCreators = require('./web/AudioActionCreators');
|
||||
}
|
||||
|
||||
const {enable, isNotSupported} = AudioActionCreators;
|
||||
|
||||
export default {
|
||||
enable: enable,
|
||||
|
||||
toggleSelfMute() {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
if (MediaEngineStore.isEnabled()) {
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_TOGGLE_SELF_MUTE});
|
||||
} else {
|
||||
this.enable();
|
||||
}
|
||||
},
|
||||
|
||||
setTemporarySelfMute(mute: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_SET_TEMPORARY_SELF_MUTE, mute});
|
||||
},
|
||||
|
||||
toggleSelfDeaf() {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_TOGGLE_SELF_DEAF});
|
||||
},
|
||||
|
||||
toggleLocalMute(userId: string) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_TOGGLE_LOCAL_MUTE, userId});
|
||||
},
|
||||
|
||||
setLocalVolume(userId: string, volume: number) {
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_SET_LOCAL_VOLUME, userId, volume});
|
||||
},
|
||||
|
||||
setLocalPan(userId: string, left: number, right: number) {
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIO_SET_LOCAL_PAN, userId, left, right});
|
||||
},
|
||||
|
||||
setMode(mode: $Keys<typeof InputModes>, options?: InputModeOptions) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
if (options == null) {
|
||||
options = MediaEngineStore.getModeOptions();
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_MODE,
|
||||
mode,
|
||||
options,
|
||||
});
|
||||
},
|
||||
|
||||
setInputVolume(volume: number) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_INPUT_VOLUME,
|
||||
volume,
|
||||
});
|
||||
},
|
||||
|
||||
setOutputVolume(volume: number) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_OUTPUT_VOLUME,
|
||||
volume,
|
||||
});
|
||||
},
|
||||
|
||||
setInputDevice(id: string) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_INPUT_DEVICE,
|
||||
id,
|
||||
});
|
||||
},
|
||||
|
||||
setOutputDevice(id: string) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_OUTPUT_DEVICE,
|
||||
id,
|
||||
});
|
||||
},
|
||||
|
||||
setVideoDevice(id: string) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MEDIA_ENGINE_SET_VIDEO_DEVICE,
|
||||
id,
|
||||
});
|
||||
},
|
||||
|
||||
setEchoCancellation(enabled: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_ECHO_CANCELLATION,
|
||||
enabled,
|
||||
});
|
||||
},
|
||||
|
||||
setNoiseSuppression(enabled: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_NOISE_SUPPRESSION,
|
||||
enabled,
|
||||
});
|
||||
},
|
||||
|
||||
setAutomaticGainControl(enabled: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_AUTOMATIC_GAIN_CONTROL,
|
||||
enabled,
|
||||
});
|
||||
},
|
||||
|
||||
setAttenuation(attenuation: number, attenuateWhileSpeakingSelf: boolean, attenuateWhileSpeakingOthers: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_ATTENUATION,
|
||||
attenuation,
|
||||
attenuateWhileSpeakingSelf,
|
||||
attenuateWhileSpeakingOthers,
|
||||
});
|
||||
},
|
||||
|
||||
setQoS(enabled: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_QOS,
|
||||
enabled,
|
||||
});
|
||||
},
|
||||
|
||||
reset() {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_RESET,
|
||||
});
|
||||
},
|
||||
|
||||
setSilenceWarning(enabled: boolean) {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_DISPLAY_SILENCE_WARNING,
|
||||
enabled,
|
||||
});
|
||||
},
|
||||
|
||||
switchSubsystem() {
|
||||
if (isNotSupported()) return;
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SWITCH_SUBSYSTEM,
|
||||
});
|
||||
},
|
||||
|
||||
setCurrentApp(application: Application) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIO_SET_CURRENT_APP,
|
||||
application,
|
||||
});
|
||||
},
|
||||
|
||||
setMediaEngine(implementation: $Keys<typeof MediaEngineImplementations>) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.SET_MEDIA_ENGINE,
|
||||
implementation,
|
||||
});
|
||||
},
|
||||
|
||||
setVideoEnabled(enabled: boolean, channelId?: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MEDIA_ENGINE_SET_VIDEO_ENABLED,
|
||||
enabled,
|
||||
channelId,
|
||||
});
|
||||
|
||||
if (!MediaEngineStore.isEnabled()) {
|
||||
enable(true);
|
||||
}
|
||||
},
|
||||
|
||||
setDesktopSource(sourceId: ?string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MEDIA_ENGINE_SET_DESKTOP_SOURCE,
|
||||
sourceId,
|
||||
});
|
||||
|
||||
if (!MediaEngineStore.isEnabled()) {
|
||||
enable();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/AudioActionCreators.js
|
|
@ -0,0 +1,121 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import GuildSettingsAuditLogStore from '../stores/GuildSettingsAuditLogStore';
|
||||
import {ActionTypes, Endpoints, AUDIT_LOG_PAGE_LIMIT} from '../Constants';
|
||||
|
||||
function transformQuery({before, userId, action}: *) {
|
||||
userId = userId || GuildSettingsAuditLogStore.userIdFilter;
|
||||
action = action || GuildSettingsAuditLogStore.actionFilter;
|
||||
|
||||
const query = {};
|
||||
query.limit = AUDIT_LOG_PAGE_LIMIT;
|
||||
if (before != null) {
|
||||
query.before = before;
|
||||
}
|
||||
if (userId != null) {
|
||||
// eslint-disable-next-line camelcase
|
||||
query.user_id = userId;
|
||||
}
|
||||
if (action != null) {
|
||||
// eslint-disable-next-line camelcase
|
||||
query.action_type = action;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
function isLoading() {
|
||||
return GuildSettingsAuditLogStore.isLoading || GuildSettingsAuditLogStore.isLoadingNextPage;
|
||||
}
|
||||
|
||||
function makeRequest(guildId: *, query: {before?: ?string, userId?: ?string, action?: ?number}) {
|
||||
const transformedQuery = transformQuery(query);
|
||||
return HTTPUtils.get({url: Endpoints.GUILD_AUDIT_LOG(guildId), query: transformedQuery});
|
||||
}
|
||||
|
||||
export function fetchLogs(guildId: string, userId?: ?string, action?: ?number) {
|
||||
if (isLoading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.AUDIT_LOG_FETCH_START});
|
||||
|
||||
return makeRequest(guildId, {userId, action}).then(
|
||||
res => {
|
||||
const {audit_log_entries: logs, users, webhooks} = res.body;
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIT_LOG_FETCH_SUCCESS,
|
||||
logs,
|
||||
users,
|
||||
webhooks,
|
||||
});
|
||||
},
|
||||
() => Dispatcher.dispatch({type: ActionTypes.AUDIT_LOG_FETCH_FAIL})
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchNextLogPage(guildId: string, isGroupedFetch: boolean = false) {
|
||||
if (!GuildSettingsAuditLogStore.hasOlderLogs || isLoading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logs = GuildSettingsAuditLogStore.logs;
|
||||
const last = logs[logs.length - 1];
|
||||
let before = null;
|
||||
if (last != null) {
|
||||
before = last.id;
|
||||
}
|
||||
|
||||
// This can happen as a result of loading more logs
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.AUDIT_LOG_FETCH_NEXT_PAGE_START,
|
||||
before,
|
||||
isGroupedFetch,
|
||||
});
|
||||
|
||||
return makeRequest(guildId, {before}).then(
|
||||
res => {
|
||||
const {audit_log_entries: logs, users, webhooks} = res.body;
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIT_LOG_FETCH_NEXT_PAGE_SUCCESS,
|
||||
logs,
|
||||
users,
|
||||
webhooks,
|
||||
});
|
||||
},
|
||||
() => Dispatcher.dispatch({type: ActionTypes.AUDIT_LOG_FETCH_NEXT_PAGE_FAIL})
|
||||
);
|
||||
}
|
||||
|
||||
export function filterByAction(action: number, guildId: string) {
|
||||
if (isLoading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIT_LOG_FILTER_BY_ACTION,
|
||||
action,
|
||||
});
|
||||
|
||||
return fetchLogs(guildId, null, action);
|
||||
}
|
||||
|
||||
export function filterByUserId(userId: string, guildId: string) {
|
||||
if (isLoading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.AUDIT_LOG_FILTER_BY_USER,
|
||||
userId,
|
||||
});
|
||||
|
||||
return fetchLogs(guildId, userId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/AuditLogActionCreators.js
|
|
@ -0,0 +1,229 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import AuthenticationStore from '../stores/AuthenticationStore';
|
||||
import RouterUtils from '../utils/RouterUtils';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import Storage from '../lib/Storage';
|
||||
import i18n from '../i18n';
|
||||
import {ActionTypes, Endpoints, Routes, DEVICE_TOKEN, DEVICE_PUSH_PROVIDER} from '../Constants';
|
||||
|
||||
export default {
|
||||
startSession(token: string) {
|
||||
// This is a Flux anti-pattern. This can currently get triggered by mounting
|
||||
// a component which could have been triggered by a store event during a dispatch.
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.START_SESSION,
|
||||
token,
|
||||
});
|
||||
},
|
||||
|
||||
login(email: string, password: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.LOGIN,
|
||||
body: {email, password},
|
||||
retries: 2,
|
||||
}).then(
|
||||
res => {
|
||||
if (res.body.mfa) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_MFA_STEP, ticket: res.body.ticket});
|
||||
} else {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_SUCCESS, token: res.body.token});
|
||||
}
|
||||
},
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOGIN_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
// Extra login step for multi-factor auth'd users
|
||||
loginMFA(code: string, ticket: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_MFA});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.LOGIN_MFA,
|
||||
body: {code, ticket},
|
||||
retries: 2,
|
||||
}).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOGIN_SUCCESS, token: res.body.token}),
|
||||
res =>
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOGIN_MFA_FAILURE,
|
||||
message: res.body ? res.body.message : i18n.Messages.NETWORK_ERROR_REST_REQUEST,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
loginToken(token: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_SUCCESS, token});
|
||||
this.startSession(token);
|
||||
},
|
||||
|
||||
loginReset() {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_RESET});
|
||||
},
|
||||
|
||||
register(username: string, invite: ?string = null, captchaKey: ?string = null) {
|
||||
this.registerFull(undefined, username, undefined, invite, captchaKey);
|
||||
},
|
||||
|
||||
registerFull(email: string, username: string, password: string, invite: ?string = null, captchaKey: ?string = null) {
|
||||
Dispatcher.dispatch({type: ActionTypes.REGISTER});
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.REGISTER,
|
||||
body: {
|
||||
fingerprint: AuthenticationStore.getFingerprint(),
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
invite,
|
||||
// eslint-disable-next-line camelcase
|
||||
captcha_key: captchaKey,
|
||||
},
|
||||
}).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.REGISTER_SUCCESS, token: res.body.token}),
|
||||
res => Dispatcher.dispatch({type: ActionTypes.REGISTER_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
logout() {
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.LOGOUT,
|
||||
body: {
|
||||
provider: DEVICE_PUSH_PROVIDER,
|
||||
token: Storage.get(DEVICE_TOKEN),
|
||||
},
|
||||
}).then(() => Dispatcher.dispatch({type: ActionTypes.LOGOUT}));
|
||||
},
|
||||
|
||||
verify(token: string, captchaKey: ?string = null) {
|
||||
if (token != null) {
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.VERIFY,
|
||||
body: {
|
||||
token,
|
||||
// eslint-disable-next-line camelcase
|
||||
captcha_key: captchaKey,
|
||||
},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_SUCCESS, token: res.body.token});
|
||||
RouterUtils.replaceWith(Routes.ME);
|
||||
},
|
||||
res => Dispatcher.dispatch({type: ActionTypes.VERIFY_FAILURE, errors: res.body})
|
||||
);
|
||||
} else {
|
||||
Dispatcher.dispatch({type: ActionTypes.VERIFY_FAILURE, errors: {}});
|
||||
}
|
||||
},
|
||||
|
||||
authorizeIPAddress(token: string) {
|
||||
if (token != null) {
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.AUTHORIZE_IP,
|
||||
body: {token},
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.VERIFY_SUCCESS}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.VERIFY_FAILURE, errors: {}})
|
||||
);
|
||||
} else {
|
||||
Dispatcher.dispatch({type: ActionTypes.VERIFY_FAILURE, errors: {}});
|
||||
}
|
||||
},
|
||||
|
||||
verifyResend() {
|
||||
HTTPUtils.post(Endpoints.VERIFY_RESEND);
|
||||
},
|
||||
|
||||
resetPassword(token: string, password: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN});
|
||||
|
||||
const body: any = {
|
||||
token,
|
||||
password,
|
||||
};
|
||||
// Only send these values if we have them.
|
||||
const pushToken = Storage.get(DEVICE_TOKEN);
|
||||
if (DEVICE_PUSH_PROVIDER != null && pushToken != null) {
|
||||
body['push_provider'] = DEVICE_PUSH_PROVIDER;
|
||||
body['push_token'] = pushToken;
|
||||
}
|
||||
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.RESET_PASSWORD,
|
||||
body,
|
||||
}).then(
|
||||
res => {
|
||||
if (res.body.mfa) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_MFA_STEP, ticket: res.body.ticket});
|
||||
} else {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_SUCCESS, token: res.body.token});
|
||||
RouterUtils.replaceWith(Routes.ME);
|
||||
}
|
||||
},
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOGIN_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
resetPasswordMFA(code: string, ticket: string, password: string, token: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_MFA});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.RESET_PASSWORD,
|
||||
body: {code, ticket, password, token},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN_SUCCESS, token: res.body.token});
|
||||
RouterUtils.replaceWith(Routes.ME);
|
||||
},
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOGIN_MFA_FAILURE, message: res.body.message})
|
||||
);
|
||||
},
|
||||
|
||||
forgotPassword(email: string, callback: () => void) {
|
||||
Dispatcher.dispatch({type: ActionTypes.LOGIN});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.FORGOT_PASSWORD,
|
||||
body: {email},
|
||||
}).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.FORGOT_PASSWORD_SENT});
|
||||
callback && callback();
|
||||
},
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOGIN_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fingerprint a user based on the location they are currently viewing.
|
||||
*
|
||||
* If the user is already registered or been previously fingerprinted
|
||||
* then those tokens are returned.
|
||||
*/
|
||||
fingerprint(location: string): Promise<?string> {
|
||||
const fingerprint = AuthenticationStore.getFingerprint() || AuthenticationStore.getToken();
|
||||
if (fingerprint != null) {
|
||||
return Promise.resolve(fingerprint);
|
||||
}
|
||||
|
||||
return HTTPUtils.get({
|
||||
url: Endpoints.EXPERIMENTS,
|
||||
context: {
|
||||
location,
|
||||
},
|
||||
retries: 3,
|
||||
}).then(
|
||||
res => {
|
||||
const fingerprint = res.body.fingerprint;
|
||||
Dispatcher.dispatch({type: ActionTypes.FINGERPRINT, token: fingerprint});
|
||||
return fingerprint;
|
||||
},
|
||||
// Better to return null then to stop user from registering.
|
||||
// This request already retries.
|
||||
() => null
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/AuthenticationActionCreators.js
|
|
@ -0,0 +1,25 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
fetch() {
|
||||
HTTPUtils.get(Endpoints.OAUTH2_TOKENS).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.USER_AUTHORIZED_APPS_UPDATE, apps: res.body}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.USER_AUTHORIZED_APPS_UPDATE, apps: []})
|
||||
);
|
||||
},
|
||||
|
||||
delete(id: string) {
|
||||
HTTPUtils.delete(Endpoints.OAUTH2_TOKEN(id)).then(() => {
|
||||
this.fetch();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/AuthorizedAppsActionCreators.js
|
|
@ -0,0 +1,284 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {ensureStripeIsLoaded} from '../utils/StripeUtils';
|
||||
import {
|
||||
ActionTypes,
|
||||
Endpoints,
|
||||
PaymentSettings,
|
||||
PremiumPlans,
|
||||
PremiumStatusTypes,
|
||||
PaymentModelModes,
|
||||
} from '../Constants';
|
||||
import AlertActionCreators from './AlertActionCreators';
|
||||
import i18n from '../i18n';
|
||||
import AnalyticsUtils from '../utils/AnalyticsUtils';
|
||||
|
||||
export type CardInfo = {
|
||||
number: string,
|
||||
exp: string,
|
||||
cvc: string,
|
||||
addressZip: string,
|
||||
};
|
||||
|
||||
type SubscribeModes = 'NEW' | 'RESUBSCRIBE';
|
||||
|
||||
export function getBillingInfo() {
|
||||
return Promise.all([
|
||||
HTTPUtils.get({url: Endpoints.BILLING}).then(response => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.BILLING_SYNC_PROFILE_SUCCESS,
|
||||
profile: response.body,
|
||||
});
|
||||
}),
|
||||
HTTPUtils.get({url: Endpoints.BILLING_HISTORY}).then(response => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.BILLING_SYNC_HISTORY_SUCCESS,
|
||||
history: response.body,
|
||||
});
|
||||
}),
|
||||
]).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.BILLING_SYNC_SUCCESS}),
|
||||
response => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.BILLING_SYNC_ERROR,
|
||||
errors: response.body,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function unsubscribe() {
|
||||
return HTTPUtils.delete({
|
||||
url: Endpoints.BILLING_PREMIUM_SUBSCRIPTION,
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE}),
|
||||
response => {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE});
|
||||
return Promise.reject(response.body);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function subscribe(mode: SubscribeModes, token: ?string, plan: string = PremiumPlans.MONTHLY) {
|
||||
const props = {
|
||||
url: Endpoints.BILLING_PREMIUM_SUBSCRIPTION,
|
||||
body: {},
|
||||
};
|
||||
let method;
|
||||
|
||||
// New subscription
|
||||
if (mode === PaymentModelModes.NEW) {
|
||||
method = 'put';
|
||||
props.body = {
|
||||
token,
|
||||
// eslint-disable-next-line camelcase
|
||||
payment_gateway: PaymentSettings.STRIPE.PAYMENT_GATEWAY,
|
||||
plan,
|
||||
};
|
||||
} else {
|
||||
// Resubscribing a canceled subscription, may or may not have a token
|
||||
method = 'patch';
|
||||
props.body = {
|
||||
status: PremiumStatusTypes.ACTIVE,
|
||||
token,
|
||||
};
|
||||
}
|
||||
const event = mode === PaymentModelModes.NEW ? 'premium_purchase_completed' : 'premium_resubscribed';
|
||||
return HTTPUtils[method](props).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE});
|
||||
Dispatcher.dispatch({type: ActionTypes.PAYMENT_UPDATE_SUCCESS});
|
||||
Dispatcher.dispatch({type: ActionTypes.SUBSCRIBE_SUCCESS});
|
||||
AnalyticsUtils.track(event, {success: true, plan});
|
||||
},
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE});
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PAYMENT_UPDATE_FAILURE,
|
||||
error: res.body,
|
||||
});
|
||||
AnalyticsUtils.track(event, {
|
||||
success: false,
|
||||
// eslint-disable-next-line camelcase
|
||||
error_code: res.body.message,
|
||||
plan,
|
||||
});
|
||||
return Promise.reject(res.body);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function changeCard(token) {
|
||||
return HTTPUtils.put({
|
||||
url: Endpoints.BILLING_PAYMENT_SOURCE,
|
||||
body: {
|
||||
// eslint-disable-next-line camelcase
|
||||
payment_gateway: PaymentSettings.STRIPE.PAYMENT_GATEWAY,
|
||||
token,
|
||||
},
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.PAYMENT_UPDATE_SUCCESS}),
|
||||
response => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PAYMENT_UPDATE_FAILURE,
|
||||
error: response.body,
|
||||
});
|
||||
return Promise.reject(response.body);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function upgrade(plan) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.BILLING_PREMIUM_SUBSCRIPTION,
|
||||
body: {
|
||||
// eslint-disable-next-line camelcase
|
||||
payment_gateway: PaymentSettings.STRIPE.PAYMENT_GATEWAY,
|
||||
plan,
|
||||
},
|
||||
}).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE});
|
||||
AnalyticsUtils.track('premium_upgrade_completed', {
|
||||
success: true,
|
||||
plan,
|
||||
});
|
||||
},
|
||||
response => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PAYMENT_UPDATE_FAILURE,
|
||||
error: response.body,
|
||||
});
|
||||
AnalyticsUtils.track('premium_upgrade_completed', {
|
||||
success: false,
|
||||
plan,
|
||||
// eslint-disable-next-line camelcase
|
||||
error_code: response.body.message,
|
||||
});
|
||||
return Promise.reject(response.body);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getStripeToken({number, exp, cvc, addressZip}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ensureStripeIsLoaded().then(Stripe => {
|
||||
Dispatcher.dispatch({type: ActionTypes.PAYMENT_PROCESSING});
|
||||
|
||||
Stripe.card.createToken(
|
||||
{
|
||||
number,
|
||||
cvc,
|
||||
exp,
|
||||
// eslint-disable-next-line camelcase
|
||||
address_zip: addressZip,
|
||||
},
|
||||
(response, body) => {
|
||||
if (response !== 200 || !body || !body.id) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.STRIPE_TOKEN_FAILURE,
|
||||
error: body ? body.error : null,
|
||||
});
|
||||
reject(body);
|
||||
} else {
|
||||
resolve(body);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
subscribe(cardInfo: CardInfo, mode: SubscribeModes = PaymentModelModes.NEW, plan: string = PremiumPlans.MONTHLY) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getStripeToken(cardInfo).then(
|
||||
response => subscribe(mode, response.id, plan).then(resolve, reject),
|
||||
response => {
|
||||
AnalyticsUtils.track('premium_purchase_completed', {
|
||||
success: false,
|
||||
// eslint-disable-next-line camelcase
|
||||
error_code: `${response.error.type}:${response.error.code}`,
|
||||
plan,
|
||||
});
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE});
|
||||
reject(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
// Only useable if there already is a payment source, subscribe should be
|
||||
// used if adding a payment source as well, even for re-subscribe
|
||||
resubscribe() {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE});
|
||||
return subscribe(PaymentModelModes.RESUBSCRIBE);
|
||||
},
|
||||
|
||||
unsubscribe(body: ?string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
AlertActionCreators.show({
|
||||
body: body || i18n.Messages.PREMIUM_CANCEL_CONFIRM_BODY,
|
||||
confirmText: i18n.Messages.PREMIUM_CANCEL_CONFIRM,
|
||||
cancelText: i18n.Messages.PREMIUM_CANCEL_CANCEL,
|
||||
onConfirm: () => {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE});
|
||||
return unsubscribe().then(resolve).catch(body => {
|
||||
AlertActionCreators.show({body: i18n.Messages.PREMIUM_CANCEL_FAILED_BODY});
|
||||
reject(body);
|
||||
});
|
||||
},
|
||||
onCancel: () => reject(),
|
||||
className: 'premium-unsubscribe',
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
upgrade(plan: string = PremiumPlans.YEARLY, body: ?string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
AlertActionCreators.show({
|
||||
body: body || i18n.Messages.PREMIUM_UPGRADE_CONFIRM_BODY,
|
||||
confirmText: i18n.Messages.PREMIUM_UPGRADE_CONFIRM,
|
||||
cancelText: i18n.Messages.PREMIUM_UPGRADE_CANCEL,
|
||||
onConfirm: () =>
|
||||
upgrade(plan).then(resolve).catch(body => {
|
||||
AlertActionCreators.show({body: i18n.Messages.PREMIUM_UPGRADE_FAILED_BODY});
|
||||
reject(body);
|
||||
}),
|
||||
onCancel: () => reject(),
|
||||
className: 'premium-upgrade',
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
changeCard(cardInfo: CardInfo) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getStripeToken(cardInfo).then(response => changeCard(response.id).then(resolve, reject), reject);
|
||||
});
|
||||
},
|
||||
|
||||
removeCard() {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE});
|
||||
return HTTPUtils.delete({url: Endpoints.BILLING_PAYMENT_SOURCE}).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE});
|
||||
return AnalyticsUtils.track('premium_cc_removed');
|
||||
},
|
||||
() => Dispatcher.dispatch({type: ActionTypes.BILLING_INFO_UPDATE_COMPLETE})
|
||||
);
|
||||
},
|
||||
|
||||
getBillingInfo() {
|
||||
// getBillingInfo can be executed during a dispatch - since it gets called
|
||||
// when the settings modal mounts
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.BILLING_SYNC});
|
||||
return getBillingInfo();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/BillingActionCreators.js
|
|
@ -0,0 +1,34 @@
|
|||
/* @flow */
|
||||
|
||||
import CallStore from '../stores/CallStore';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {Endpoints} from '../Constants';
|
||||
|
||||
export default {
|
||||
_getCallContext(channelId: string): {message_id: ?string} {
|
||||
return {
|
||||
message_id: CallStore.getMessageId(channelId), // eslint-disable-line camelcase
|
||||
};
|
||||
},
|
||||
|
||||
ring(channelId: string, recipients?: Array<string>): Promise {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.CALL_RING(channelId),
|
||||
body: {recipients},
|
||||
context: this._getCallContext(channelId),
|
||||
});
|
||||
},
|
||||
|
||||
stopRinging(channelId: string, recipients?: Array<string>): Promise {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.CALL_STOP_RINGING(channelId),
|
||||
body: {recipients},
|
||||
context: this._getCallContext(channelId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/CallActionCreators.js
|
|
@ -0,0 +1,23 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
showChangeLog() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_LOG_OPEN,
|
||||
});
|
||||
},
|
||||
|
||||
hideChangeLog() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_LOG_CLOSE,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChangeLogActionCreators.js
|
|
@ -0,0 +1,35 @@
|
|||
/* @flow */
|
||||
|
||||
import {Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import i18n from '../i18n';
|
||||
import MessageActionCreators from './MessageActionCreators';
|
||||
|
||||
export default {
|
||||
changeNickname(guildId: string, channelId: string, userId: string, nick: string): Promise {
|
||||
return HTTPUtils.patch({
|
||||
url: `${Endpoints.GUILD_MEMBERS(guildId)}/${userId}/nick`,
|
||||
body: {nick},
|
||||
}).then(
|
||||
res => {
|
||||
nick = res.body.nick;
|
||||
MessageActionCreators.sendBotMessage(
|
||||
channelId,
|
||||
nick ? i18n.Messages.COMMAND_NICK_SUCCESS.plainFormat({nick}) : i18n.Messages.COMMAND_NICK_RESET
|
||||
);
|
||||
},
|
||||
res => {
|
||||
if (res.status === 403) {
|
||||
MessageActionCreators.sendBotMessage(channelId, i18n.Messages.COMMAND_NICK_FAILURE_PERMISSION.plainFormat());
|
||||
} else {
|
||||
MessageActionCreators.sendBotMessage(channelId, i18n.Messages.COMMAND_NICK_FAILURE);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChangeNicknameActionCreators.js
|
|
@ -0,0 +1,48 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints, ME} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import AuthenticationStore from '../stores/AuthenticationStore';
|
||||
|
||||
export default {
|
||||
open(guildId: string, userId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_NICKNAME_MODAL_OPEN,
|
||||
guildId,
|
||||
userId,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_NICKNAME_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
changeNickname(guildId: string, userId: string, nick: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_NICKNAME_MODAL_SUBMIT,
|
||||
});
|
||||
let url;
|
||||
if (AuthenticationStore.getId() === userId) {
|
||||
url = `${Endpoints.GUILD_MEMBERS(guildId)}/${ME}/nick`;
|
||||
} else {
|
||||
url = `${Endpoints.GUILD_MEMBERS(guildId)}/${userId}`;
|
||||
}
|
||||
HTTPUtils.patch({url, body: {nick}}).then(
|
||||
() => this.close(),
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_NICKNAME_MODAL_SUBMIT_FAILURE,
|
||||
errors: res.body || {},
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChangeNicknameModalActionCreators.js
|
|
@ -0,0 +1,90 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
openModal(guildId: string, code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_VANITY_URL_MODAL_OPEN,
|
||||
guildId,
|
||||
code,
|
||||
});
|
||||
},
|
||||
|
||||
closeModal() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_VANITY_URL_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
removeVanityURL(guildId: string) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_VANITY_URL(guildId),
|
||||
body: {
|
||||
code: null,
|
||||
},
|
||||
}).then(() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_VANITY_URL,
|
||||
code: null,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Deprecated: This is used for the old settings modal and should not be used anymore
|
||||
// Use `setVanityURL` instead
|
||||
changeVanityURL(guildId: string, code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_VANITY_URL_MODAL_SUBMIT,
|
||||
});
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_VANITY_URL(guildId),
|
||||
body: {
|
||||
code,
|
||||
},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_VANITY_URL,
|
||||
code: res.body.code,
|
||||
});
|
||||
this.closeModal();
|
||||
},
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_VANITY_URL_MODAL_SUBMIT_FAILURE,
|
||||
hasError: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
setVanityURL(guildId: string, code: string) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_VANITY_URL(guildId),
|
||||
body: {
|
||||
code,
|
||||
},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_VANITY_URL,
|
||||
code: res.body.code,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANGE_VANITY_URL_MODAL_SUBMIT_FAILURE,
|
||||
hasError: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChangeVanityURLActionCreators.js
|
|
@ -0,0 +1,165 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import RouterUtils from '../utils/RouterUtils';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import ChannelStore from '../stores/ChannelStore';
|
||||
import SelectedChannelActionCreators from '../actions/SelectedChannelActionCreators';
|
||||
import CallActionCreators from '../actions/CallActionCreators';
|
||||
import {ActionTypes, Endpoints, ME, Routes} from '../Constants';
|
||||
import type {Channel} from '../flow/Server';
|
||||
|
||||
type OverwriteType = {
|
||||
id: string,
|
||||
type: string,
|
||||
allow: number,
|
||||
deny: number,
|
||||
};
|
||||
|
||||
export default {
|
||||
openPrivateChannel(userId: string, recipientIds?: Array<string> | string, joinCall: boolean = false) {
|
||||
const joinCallIfRequested = channelId => {
|
||||
if (joinCall) {
|
||||
SelectedChannelActionCreators.selectVoiceChannel(null, channelId);
|
||||
CallActionCreators.ring(channelId);
|
||||
}
|
||||
};
|
||||
|
||||
// If we are are trying to open a DM, let's see if the client already knows about that channel,
|
||||
// so we don't have to wait for the network call to navigate.
|
||||
if (recipientIds != null && recipientIds.length == 1) {
|
||||
const channelId = this._openCachedDMChannel(recipientIds[0]);
|
||||
if (channelId != null) {
|
||||
joinCallIfRequested(channelId);
|
||||
return Promise.resolve(channelId);
|
||||
}
|
||||
}
|
||||
|
||||
const recipients = this._getRecipients(recipientIds);
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.USER_CHANNELS(userId),
|
||||
body: {recipients},
|
||||
}).then(res => {
|
||||
this._openPrivateChannel(res.body);
|
||||
joinCallIfRequested(res.body.id);
|
||||
return res.body.id;
|
||||
});
|
||||
},
|
||||
|
||||
_openCachedDMChannel(recipientId: string): ?string {
|
||||
const channelId = ChannelStore.getDMFromUserId(recipientId);
|
||||
const channel = channelId != null ? ChannelStore.getChannel(channelId) : null;
|
||||
if (channel) {
|
||||
SelectedChannelActionCreators.selectPrivateChannel(channel.id);
|
||||
return channel.id;
|
||||
}
|
||||
},
|
||||
|
||||
ensurePrivateChannel(userId: string, recipientIds?: Array<string> | string): Promise<string> {
|
||||
const recipients = this._getRecipients(recipientIds);
|
||||
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.USER_CHANNELS(userId),
|
||||
body: {recipients},
|
||||
}).then(res => {
|
||||
const channel = res.body;
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_CREATE, channel});
|
||||
return channel.id;
|
||||
});
|
||||
},
|
||||
|
||||
_getRecipients(recipientIds?: Array<string> | string): Array<string> {
|
||||
if (recipientIds != null) {
|
||||
return Array.isArray(recipientIds) ? recipientIds : [recipientIds];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
_openPrivateChannel(channel: Channel) {
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_CREATE, channel});
|
||||
SelectedChannelActionCreators.selectPrivateChannel(channel.id);
|
||||
},
|
||||
|
||||
closePrivateChannel(channelId: string, unselect: boolean = false) {
|
||||
// TODO: do something better if an error happens
|
||||
HTTPUtils.delete(`${Endpoints.CHANNELS}/${channelId}`);
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_DELETE,
|
||||
channel: {
|
||||
id: channelId,
|
||||
// eslint-disable-next-line camelcase
|
||||
guild_id: null,
|
||||
},
|
||||
});
|
||||
if (unselect) {
|
||||
if (__WEB__) {
|
||||
RouterUtils.transitionTo(Routes.FRIENDS);
|
||||
} else {
|
||||
SelectedChannelActionCreators.selectChannel(ME, null);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// FIXME: remove fromNewSettings when settings modals are removed
|
||||
updatePermissionOverwrite(channelId: string, overwrite: OverwriteType, fromNewSettings: boolean = false) {
|
||||
// eslint-disable-next-line camelcase
|
||||
const query = fromNewSettings ? {_is_new_settings_screen: true} : {};
|
||||
return HTTPUtils.put({
|
||||
url: `${Endpoints.CHANNEL_PERMISSIONS(channelId)}/${overwrite.id}`,
|
||||
body: overwrite,
|
||||
query,
|
||||
});
|
||||
},
|
||||
|
||||
clearPermissionOverwrite(channelId: string, overwriteId: string) {
|
||||
HTTPUtils.delete(`${Endpoints.CHANNEL_PERMISSIONS(channelId)}/${overwriteId}`);
|
||||
},
|
||||
|
||||
// Group DM
|
||||
|
||||
addRecipient(channelId: string, userId: string) {
|
||||
return HTTPUtils.put(`${Endpoints.CHANNEL_RECIPIENTS(channelId)}/${userId}`).then(res => {
|
||||
if (res.status === 201 /* CREATED */) {
|
||||
this._openPrivateChannel(res.body);
|
||||
return res.body.id;
|
||||
} else {
|
||||
return channelId;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addRecipients(channelId: string, userIds: Array<string>) {
|
||||
return this.addRecipient(channelId, userIds[0]).then(channelId => {
|
||||
return Promise.all(userIds.slice(1).map(userId => this.addRecipient(channelId, userId))).then(() => channelId);
|
||||
});
|
||||
},
|
||||
|
||||
removeRecipient(channelId: string, userId: string) {
|
||||
return HTTPUtils.delete(`${Endpoints.CHANNEL_RECIPIENTS(channelId)}/${userId}`);
|
||||
},
|
||||
|
||||
setName(channelId: string, name: string) {
|
||||
HTTPUtils.patch({
|
||||
url: `${Endpoints.CHANNELS}/${channelId}`,
|
||||
body: {name},
|
||||
});
|
||||
},
|
||||
|
||||
setIcon(channelId: string, icon: ?string) {
|
||||
HTTPUtils.patch({
|
||||
url: `${Endpoints.CHANNELS}/${channelId}`,
|
||||
body: {icon},
|
||||
});
|
||||
},
|
||||
|
||||
convertToGuild(channelId: string): Promise {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.CHANNEL_CONVERT(channelId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelActionCreators.js
|
|
@ -0,0 +1,22 @@
|
|||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
update(channelId) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_COLLAPSE,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
updateMuted() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_COLLAPSE_MUTED,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelCollapseActionCreators.js
|
|
@ -0,0 +1,30 @@
|
|||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import GuildRecord from '../records/GuildRecord';
|
||||
|
||||
export function showNotice(noticeType, guild: GuildRecord) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.CHANNEL_NOTICE_SHOW,
|
||||
noticeType,
|
||||
guild,
|
||||
});
|
||||
}
|
||||
|
||||
export function hideNotice(noticeType) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.CHANNEL_NOTICE_HIDE,
|
||||
noticeType,
|
||||
});
|
||||
}
|
||||
|
||||
export function handleForceCreateInvite(guildId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_NOTICE_INVITE_FORCE_CREATE,
|
||||
guildId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelNoticeActionCreators.js
|
|
@ -0,0 +1,91 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import ChannelPinsStore from '../stores/ChannelPinsStore';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import i18n from '../i18n';
|
||||
import ChannelRecord from '../records/ChannelRecord';
|
||||
import AlertActionCreators from '../actions/AlertActionCreators';
|
||||
import {ActionTypes, Endpoints, MAX_PINS_PER_CHANNEL} from '../Constants';
|
||||
|
||||
export default {
|
||||
pinMessage(channel: ChannelRecord, messageId: string) {
|
||||
const {id: channelId, name} = channel;
|
||||
|
||||
HTTPUtils.put({
|
||||
url: Endpoints.PIN(channelId, messageId),
|
||||
}).catch(() => {
|
||||
let body;
|
||||
if (channel.isPrivate()) {
|
||||
body = i18n.Messages.PIN_MESSAGE_TOO_MANY_BODY_PRIVATE_CHANNEL.format({maxPins: MAX_PINS_PER_CHANNEL});
|
||||
} else {
|
||||
body = i18n.Messages.PIN_MESSAGE_TOO_MANY_BODY.format({maxPins: MAX_PINS_PER_CHANNEL, channelName: name});
|
||||
}
|
||||
|
||||
AlertActionCreators.show({
|
||||
title: i18n.Messages.PIN_MESSAGE_TOO_MANY_TITLE,
|
||||
body,
|
||||
confirmText: i18n.Messages.OKAY,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
unpinMessage(channel: ChannelRecord, messageId: string) {
|
||||
HTTPUtils.delete({
|
||||
url: Endpoints.PIN(channel.id, messageId),
|
||||
}).catch(() =>
|
||||
AlertActionCreators.show({
|
||||
title: i18n.Messages.UNPIN_MESSAGE_FAILED_TITLE,
|
||||
body: i18n.Messages.UNPIN_MESSAGE_FAILED_BODY,
|
||||
confirmText: i18n.Messages.TRY_AGAIN,
|
||||
cancelText: i18n.Messages.CANCEL,
|
||||
onConfirm: this.unpinMessage.bind(this, channel, messageId),
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
ackPins(channelId: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.CHANNEL_PINS_ACK,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
fetchPins(channelId: string) {
|
||||
const pins = ChannelPinsStore.getPinnedMessages(channelId);
|
||||
if (pins && (pins.loaded || pins.loading)) {
|
||||
// we got all pins once, so that means we will be updating with deltas as they happen.
|
||||
// don't need to fetch them all again.
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.LOAD_PINNED_MESSAGES,
|
||||
channelId,
|
||||
});
|
||||
|
||||
HTTPUtils.get({
|
||||
url: Endpoints.PINS(channelId),
|
||||
retries: 2,
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_PINNED_MESSAGES_SUCCESS,
|
||||
messages: res.body,
|
||||
channelId: channelId,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_PINNED_MESSAGES_FAILURE,
|
||||
channelId: channelId,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelPinActionCreators.js
|
|
@ -0,0 +1,18 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
toggleSection(section: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_TOGGLE_SECTION,
|
||||
section,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelSectionActionCreators.js
|
|
@ -0,0 +1,101 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {pushLayer} from './LayerActionCreators';
|
||||
import {ActionTypes, Endpoints, Layers} from '../Constants';
|
||||
|
||||
type ChannelSettingsObject = {
|
||||
name?: string,
|
||||
position?: string,
|
||||
topic?: string,
|
||||
bitrate?: number,
|
||||
userLimit?: number,
|
||||
};
|
||||
|
||||
export function init(channelId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_INIT,
|
||||
channelId,
|
||||
});
|
||||
}
|
||||
|
||||
export function open(channelId: string) {
|
||||
if (__IOS__) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_OPEN,
|
||||
channelId,
|
||||
});
|
||||
} else {
|
||||
init(channelId);
|
||||
pushLayer(Layers.CHANNEL_SETTINGS);
|
||||
}
|
||||
}
|
||||
|
||||
export function close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_CLOSE,
|
||||
});
|
||||
}
|
||||
|
||||
export function setSection(section: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_SET_SECTION,
|
||||
section,
|
||||
});
|
||||
}
|
||||
|
||||
export function selectPermissionOverwrite(overwriteId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_OVERWRITE_SELECT,
|
||||
overwriteId,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateChannel({name, topic, bitrate, userLimit}: ChannelSettingsObject) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_UPDATE,
|
||||
name,
|
||||
topic,
|
||||
bitrate,
|
||||
userLimit,
|
||||
});
|
||||
}
|
||||
|
||||
export function saveChannel(channelId: string, {name, position, topic, bitrate, userLimit}: ChannelSettingsObject) {
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_SETTINGS_SUBMIT});
|
||||
return HTTPUtils.patch({
|
||||
url: `${Endpoints.CHANNELS}/${channelId}`,
|
||||
// eslint-disable-next-line camelcase
|
||||
body: {name, position, topic, bitrate, user_limit: userLimit},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_SETTINGS_SUBMIT_SUCCESS, channelId});
|
||||
return res;
|
||||
},
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_SETTINGS_SUBMIT_FAILURE, errors: res.body});
|
||||
return res;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteChannel(channelId: string) {
|
||||
HTTPUtils.delete(`${Endpoints.CHANNELS}/${channelId}`).then(() => close());
|
||||
}
|
||||
|
||||
export default {
|
||||
init,
|
||||
open,
|
||||
close,
|
||||
setSection,
|
||||
selectPermissionOverwrite,
|
||||
updateChannel,
|
||||
saveChannel,
|
||||
deleteChannel,
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelSettingsActionCreators.js
|
|
@ -0,0 +1,55 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import ChannelActionCreators from './ChannelActionCreators';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export function updatePermission(id: string, allow: number, deny: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_PERMISSIONS_UPDATE_PERMISSION,
|
||||
id,
|
||||
allow,
|
||||
deny,
|
||||
});
|
||||
}
|
||||
|
||||
export function selectPermission(id: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_SETTINGS_PERMISSIONS_SELECT_PERMISSION,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
export function init() {
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_SETTINGS_PERMISSIONS_INIT});
|
||||
}
|
||||
|
||||
type OverwriteType = {
|
||||
id: string,
|
||||
type: string,
|
||||
allow: number,
|
||||
deny: number,
|
||||
};
|
||||
|
||||
export function savePermissionUpdates(channelId: string, overwrites: Array<OverwriteType>) {
|
||||
Dispatcher.dispatch({type: ActionTypes.CHANNEL_SETTINGS_PERMISSIONS_SUBMITTING});
|
||||
return new Promise(resolve => {
|
||||
const chain = () => {
|
||||
if (overwrites.length === 0) {
|
||||
return resolve();
|
||||
}
|
||||
const overwrite = overwrites.pop();
|
||||
if (overwrite == null) {
|
||||
return chain();
|
||||
}
|
||||
ChannelActionCreators.updatePermissionOverwrite(channelId, overwrite, true).then(chain, chain);
|
||||
};
|
||||
|
||||
chain();
|
||||
}).then(() => Dispatcher.dispatch({type: ActionTypes.CHANNEL_SETTINGS_PERMISSIONS_SAVE_SUCCESS}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ChannelSettingsPermissionsActionCreators.js
|
|
@ -0,0 +1,80 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints, UNSAFE_PLATFORM_TYPES} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
fetch() {
|
||||
HTTPUtils.get(Endpoints.CONNECTIONS).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.USER_CONNECTIONS_UPDATE, local: true, accounts: res.body}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.USER_CONNECTIONS_UPDATE, local: true, accounts: []})
|
||||
);
|
||||
},
|
||||
|
||||
authorize(platformType: string) {
|
||||
return HTTPUtils.get(Endpoints.CONNECTIONS_AUTHORIZE(platformType));
|
||||
},
|
||||
|
||||
callback(
|
||||
platformType: string,
|
||||
body: {code: string, openid_params: Object, state: string},
|
||||
insecure: boolean = false
|
||||
) {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.CONNECTIONS_CALLBACK(platformType),
|
||||
body: {...body, insecure},
|
||||
});
|
||||
},
|
||||
|
||||
connect(platformType: string, accountId: string, name: string, location?: string) {
|
||||
HTTPUtils.put({
|
||||
url: Endpoints.CONNECTION(platformType, encodeURIComponent(accountId)),
|
||||
body: {
|
||||
name,
|
||||
friend_sync: UNSAFE_PLATFORM_TYPES.has(platformType), // eslint-disable-line camelcase
|
||||
},
|
||||
context: {location},
|
||||
});
|
||||
},
|
||||
|
||||
disconnect(platformType: string, accountId: string) {
|
||||
HTTPUtils.delete(Endpoints.CONNECTION(platformType, encodeURIComponent(accountId)));
|
||||
},
|
||||
|
||||
setVisibility(platformType: string, id: string, visibility: number): Promise {
|
||||
return this.update(platformType, id, {visibility});
|
||||
},
|
||||
|
||||
setFriendSync(platformType: string, id: string, friendSync: boolean): Promise {
|
||||
return this.update(platformType, id, {friend_sync: friendSync}); // eslint-disable-line camelcase
|
||||
},
|
||||
|
||||
update(platformType: string, accountId: string, body: {visibility?: boolean, friend_sync?: boolean}): Promise {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.CONNECTION(platformType, encodeURIComponent(accountId)),
|
||||
body,
|
||||
});
|
||||
},
|
||||
|
||||
joinServer(integrationId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_CONNECTIONS_INTEGRATION_JOINING,
|
||||
integrationId,
|
||||
joining: true,
|
||||
});
|
||||
|
||||
HTTPUtils.post(Endpoints.INTEGRATIONS_JOIN(integrationId), () => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_CONNECTIONS_INTEGRATION_JOINING,
|
||||
integrationId,
|
||||
joining: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ConnectedAccountsActionCreators.js
|
|
@ -0,0 +1,25 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import type {ContextMenuProps} from '../flow/Client';
|
||||
|
||||
export default {
|
||||
open(contextMenu: ContextMenuProps) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CONTEXT_MENU_OPEN,
|
||||
contextMenu,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CONTEXT_MENU_CLOSE,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ContextMenuActionCreators.js
|
|
@ -0,0 +1,65 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints, BITRATE_DEFAULT} from '../Constants';
|
||||
import type {PermissionOverwrite} from '../records/ChannelRecord';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
open(channelId: ?string, channelType: ?number, guildId: ?string, cloneChannelId: ?string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_CHANNEL_MODAL_OPEN,
|
||||
channelId,
|
||||
channelType,
|
||||
guildId,
|
||||
cloneChannelId,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_CHANNEL_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
createChannel(
|
||||
guildId: string,
|
||||
type: number,
|
||||
name: string,
|
||||
permissionOverwrites: Array<PermissionOverwrite> = [],
|
||||
bitrate: ?number,
|
||||
userLimit: ?number
|
||||
) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_CHANNEL_MODAL_SUBMIT,
|
||||
});
|
||||
|
||||
const body: any = {
|
||||
type,
|
||||
name,
|
||||
// eslint-disable-next-line camelcase
|
||||
permission_overwrites: permissionOverwrites,
|
||||
};
|
||||
|
||||
if (bitrate != null && bitrate !== BITRATE_DEFAULT) {
|
||||
body.bitrate = bitrate;
|
||||
}
|
||||
|
||||
if (userLimit != null && userLimit > 0) {
|
||||
body['user_limit'] = userLimit;
|
||||
}
|
||||
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILD_CHANNELS(guildId),
|
||||
body,
|
||||
}).then(
|
||||
() => this.close(),
|
||||
res => Dispatcher.dispatch({type: ActionTypes.CREATE_CHANNEL_MODAL_SUBMIT_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/CreateChannelModalActionCreators.js
|
|
@ -0,0 +1,65 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import AnalyticsUtils from '../utils/AnalyticsUtils';
|
||||
|
||||
export default {
|
||||
open(screen: ?number, source: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_GUILD_MODAL_OPEN,
|
||||
screen,
|
||||
});
|
||||
|
||||
AnalyticsUtils.track('Open Modal', {
|
||||
Type: 'Create Guild',
|
||||
Source: source,
|
||||
});
|
||||
},
|
||||
|
||||
setScreen(screen: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_GUILD_MODAL_SET_SCREEN,
|
||||
screen,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_GUILD_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
join(code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.JOIN_GUILD_MODAL,
|
||||
text: code,
|
||||
});
|
||||
},
|
||||
|
||||
updateTemporaryGuild(update: {icon?: string, name?: string, region?: string}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_GUILD_MODAL_UPDATE,
|
||||
guild: update,
|
||||
});
|
||||
},
|
||||
|
||||
createGuild(name: string, region: string, icon: ?string = null) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CREATE_GUILD_MODAL_SUBMIT,
|
||||
});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILDS,
|
||||
body: {name, region, icon},
|
||||
}).then(
|
||||
() => this.close(),
|
||||
res => Dispatcher.dispatch({type: ActionTypes.CREATE_GUILD_MODAL_SUBMIT_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/CreateGuildModalActionCreators.js
|
|
@ -0,0 +1,33 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
selectGuild(guildId?: ?string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.DELAYED_GUILD_SELECT,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
selectChannel(guildId: ?string, channelId: ?string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.DELAYED_CHANNEL_SELECT,
|
||||
guildId,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
flushSelection(immediate?: boolean = false) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.DELAYED_SELECT_FLUSH,
|
||||
immediate,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/DelayedSelectionActionCreators.js
|
|
@ -0,0 +1,59 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
updateChannelDimensions(channelId: string, scrollTop: ?number, scrollHeight: ?number, offsetHeight: ?number) {
|
||||
// This is a Flux anti-pattern because you cannot dispatch
|
||||
// while the Dispatcher is dispatching, but to keep the scroll
|
||||
// in sync with the DOM we have to dispatch right after a re-render.
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.UPDATE_CHANNEL_DIMENSIONS,
|
||||
channelId,
|
||||
scrollTop,
|
||||
scrollHeight,
|
||||
offsetHeight,
|
||||
});
|
||||
},
|
||||
|
||||
updateChannelListScroll(guildId: string, scrollTop: ?number) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.UPDATE_CHANNEL_LIST_DIMENSIONS,
|
||||
guildId,
|
||||
scrollTop,
|
||||
});
|
||||
},
|
||||
|
||||
channelListScrollTo(guildId: string, scrollTo: ?string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.UPDATE_CHANNEL_LIST_DIMENSIONS,
|
||||
guildId,
|
||||
scrollTo,
|
||||
});
|
||||
},
|
||||
|
||||
clearChannelListScrollTo(guildId: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.UPDATE_CHANNEL_LIST_DIMENSIONS,
|
||||
guildId,
|
||||
scrollTo: null,
|
||||
});
|
||||
},
|
||||
|
||||
clearChannelDimensions(channelId: string) {
|
||||
this.updateChannelDimensions(channelId, null, null, null);
|
||||
},
|
||||
|
||||
updateGuildListScrollTo(scrollTop: number) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.UPDATE_GUILD_LIST_DIMENSIONS,
|
||||
scrollTop,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/DimensionActionCreators.js
|
|
@ -0,0 +1,23 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
open() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EMAIL_VERIFICATION_MODAL_OPEN,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EMAIL_VERIFICATION_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/EmailVerificationModalActionCreators.js
|
|
@ -0,0 +1,64 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
|
||||
export default {
|
||||
setDiversityColor(color: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EMOJI_DIVERSITY_COLOR_CHANGE,
|
||||
color,
|
||||
});
|
||||
},
|
||||
|
||||
fetchEmoji(guildId: string) {
|
||||
Dispatcher.maybeDispatch({type: ActionTypes.EMOJI_FETCH, guildId});
|
||||
HTTPUtils.get({
|
||||
url: Endpoints.GUILD_EMOJIS(guildId),
|
||||
}).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.EMOJI_FETCH_SUCCESS, guildId, emojis: res.body}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.EMOJI_FETCH_FAILURE, guildId})
|
||||
);
|
||||
},
|
||||
|
||||
uploadEmoji(guildId: string, image: string, name: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EMOJI_UPLOAD_START,
|
||||
guildId,
|
||||
});
|
||||
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILD_EMOJIS(guildId),
|
||||
body: {
|
||||
image,
|
||||
name,
|
||||
},
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.EMOJI_UPLOAD_STOP, guildId}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.EMOJI_UPLOAD_STOP, guildId})
|
||||
);
|
||||
},
|
||||
|
||||
deleteEmoji(guildId: string, emojiId: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.EMOJI_DELETE, guildId, emojiId});
|
||||
|
||||
HTTPUtils.delete({
|
||||
url: Endpoints.GUILD_EMOJI(guildId, emojiId),
|
||||
});
|
||||
},
|
||||
|
||||
updateEmoji(guildId: string, emojiId: string, name: string) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_EMOJI(guildId, emojiId),
|
||||
body: {
|
||||
name,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/EmojiActionCreators.js
|
|
@ -0,0 +1,66 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import type Flux from '../lib/flux';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import AuthenticationStore from '../stores/AuthenticationStore';
|
||||
import type {ExperimentDescriptor, RenderFunctions} from '../flow/Client';
|
||||
|
||||
export default {
|
||||
trigger(experimentDescriptor: ExperimentDescriptor) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.EXPERIMENT_TRIGGER,
|
||||
experimentDescriptor,
|
||||
});
|
||||
},
|
||||
|
||||
register(store: Flux.Store, renderFunctions: RenderFunctions) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EXPERIMENT_REGISTER,
|
||||
store,
|
||||
renderFunctions,
|
||||
});
|
||||
},
|
||||
|
||||
overrideType(experimentId: string, experimentType: ?string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EXPERIMENT_OVERRIDE_TYPE,
|
||||
experimentId,
|
||||
experimentType,
|
||||
});
|
||||
},
|
||||
|
||||
overrideBucket(experimentId: string, experimentBucket: ?number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.EXPERIMENT_OVERRIDE_BUCKET,
|
||||
experimentId,
|
||||
experimentBucket,
|
||||
});
|
||||
},
|
||||
|
||||
fetchExperiments() {
|
||||
const headers = {};
|
||||
const currentFingerprint = AuthenticationStore.getFingerprint();
|
||||
if (currentFingerprint) {
|
||||
headers['X-Fingerprint'] = currentFingerprint;
|
||||
}
|
||||
|
||||
return HTTPUtils.get({
|
||||
url: Endpoints.EXPERIMENTS,
|
||||
headers,
|
||||
}).then(res => {
|
||||
const {fingerprint, assignments} = res.body;
|
||||
if (fingerprint) {
|
||||
Dispatcher.dispatch({type: ActionTypes.FINGERPRINT, token: fingerprint});
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.EXPERIMENTS_FETCH_SUCCESS, experiments: assignments});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ExperimentActionCreators.js
|
|
@ -0,0 +1,23 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {Endpoints, ActionTypes} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
fetch() {
|
||||
HTTPUtils.get(Endpoints.FRIEND_SUGGESTIONS).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOAD_FRIEND_SUGGESTIONS_SUCCESS, suggestions: res.body}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.LOAD_FRIEND_SUGGESTIONS_FAILURE})
|
||||
);
|
||||
},
|
||||
|
||||
ignore(userId: string) {
|
||||
HTTPUtils.delete(`${Endpoints.FRIEND_SUGGESTIONS}/${userId}`);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/FriendSuggestionActionCreators.js
|
|
@ -0,0 +1,18 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
setSection(section: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.FRIENDS_SET_SECTION,
|
||||
section,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/FriendsActionCreators.js
|
|
@ -0,0 +1,38 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
addGame(pid: number) {
|
||||
Dispatcher.dispatch({type: ActionTypes.RUNNING_GAME_ADD_OVERRIDE, pid});
|
||||
},
|
||||
|
||||
toggleOverlay(game: Object) {
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.RUNNING_GAME_TOGGLE_OVERLAY, game});
|
||||
},
|
||||
|
||||
editName(game: Object, newName: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.RUNNING_GAME_EDIT_NAME, game, newName});
|
||||
},
|
||||
|
||||
setRunningGame(name: string) {
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.USER_GAMES_START,
|
||||
body: {
|
||||
name,
|
||||
},
|
||||
retries: 3,
|
||||
});
|
||||
},
|
||||
|
||||
deleteEntry(game: Object) {
|
||||
Dispatcher.dispatch({type: ActionTypes.RUNNING_GAME_DELETE_ENTRY, game});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/GamesActionCreators.js
|
|
@ -0,0 +1,167 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints, ME, Routes} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import RouterUtils from '../utils/RouterUtils';
|
||||
import type {CreatedGuild} from '../flow/Server';
|
||||
import ChannelStore from '../stores/ChannelStore';
|
||||
import SelectedChannelActionCreators from '../actions/SelectedChannelActionCreators';
|
||||
import GuildActionCreators from '../actions/GuildActionCreators';
|
||||
|
||||
export default {
|
||||
selectGuild(guildId: string, focus: boolean = false) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SELECT,
|
||||
guildId: guildId === ME ? null : guildId,
|
||||
focus,
|
||||
});
|
||||
},
|
||||
|
||||
createGuild(guild: CreatedGuild) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_CREATE,
|
||||
guild,
|
||||
});
|
||||
},
|
||||
|
||||
syncGuild(guildId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SYNC_ADD,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
markGuildAsRead(guildId: string): Promise {
|
||||
return HTTPUtils.post(Endpoints.GUILD_ACK(guildId)).then(() =>
|
||||
Dispatcher.dispatch({type: ActionTypes.GUILD_ACK, guildId})
|
||||
);
|
||||
},
|
||||
|
||||
setServerMute(guildId: string, userId: string, mute: boolean) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_MEMBER(guildId, userId),
|
||||
body: {mute},
|
||||
});
|
||||
},
|
||||
|
||||
setServerDeaf(guildId: string, userId: string, deaf: boolean) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_MEMBER(guildId, userId),
|
||||
body: {deaf},
|
||||
});
|
||||
},
|
||||
|
||||
setChannel(guildId: string, userId: string, channelId: string) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_MEMBER(guildId, userId),
|
||||
// eslint-disable-next-line camelcase
|
||||
body: {channel_id: channelId},
|
||||
});
|
||||
},
|
||||
|
||||
kickUser(guildId: string, userId: string, reason: string) {
|
||||
return HTTPUtils.delete({url: Endpoints.GUILD_MEMBER(guildId, userId), query: {reason}});
|
||||
},
|
||||
|
||||
banUser(guildId: string, userId: string, deleteMessageForDays: string, reason: ?string) {
|
||||
const query = {'delete-message-days': deleteMessageForDays, reason};
|
||||
return HTTPUtils.put({url: Endpoints.GUILD_BAN(guildId, userId), query});
|
||||
},
|
||||
|
||||
unbanUser(guildId: string, userId: string) {
|
||||
return HTTPUtils.delete(Endpoints.GUILD_BAN(guildId, userId));
|
||||
},
|
||||
|
||||
createRole(guildId: string) {
|
||||
HTTPUtils.post(Endpoints.GUILD_ROLES(guildId));
|
||||
},
|
||||
|
||||
// FIXME: remove fromNewSettings when settings modals are removed
|
||||
updateRole(
|
||||
guildId: string,
|
||||
roleId: string,
|
||||
name: string,
|
||||
permissions: number,
|
||||
color: number,
|
||||
hoist: boolean,
|
||||
mentionable: boolean,
|
||||
fromNewSettings: boolean = false
|
||||
) {
|
||||
// eslint-disable-next-line camelcase
|
||||
const query = fromNewSettings ? {_is_new_settings_screen: true} : {};
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_ROLE(guildId, roleId),
|
||||
body: {name, permissions, color: color || 0, hoist, mentionable},
|
||||
query,
|
||||
});
|
||||
},
|
||||
|
||||
deleteRole(guildId: string, roleId: string) {
|
||||
HTTPUtils.delete(`${Endpoints.GUILD_ROLES(guildId)}/${roleId}`);
|
||||
},
|
||||
|
||||
batchChannelUpdate(guildId: string, channels: Array<{id: string, position: number}>) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_CHANNELS(guildId),
|
||||
body: channels,
|
||||
});
|
||||
},
|
||||
|
||||
batchRoleUpdate(guildId: string, roles: Array<{id: string, position: number}>) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_ROLES(guildId),
|
||||
body: roles,
|
||||
});
|
||||
},
|
||||
|
||||
requestMembers(guildId: string, query: string = '', limit: number = 10) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.GUILD_MEMBERS_REQUEST,
|
||||
guildId,
|
||||
query,
|
||||
limit,
|
||||
});
|
||||
},
|
||||
|
||||
move(fromIndex: number, toIndex: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_MOVE,
|
||||
fromIndex,
|
||||
toIndex,
|
||||
});
|
||||
},
|
||||
|
||||
nsfwAgree(guildId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_NSFW_AGREE,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
nsfwDisagree(guildId: string) {
|
||||
// Attempt to punt user to the default channel if it is not NSFW.
|
||||
const defaultChannel = ChannelStore.getChannel(guildId);
|
||||
if (defaultChannel != null && !defaultChannel.isNSFW()) {
|
||||
if (__WEB__) {
|
||||
RouterUtils.transitionTo(Routes.CHANNEL(guildId, defaultChannel.id));
|
||||
} else {
|
||||
SelectedChannelActionCreators.selectChannel(guildId, defaultChannel.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise just take user to the friends list.
|
||||
if (__WEB__) {
|
||||
RouterUtils.transitionTo(Routes.FRIENDS);
|
||||
} else {
|
||||
GuildActionCreators.selectGuild(ME);
|
||||
SelectedChannelActionCreators.selectChannel(ME, null);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/GuildActionCreators.js
|
|
@ -0,0 +1,270 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import i18n from '../i18n';
|
||||
import {ActionTypes, Endpoints, Layers} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import mfaInterceptor from '../utils/MFAInterceptionUtils';
|
||||
import {pushLayer} from './LayerActionCreators';
|
||||
|
||||
export type GuildUpdate = {
|
||||
name?: string,
|
||||
icon?: string,
|
||||
splash?: string,
|
||||
region?: string,
|
||||
afkChannelId?: ?string,
|
||||
afkTimeout?: number,
|
||||
verificationLevel?: number,
|
||||
defaultMessageNotifications?: string,
|
||||
explicitContentFilter?: number,
|
||||
};
|
||||
|
||||
export default {
|
||||
init(guildId: string, section: ?string = null) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_INIT,
|
||||
guildId,
|
||||
section,
|
||||
});
|
||||
},
|
||||
|
||||
open(guildId: string, section?: string) {
|
||||
if (__IOS__) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_OPEN,
|
||||
guildId,
|
||||
section,
|
||||
});
|
||||
} else {
|
||||
this.init(guildId, section);
|
||||
pushLayer(Layers.GUILD_SETTINGS);
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
setSection(section: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_SECTION,
|
||||
section,
|
||||
});
|
||||
},
|
||||
|
||||
setSearchQuery(searchQuery: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_SEARCH_QUERY,
|
||||
searchQuery,
|
||||
});
|
||||
},
|
||||
|
||||
selectRole(roleId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLE_SELECT,
|
||||
roleId,
|
||||
});
|
||||
},
|
||||
|
||||
updateEmbed(guildId: string, enabled: boolean, channelId: string) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_EMBED(guildId),
|
||||
// eslint-disable-next-line camelcase
|
||||
body: {enabled, channel_id: channelId},
|
||||
}).then(res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_EMBED,
|
||||
guildId: guildId,
|
||||
enabled: res.body.enabled,
|
||||
channelId: res.body['channel_id'],
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updateMFALevel({guildId, level, isEnabled}: {guildId: string, level: number, isEnabled: boolean}) {
|
||||
mfaInterceptor(
|
||||
{
|
||||
title: i18n.Messages.GUILD_SECURITY_REQ_MFA_LABEL,
|
||||
actionText: isEnabled
|
||||
? i18n.Messages.GUILD_SECURITY_REQ_MFA_TURN_OFF
|
||||
: i18n.Messages.GUILD_SECURITY_REQ_MFA_TURN_ON,
|
||||
},
|
||||
extraBody =>
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILD_MFA(guildId),
|
||||
body: {level, ...extraBody},
|
||||
})
|
||||
).then(res => Dispatcher.dispatch({type: ActionTypes.GUILD_SETTINGS_SET_MFA_SUCCESS, level: res.body.level}));
|
||||
},
|
||||
|
||||
// For iOS only
|
||||
updateIcon(guildId: string, icon: string) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD(guildId),
|
||||
body: {icon},
|
||||
}).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_UPDATE,
|
||||
icon,
|
||||
});
|
||||
},
|
||||
res => Dispatcher.dispatch({type: ActionTypes.GUILD_SETTINGS_SUBMIT_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
// For iOS only
|
||||
cancelChanges(guildId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_CANCEL_CHANGES,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
updateGuild(update: GuildUpdate) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_UPDATE,
|
||||
...update,
|
||||
});
|
||||
},
|
||||
|
||||
saveGuild(
|
||||
guildId: string,
|
||||
{
|
||||
name,
|
||||
icon,
|
||||
splash,
|
||||
region,
|
||||
afkChannelId,
|
||||
afkTimeout,
|
||||
verificationLevel,
|
||||
defaultMessageNotifications,
|
||||
explicitContentFilter,
|
||||
}: GuildUpdate
|
||||
) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SUBMIT,
|
||||
});
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD(guildId),
|
||||
body: {
|
||||
name,
|
||||
icon,
|
||||
splash,
|
||||
region,
|
||||
/* eslint-disable camelcase */
|
||||
afk_channel_id: afkChannelId,
|
||||
afk_timeout: afkTimeout,
|
||||
verification_level: verificationLevel,
|
||||
default_message_notifications: defaultMessageNotifications,
|
||||
explicit_content_filter: explicitContentFilter,
|
||||
/* eslint-enable camelcase */
|
||||
},
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.GUILD_SETTINGS_SUBMIT_SUCCESS}),
|
||||
res => Dispatcher.dispatch({type: ActionTypes.GUILD_SETTINGS_SUBMIT_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
updateGuildModeration(guildId: string, update: {verificationLevel: number, explicitContentFilter: number}) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD(guildId),
|
||||
body: {
|
||||
/* eslint-disable camelcase */
|
||||
verification_level: update.verificationLevel,
|
||||
explicit_content_filter: update.explicitContentFilter,
|
||||
/* eslint-enable camelcase */
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
transferOwnership(guildId: string, ownerId: string) {
|
||||
return mfaInterceptor(
|
||||
{
|
||||
title: i18n.Messages.TRANSFER_OWNERSHIP,
|
||||
actionText: i18n.Messages.TRANSFER_OWNERSHIP,
|
||||
},
|
||||
extraBody =>
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.GUILD(guildId),
|
||||
// eslint-disable-next-line camelcase
|
||||
body: {owner_id: ownerId, ...extraBody},
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
deleteGuild(guildId: string, guildName: string) {
|
||||
mfaInterceptor(
|
||||
{
|
||||
title: i18n.Messages.DELETE_SERVER_TITLE.format({name: guildName}),
|
||||
actionText: i18n.Messages.DELETE_SERVER,
|
||||
},
|
||||
body =>
|
||||
HTTPUtils.post({
|
||||
url: `${Endpoints.GUILD(guildId)}/delete`,
|
||||
body,
|
||||
})
|
||||
).then(() => this.close());
|
||||
},
|
||||
|
||||
leaveGuild(guildId: string) {
|
||||
HTTPUtils.delete(`${Endpoints.ME}${Endpoints.GUILD(guildId)}`).then(() => this.close());
|
||||
},
|
||||
|
||||
updateMemberRoles(guildId: string, userId: string, roles: Array<string>) {
|
||||
HTTPUtils.patch({
|
||||
url: `${Endpoints.GUILD_MEMBERS(guildId)}/${userId}`,
|
||||
body: {roles},
|
||||
});
|
||||
},
|
||||
|
||||
enableIntegration(guildId: string, type: string, id: string) {
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILD_INTEGRATIONS(guildId),
|
||||
body: {type, id},
|
||||
});
|
||||
},
|
||||
|
||||
disableIntegration(guildId: string, integrationId: string) {
|
||||
HTTPUtils.delete(`${Endpoints.GUILD_INTEGRATIONS(guildId)}/${integrationId}`);
|
||||
},
|
||||
|
||||
updateIntegration(
|
||||
guildId: string,
|
||||
integrationId: string,
|
||||
expireBehavior: number,
|
||||
expireGracePeriod: number,
|
||||
enableEmoticons: boolean
|
||||
) {
|
||||
const params = {
|
||||
/* eslint-disable camelcase */
|
||||
expire_behavior: expireBehavior,
|
||||
expire_grace_period: expireGracePeriod,
|
||||
enable_emoticons: enableEmoticons,
|
||||
/* eslint-enable camelcase */
|
||||
};
|
||||
|
||||
HTTPUtils.patch({
|
||||
url: `${Endpoints.GUILD_INTEGRATIONS(guildId)}/${integrationId}`,
|
||||
body: params,
|
||||
});
|
||||
},
|
||||
|
||||
enableCustomEmoji(guildId: string, integrationId: string, enableCustomEmoji: boolean) {
|
||||
HTTPUtils.patch({
|
||||
url: `${Endpoints.GUILD_INTEGRATIONS(guildId)}/${integrationId}/custom_emoji`,
|
||||
body: {enable: enableCustomEmoji},
|
||||
});
|
||||
},
|
||||
|
||||
syncIntegration(guildId: string, integrationId: string) {
|
||||
HTTPUtils.post(`${Endpoints.GUILD_INTEGRATIONS(guildId)}/${integrationId}/sync`);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/GuildSettingsActionCreators.js
|
|
@ -0,0 +1,99 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import GuildActionCreators from './GuildActionCreators';
|
||||
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
import type {GuildRole} from '../flow/Server';
|
||||
|
||||
export function updateRoleSort(roles: Array<string>) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLES_SORT_UPDATE,
|
||||
roles,
|
||||
});
|
||||
}
|
||||
|
||||
export function init() {
|
||||
Dispatcher.dispatch({type: ActionTypes.GUILD_SETTINGS_ROLES_INIT});
|
||||
}
|
||||
|
||||
export function updateRolePermissions(id: string, flag: number, allow: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLES_UPDATE_PERMISSIONS,
|
||||
id,
|
||||
flag,
|
||||
allow,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateRoleName(id: string, name: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLES_UPDATE_NAME,
|
||||
id,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateRoleColor(id: string, color: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLES_UPDATE_COLOR,
|
||||
id,
|
||||
color,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleRoleSettings(id: string, hoist: boolean, mentionable: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLES_UPDATE_SETTINGS,
|
||||
id,
|
||||
hoist,
|
||||
mentionable,
|
||||
});
|
||||
}
|
||||
|
||||
export function saveRoleSettings(
|
||||
guildId: string,
|
||||
roles?: Array<GuildRole>,
|
||||
deltas?: Array<{id: string, position: number}>
|
||||
) {
|
||||
Dispatcher.dispatch({type: ActionTypes.GUILD_SETTINGS_ROLES_SUBMITTING});
|
||||
// Save all role update changes consecutively, starting with order
|
||||
return new Promise(resolve => {
|
||||
const chain = () => {
|
||||
if (roles == null || roles.length === 0) {
|
||||
return resolve();
|
||||
}
|
||||
const role = roles.pop();
|
||||
if (role == null || role.name === '') {
|
||||
return chain();
|
||||
}
|
||||
|
||||
GuildActionCreators.updateRole(
|
||||
guildId,
|
||||
role.id,
|
||||
role.name,
|
||||
role.permissions,
|
||||
role.color,
|
||||
role.hoist,
|
||||
role.mentionable,
|
||||
true
|
||||
).then(chain, chain);
|
||||
};
|
||||
|
||||
if (deltas != null && deltas.length > 0) {
|
||||
GuildActionCreators.batchRoleUpdate(guildId, deltas).then(chain, chain);
|
||||
} else {
|
||||
chain();
|
||||
}
|
||||
}).then(() =>
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_ROLES_SAVE_SUCCESS,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/GuildSettingsRolesActionCreators.js
|
|
@ -0,0 +1,52 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_VANITY_URL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
resetCode() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_VANITY_URL_RESET,
|
||||
});
|
||||
},
|
||||
|
||||
setCode(code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_VANITY_URL_SET,
|
||||
code,
|
||||
});
|
||||
},
|
||||
|
||||
saveCode(guildId: string, code: string) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.GUILD_VANITY_URL(guildId),
|
||||
body: {
|
||||
code,
|
||||
},
|
||||
}).then(
|
||||
({body}: {body: {code: string}}) => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_SET_VANITY_URL,
|
||||
code: body.code,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.GUILD_SETTINGS_VANITY_URL_ERROR,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/GuildSettingsVanityURLActionCreators.js
|
|
@ -0,0 +1,23 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import HelpdeskUtils from '../utils/HelpdeskUtils';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
loadFeaturedArticles() {
|
||||
HTTPUtils.get(HelpdeskUtils.getFeaturedArticlesJsonURL()).then(res => {
|
||||
const articles = res.body.articles || [];
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.FEATURED_HELP_ARTICLES,
|
||||
articles,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/HelpdeskActionCreators.js
|
|
@ -0,0 +1,27 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
saveCurrentText(channelId: string, textSoFar: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.IN_PROGRESS_TEXT_SAVE,
|
||||
channelId,
|
||||
textSoFar,
|
||||
});
|
||||
},
|
||||
|
||||
changeCurrentText(channelId: string, textSoFar: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.IN_PROGRESS_TEXT_CHANGE,
|
||||
channelId,
|
||||
textSoFar,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/InProgressTextCreators.js
|
|
@ -0,0 +1,19 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
move(x: number, y: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INCOMING_CALL_MOVE,
|
||||
x,
|
||||
y,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/IncomingCallActionCreators.js
|
|
@ -0,0 +1,233 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints, ChannelTypes, Routes, ME, RPCCommands} from '../Constants';
|
||||
import SelectedChannelActionCreators from './SelectedChannelActionCreators';
|
||||
import ChannelStore from '../stores/ChannelStore';
|
||||
import ProtocolUtils from '../utils/web/ProtocolUtils';
|
||||
import RouterUtils from '../utils/RouterUtils';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import platform from 'platform';
|
||||
import AuthenticationStore from '../stores/AuthenticationStore';
|
||||
import AnalyticsUtils from '../utils/AnalyticsUtils';
|
||||
|
||||
const ANDROID_HREF =
|
||||
'intent://invite/%s#Intent;scheme=discord;package=com.discord;S.browser_fallback_url=http://discordapp.com;end';
|
||||
|
||||
export type Invite = {
|
||||
code: string,
|
||||
channel?: {
|
||||
id: string,
|
||||
name: string,
|
||||
},
|
||||
guild?: {
|
||||
id: string,
|
||||
name: string,
|
||||
},
|
||||
temporary?: boolean,
|
||||
revoked?: boolean,
|
||||
max_uses?: number,
|
||||
max_age?: number,
|
||||
created_at: ?number,
|
||||
};
|
||||
|
||||
export default {
|
||||
createInvite(channelId: string, options: any = {}, location: string): any {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.INSTANT_INVITES(channelId),
|
||||
body: options,
|
||||
context: {location},
|
||||
}).then(res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INSTANT_INVITE_CREATE_SUCCESS,
|
||||
channelId,
|
||||
invite: res.body,
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
},
|
||||
|
||||
revokeInvite(code: string) {
|
||||
HTTPUtils.delete(`${Endpoints.INVITE}/${code}`).then(() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INSTANT_INVITE_REVOKE_SUCCESS,
|
||||
code,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
resolveInvite(code: string, location?: string, extraOptions?: any): Promise<*> {
|
||||
Dispatcher.maybeDispatch({
|
||||
type: ActionTypes.INVITE_RESOLVE,
|
||||
code,
|
||||
});
|
||||
return HTTPUtils.get({
|
||||
url: `${Endpoints.INVITE}/${code}`,
|
||||
query: extraOptions,
|
||||
}).then(
|
||||
res => {
|
||||
// While this may be a large number of events, we need it if we want to see
|
||||
// success rates. It also makes tracking viewing the instant invite page
|
||||
// unnecessary, and lets us also see how people are using the other ways
|
||||
// of accepting invites
|
||||
const invite = res.body;
|
||||
|
||||
if (location) {
|
||||
AnalyticsUtils.track('resolve_invite', {
|
||||
resolved: true,
|
||||
guild_id: invite.guild.id, // eslint-disable-line camelcase
|
||||
channel_id: invite.channel.id, // eslint-disable-line camelcase
|
||||
channel_type: invite.channel.type, // eslint-disable-line camelcase
|
||||
inviter_id: invite.inviter ? invite.inviter.id : null, // eslint-disable-line camelcase
|
||||
code,
|
||||
location,
|
||||
authenticated: AuthenticationStore.isAuthenticated(),
|
||||
});
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.INVITE_RESOLVE_SUCCESS, invite, code});
|
||||
return {invite, code};
|
||||
},
|
||||
() => {
|
||||
if (location) {
|
||||
AnalyticsUtils.track('resolve_invite', {
|
||||
resolved: false,
|
||||
code,
|
||||
location,
|
||||
authenticated: AuthenticationStore.isAuthenticated(),
|
||||
});
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({type: ActionTypes.INVITE_RESOLVE_FAILURE, code});
|
||||
return {invite: null, code};
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
acceptInvite(code: string, userLocation: string, callback: (invite: Invite) => void) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.INVITE_ACCEPT,
|
||||
code,
|
||||
});
|
||||
|
||||
HTTPUtils.post({
|
||||
url: `${Endpoints.INVITE}/${code}`,
|
||||
context: {
|
||||
Location: userLocation,
|
||||
},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_ACCEPT_SUCCESS,
|
||||
invite: res.body,
|
||||
code,
|
||||
});
|
||||
if (callback) {
|
||||
callback(res.body);
|
||||
} else {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_APP_NOT_OPENED,
|
||||
code,
|
||||
});
|
||||
}
|
||||
},
|
||||
() => Dispatcher.dispatch({type: ActionTypes.INVITE_ACCEPT_FAILURE, code})
|
||||
);
|
||||
},
|
||||
|
||||
acceptInviteAndTransitionToInviteChannel(code: string, userLocation: string) {
|
||||
this.acceptInvite(code, userLocation, invite => {
|
||||
if (invite.channel != null) {
|
||||
this.transitionToInviteChannelSync(invite.channel.id);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
transitionToInviteChannel(guildId: string, channelId: string, channelType: number = ChannelTypes.GUILD_TEXT) {
|
||||
if (channelType === ChannelTypes.GUILD_VOICE) {
|
||||
SelectedChannelActionCreators.selectVoiceChannel(guildId, channelId);
|
||||
RouterUtils.transitionTo(Routes.CHANNEL(guildId, guildId));
|
||||
} else {
|
||||
RouterUtils.transitionTo(Routes.CHANNEL(guildId, channelId));
|
||||
}
|
||||
},
|
||||
|
||||
transitionToInviteChannelSync(channelId: string) {
|
||||
ChannelStore.addConditionalChangeListener(() => {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
if (channel != null) {
|
||||
this.transitionToInviteChannel(channel.getGuildId() || ME, channel.id, channel.type);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
openNativeAppModal(code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_NATIVE_APP_MODAL_OPENING,
|
||||
code,
|
||||
});
|
||||
|
||||
if (!__IOS__) {
|
||||
const RPCSocket = require('../lib/RPCSocket');
|
||||
|
||||
// $FlowFixMe
|
||||
RPCSocket.request(RPCCommands.INVITE_BROWSER, {code})
|
||||
.then(({code}) => this.nativeModalOpened(code))
|
||||
.catch(() => this.nativeModalOpenFailed(code))
|
||||
// $FlowFixMe
|
||||
.then(() => RPCSocket.disconnect());
|
||||
}
|
||||
},
|
||||
|
||||
nativeModalOpened(code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_NATIVE_APP_MODAL_OPENED,
|
||||
code,
|
||||
});
|
||||
},
|
||||
|
||||
nativeModalOpenFailed(code: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_NATIVE_APP_MODAL_OPEN_FAILED,
|
||||
code,
|
||||
});
|
||||
},
|
||||
|
||||
openApp(code: string, channelId: string) {
|
||||
Dispatcher.maybeDispatch({
|
||||
type: ActionTypes.INVITE_APP_OPENING,
|
||||
code,
|
||||
});
|
||||
|
||||
let path;
|
||||
if (platform.os.family === 'Android') {
|
||||
path = ANDROID_HREF.replace(/%s/g, code);
|
||||
} else {
|
||||
if (platform.os.family === 'iOS') {
|
||||
path = Routes.INVITE(code);
|
||||
} else {
|
||||
path = Routes.INVITE_PROXY(channelId);
|
||||
}
|
||||
|
||||
if (path[0] === '#') {
|
||||
path = path.slice(1);
|
||||
}
|
||||
|
||||
path = `discord://${path}`;
|
||||
}
|
||||
|
||||
ProtocolUtils.launch(path, supported => {
|
||||
Dispatcher.dispatch({
|
||||
type: supported ? ActionTypes.INVITE_APP_OPENED : ActionTypes.INVITE_APP_NOT_OPENED,
|
||||
code,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/InstantInviteActionCreators.js
|
|
@ -0,0 +1,30 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
search(integration: string, query: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.INTEGRATION_QUERY, integration, query});
|
||||
HTTPUtils.get({
|
||||
url: `${Endpoints.INTEGRATIONS}/${integration}/search`,
|
||||
query: {q: query},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INTEGRATION_QUERY_SUCCESS,
|
||||
integration,
|
||||
query,
|
||||
results: res.body,
|
||||
});
|
||||
},
|
||||
() => Dispatcher.dispatch({type: ActionTypes.INTEGRATION_QUERY_FAILURE, integration, query})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/IntegrationActionCreators.js
|
|
@ -0,0 +1,50 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import i18n from '../i18n';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import InstantInviteActionCreators from './InstantInviteActionCreators';
|
||||
|
||||
export default {
|
||||
acceptInvite(code: string, userLocation: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.INVITE_ACCEPT,
|
||||
code,
|
||||
});
|
||||
|
||||
HTTPUtils.post({
|
||||
url: `${Endpoints.INVITE}/${code}`,
|
||||
context: {
|
||||
Location: userLocation,
|
||||
},
|
||||
}).then(
|
||||
res => {
|
||||
const invite = res.body;
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_ACCEPT_SUCCESS,
|
||||
invite,
|
||||
code,
|
||||
});
|
||||
InstantInviteActionCreators.transitionToInviteChannel(invite.guild.id, invite.channel.id, invite.channel.type);
|
||||
Dispatcher.dispatch({type: ActionTypes.INVITE_MODAL_CLOSE});
|
||||
},
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.INVITE_ACCEPT_FAILURE, code});
|
||||
const message = (res && res.body && res.body.message) || i18n.Messages.INVITE_MODAL_ERROR_DEFAULT;
|
||||
Dispatcher.dispatch({type: ActionTypes.INVITE_MODAL_ERROR, message: message});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.INVITE_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/InviteModalActionCreators.js
|
|
@ -0,0 +1,35 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import type {KeybindShortcut} from '../flow/Client';
|
||||
|
||||
export type Keybind = {
|
||||
id: string,
|
||||
action: string,
|
||||
shortcut: KeybindShortcut,
|
||||
managed: boolean,
|
||||
};
|
||||
|
||||
export default {
|
||||
addKeybind() {
|
||||
Dispatcher.dispatch({type: ActionTypes.KEYBINDS_ADD_KEYBIND});
|
||||
},
|
||||
|
||||
setKeybind(keybind: Keybind) {
|
||||
Dispatcher.dispatch({type: ActionTypes.KEYBINDS_SET_KEYBIND, keybind});
|
||||
},
|
||||
|
||||
deleteKeybind(id: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.KEYBINDS_DELETE_KEYBIND, id});
|
||||
},
|
||||
|
||||
enableAll(enable: boolean) {
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.KEYBINDS_ENABLE_ALL_KEYBINDS, enable});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/KeybindsActionCreators.js
|
|
@ -0,0 +1,29 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
show() {
|
||||
// Not sure this is the right way to do this...
|
||||
this.deactivateRagingDemon();
|
||||
Dispatcher.dispatch({type: ActionTypes.SHOW_KEYBOARD_SHORTCUTS});
|
||||
},
|
||||
|
||||
hide() {
|
||||
Dispatcher.dispatch({type: ActionTypes.HIDE_KEYBOARD_SHORTCUTS});
|
||||
},
|
||||
|
||||
activateRagingDemon() {
|
||||
Dispatcher.dispatch({type: ActionTypes.ACTIVATE_RAGING_DEMON});
|
||||
},
|
||||
|
||||
deactivateRagingDemon() {
|
||||
Dispatcher.dispatch({type: ActionTypes.DEACTIVATE_RAGING_DEMON});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/KeyboardShortcutModalActionCreators.js
|
|
@ -0,0 +1,27 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export function pushLayer(component: string) {
|
||||
// DirtyDispatch because pushLayer can be called on connection open for forced verification
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.LAYER_PUSH,
|
||||
component,
|
||||
});
|
||||
}
|
||||
|
||||
export function popLayer() {
|
||||
// Must be a dirtyDispatch because popLayer can be called on channel deletion
|
||||
// if someone is viewing channel settings
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.LAYER_POP});
|
||||
}
|
||||
|
||||
export function popAllLayers() {
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.LAYER_POP_ALL});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/LayerActionCreators.js
|
|
@ -0,0 +1,58 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import i18n from '../i18n';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import mfaInterceptor from '../utils/MFAInterceptionUtils';
|
||||
|
||||
export default {
|
||||
enable({code, secret}: {code: string, secret: string}) {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.MFA_TOTP_ENABLE,
|
||||
body: {code, secret},
|
||||
}).then(res =>
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MFA_ENABLE_SUCCESS,
|
||||
token: res.body.token,
|
||||
codes: res.body['backup_codes'],
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
disable() {
|
||||
mfaInterceptor(
|
||||
{
|
||||
title: i18n.Messages.TWO_FA_DISABLE,
|
||||
actionText: i18n.Messages.TWO_FA_REMOVE,
|
||||
},
|
||||
body =>
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.MFA_TOTP_DISABLE,
|
||||
body,
|
||||
}),
|
||||
true
|
||||
).then(res => Dispatcher.dispatch({type: ActionTypes.MFA_DISABLE_SUCCESS, token: res.body.token}));
|
||||
},
|
||||
|
||||
viewBackupCodes(password: string, regenerate: boolean = false): Promise {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.MFA_CODES,
|
||||
body: {password, regenerate},
|
||||
}).then(res => Dispatcher.dispatch({type: ActionTypes.MFA_VIEW_BACKUP_CODES, codes: res.body['backup_codes']}));
|
||||
},
|
||||
|
||||
clearBackupCodes() {
|
||||
// With the new settings screen, this can be called on a logout
|
||||
// unmount action, therefore we need to make it dirty to avoid
|
||||
// a dispatch firing from within a dispatch
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.MFA_CLEAR_BACKUP_CODES,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/MFAActionCreators.js
|
|
@ -0,0 +1,18 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
function trustDomain(url: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MASKED_LINK_ADD_TRUSTED_DOMAIN,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
export {trustDomain};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/MaskedLinkActionCreators.js
|
|
@ -0,0 +1,90 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {Endpoints, ActionTypes, MAX_MENTIONS_PER_FETCH} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
setGuildFilter({
|
||||
guildFilter,
|
||||
roleFilter,
|
||||
everyoneFilter,
|
||||
}: {
|
||||
guildFilter: string,
|
||||
roleFilter: boolean,
|
||||
everyoneFilter: boolean,
|
||||
}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.SET_RECENT_MENTIONS_FILTER,
|
||||
guildFilter,
|
||||
roleFilter,
|
||||
everyoneFilter,
|
||||
});
|
||||
},
|
||||
|
||||
truncateMentions(size: number) {
|
||||
// This can be triggered while unloading the popout, which can be triggered by another action.
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.TRUNCATE_MENTIONS,
|
||||
size,
|
||||
});
|
||||
},
|
||||
|
||||
fetchRecentMentions(
|
||||
before: ?string,
|
||||
limit: ?number = MAX_MENTIONS_PER_FETCH,
|
||||
guildId: ?string = null,
|
||||
roles: ?boolean = true,
|
||||
everyone: ?boolean = true
|
||||
) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.LOAD_RECENT_MENTIONS,
|
||||
guildId,
|
||||
});
|
||||
|
||||
HTTPUtils.get({
|
||||
url: Endpoints.MENTIONS,
|
||||
query: {
|
||||
before,
|
||||
limit,
|
||||
guild_id: guildId, // eslint-disable-line camelcase
|
||||
roles,
|
||||
everyone,
|
||||
},
|
||||
retries: 2,
|
||||
}).then(
|
||||
({body: messages}) => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_RECENT_MENTIONS_SUCCESS,
|
||||
messages: messages,
|
||||
isAfter: before != null,
|
||||
hasMoreAfter: messages.length >= MAX_MENTIONS_PER_FETCH,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_RECENT_MENTIONS_FAILURE,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
deleteRecentMention(messageId: string) {
|
||||
HTTPUtils.delete({
|
||||
url: Endpoints.MENTIONS_MESSAGE_ID(messageId),
|
||||
retries: 2,
|
||||
});
|
||||
|
||||
// This is an optimistic delete. The server will also dispatch this so that
|
||||
// all other connected clients also delete the recent mention.
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.RECENT_MENTION_DELETE,
|
||||
id: messageId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/MentionActionCreators.js
|
|
@ -0,0 +1,353 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import lodash from 'lodash';
|
||||
import MessageUtils from '../utils/MessageUtils';
|
||||
import type {ParsedMessage} from '../utils/MessageUtils';
|
||||
import ReadStateStore from '../stores/ReadStateStore';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import MessageQueue from '../lib/MessageQueue';
|
||||
import i18n from '../i18n';
|
||||
import ChannelRecord from '../records/ChannelRecord';
|
||||
import AppAnalyticsUtils from '../utils/AppAnalyticsUtils';
|
||||
import {
|
||||
ActionTypes,
|
||||
Endpoints,
|
||||
ME,
|
||||
MAX_MESSAGES_PER_CHANNEL,
|
||||
CLYDE_ERROR_MESSAGES,
|
||||
Permissions,
|
||||
AbortCodes,
|
||||
} from '../Constants';
|
||||
import MessageStore from '../stores/MessageStore';
|
||||
import UserStore from '../stores/UserStore';
|
||||
import GuildStore from '../stores/GuildStore';
|
||||
import ChannelStore from '../stores/ChannelStore';
|
||||
import PermissionUtils from '../utils/PermissionUtils';
|
||||
import type {Message} from '../flow/Server';
|
||||
import type {JumpDescriptor} from '../flow/Client';
|
||||
|
||||
let seenEmojiWarning = false;
|
||||
|
||||
export default {
|
||||
receiveMessage(channelId: string, message: Message, optimistic: boolean = false) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.MESSAGE_CREATE,
|
||||
channelId,
|
||||
message,
|
||||
optimistic,
|
||||
});
|
||||
},
|
||||
|
||||
sendBotMessage(channelId: string, content: string) {
|
||||
this.receiveMessage(channelId, MessageUtils.createBotMessage(channelId, content));
|
||||
},
|
||||
|
||||
sendClydeError(channelId: string, code: number = 0) {
|
||||
let message;
|
||||
if (code === AbortCodes.EXPLICIT_CONTENT) {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
if (channel == null) {
|
||||
return;
|
||||
}
|
||||
if (channel.isDM()) {
|
||||
message = i18n.Messages.BOT_DM_EXPLICIT_CONTENT.format({name: channel.toString()});
|
||||
} else if (channel.isGroupDM()) {
|
||||
message = i18n.Messages.BOT_GDM_EXPLICIT_CONTENT.format({name: channel.toString()});
|
||||
} else {
|
||||
const guild = GuildStore.getGuild(channel.getGuildId());
|
||||
if (guild == null) {
|
||||
return;
|
||||
}
|
||||
message = i18n.Messages.BOT_GUILD_EXPLICIT_CONTENT.format({name: guild.toString()});
|
||||
}
|
||||
} else {
|
||||
message = i18n.Messages[CLYDE_ERROR_MESSAGES[code] || CLYDE_ERROR_MESSAGES.DEFAULT];
|
||||
}
|
||||
this.sendBotMessage(channelId, message);
|
||||
},
|
||||
|
||||
truncateMessages(channelId: string, truncateBottom: boolean, truncateTop: boolean) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.TRUNCATE_MESSAGES,
|
||||
channelId,
|
||||
truncateBottom,
|
||||
truncateTop,
|
||||
});
|
||||
},
|
||||
|
||||
jumpToPresent(channelId: string, limit: number) {
|
||||
this.trackJump(channelId, null, 'Present');
|
||||
|
||||
const jump = {
|
||||
present: true,
|
||||
};
|
||||
|
||||
if (MessageStore.hasPresent(channelId)) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_MESSAGES_SUCCESS_CACHED,
|
||||
jump,
|
||||
channelId,
|
||||
limit,
|
||||
});
|
||||
} else {
|
||||
this.fetchMessages(channelId, null, null, limit, jump);
|
||||
}
|
||||
},
|
||||
|
||||
trackJump(channelId: string, messageId: ?string, context: string, extraProperties: ?Object) {
|
||||
AppAnalyticsUtils.trackWithMetadata('jump', {
|
||||
context,
|
||||
channel_id: channelId, // eslint-disable-line camelcase
|
||||
message_id: messageId, // eslint-disable-line camelcase
|
||||
...extraProperties,
|
||||
});
|
||||
},
|
||||
|
||||
jumpToMessage(
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
flash?: boolean = true,
|
||||
context?: string,
|
||||
extraProperties: ?Object = null
|
||||
) {
|
||||
if (typeof context === 'string') {
|
||||
this.trackJump(channelId, messageId, context, extraProperties);
|
||||
}
|
||||
|
||||
this.fetchMessages(channelId, null, null, MAX_MESSAGES_PER_CHANNEL, {messageId, flash});
|
||||
},
|
||||
|
||||
// Tell the stores that the channel is probably going to be loaded, and do any kind of loading
|
||||
// ahead of time. I.e. fetching messages for a channel we know might be selected soon.
|
||||
prefetchMessages(guildId: string, channelId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PREFETCH_MESSAGES,
|
||||
guildId: guildId === ME ? null : guildId,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
fetchMessages(channelId: string, before: ?string, after: ?string, limit: number, jump?: JumpDescriptor) {
|
||||
if (this._tryFetchMessagesCached(channelId, before, after, limit, jump)) {
|
||||
return;
|
||||
}
|
||||
// The only time this happens during a dispatch is when the channel is loaded for the first
|
||||
// time, but there is no need to dispatch this because channels are loading by default.
|
||||
Dispatcher.maybeDispatch({
|
||||
type: ActionTypes.LOAD_MESSAGES,
|
||||
channelId,
|
||||
jump,
|
||||
});
|
||||
|
||||
const around = jump && jump.messageId;
|
||||
|
||||
HTTPUtils.get({
|
||||
url: Endpoints.MESSAGES(channelId),
|
||||
query: {before, after, limit, around},
|
||||
retries: 2,
|
||||
}).then(
|
||||
res => {
|
||||
const messages = res.body;
|
||||
const isBefore = before != null;
|
||||
const isAfter = after != null;
|
||||
const isReplacement = before == null && after == null;
|
||||
|
||||
let hasMoreBefore = around != null || (messages.length === limit && (isBefore || isReplacement));
|
||||
let hasMoreAfter = around != null || (isAfter && messages.length === limit);
|
||||
if (around) {
|
||||
const halfLength = limit / 2;
|
||||
|
||||
// the messages are in reverse order, so the index needs to be inverted
|
||||
const targetIndex = messages.length - 1 - lodash.findIndex(messages, ({id}) => id === around);
|
||||
|
||||
// If the target message has less than halfLength messages before it, then we are at
|
||||
// the beginning of the message history
|
||||
if (targetIndex < halfLength) {
|
||||
hasMoreBefore = false;
|
||||
}
|
||||
|
||||
// if fewer than limit + 1 messages are returned, and the target has halfLength messages before it,
|
||||
// then we're at the end of the message history
|
||||
if (targetIndex >= halfLength && messages.length < limit + 1) {
|
||||
hasMoreAfter = false;
|
||||
}
|
||||
// If we still have more after, let's do a quick sanity check to see if the last message id
|
||||
// loaded is the last message ID according to the read state store. If it is, we're not missing
|
||||
// any messages.
|
||||
if (hasMoreAfter && messages.length) {
|
||||
const lastMessageId = ReadStateStore.lastMessageId(channelId);
|
||||
if (messages[0].id == lastMessageId) {
|
||||
hasMoreAfter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_MESSAGES_SUCCESS,
|
||||
channelId,
|
||||
messages,
|
||||
isBefore,
|
||||
isAfter,
|
||||
hasMoreBefore,
|
||||
hasMoreAfter,
|
||||
jump,
|
||||
});
|
||||
},
|
||||
() => Dispatcher.dispatch({type: ActionTypes.LOAD_MESSAGES_FAILURE, channelId})
|
||||
);
|
||||
},
|
||||
|
||||
_tryFetchMessagesCached(channelId: string, before: ?string, after: ?string, limit: number, jump?: JumpDescriptor) {
|
||||
// citron note: this can be invoked during a channel switch, so all dispatches need to be dirty
|
||||
const messages = MessageStore.getMessages(channelId);
|
||||
|
||||
// if we're jumping, check if we already have the target message loaded
|
||||
if (jump && jump.messageId && messages.has(jump.messageId, false)) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.LOAD_MESSAGES_SUCCESS_CACHED,
|
||||
channelId,
|
||||
jump,
|
||||
limit,
|
||||
});
|
||||
|
||||
return true;
|
||||
} else if (before && messages.hasBeforeCached(before)) {
|
||||
// we're not jumping, but check if we already have the previous block cached
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.LOAD_MESSAGES_SUCCESS_CACHED,
|
||||
channelId,
|
||||
before,
|
||||
limit,
|
||||
});
|
||||
|
||||
return true;
|
||||
} else if (after && messages.hasAfterCached(after)) {
|
||||
// we're not jumping, but check if we already have the next block cached
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.LOAD_MESSAGES_SUCCESS_CACHED,
|
||||
channelId,
|
||||
after,
|
||||
limit,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
sendMessage(channelId: string, message: ParsedMessage, whenReady?: boolean = true) {
|
||||
if (whenReady) {
|
||||
MessageStore.whenReady(channelId, () => this._sendMessage(channelId, message));
|
||||
} else {
|
||||
this._sendMessage(channelId, message);
|
||||
}
|
||||
},
|
||||
|
||||
_sendMessage(channelId: string, {content, invalidEmojis, tts = false}: ParsedMessage) {
|
||||
// Don't send empty messages.
|
||||
if (!content) return;
|
||||
|
||||
const message = MessageUtils.createMessage(channelId, content, tts);
|
||||
this.receiveMessage(channelId, message, true);
|
||||
|
||||
if (!seenEmojiWarning && invalidEmojis && invalidEmojis.length > 0) {
|
||||
seenEmojiWarning = true;
|
||||
const user = UserStore.getCurrentUser();
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
if (!__IOS__ && PermissionUtils.can(Permissions.USE_EXTERNAL_EMOJIS, user, channel)) {
|
||||
this.sendBotMessage(channelId, i18n.Messages.INVALID_EMOJI_BODY_UPGRADE);
|
||||
} else {
|
||||
this.sendBotMessage(channelId, i18n.Messages.INVALID_EMOJI_BODY);
|
||||
}
|
||||
}
|
||||
|
||||
MessageQueue.enqueue(
|
||||
{
|
||||
type: 'send',
|
||||
message: {
|
||||
channelId,
|
||||
content,
|
||||
nonce: message.id,
|
||||
tts,
|
||||
},
|
||||
},
|
||||
res => {
|
||||
if (res.ok) {
|
||||
this.receiveMessage(channelId, res.body);
|
||||
} else {
|
||||
if (res.status >= 400 && res.status < 500 && res.body) {
|
||||
this.sendClydeError(channelId, res.body.code);
|
||||
}
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_SEND_FAILED,
|
||||
messageId: message.id,
|
||||
channelId,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
retrySendMessage(channel: ChannelRecord, messageId: string, content: string, tts: boolean, parse: boolean = false) {
|
||||
this.deleteMessage(channel.id, messageId, true);
|
||||
if (parse) {
|
||||
this.sendMessage(channel.id, MessageUtils.parse(channel, content));
|
||||
} else {
|
||||
this.sendMessage(channel.id, {content, tts});
|
||||
}
|
||||
},
|
||||
|
||||
startEditMessage(channelId: string, messageId: string, parsedContent: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_START_EDIT,
|
||||
channelId,
|
||||
messageId,
|
||||
content: parsedContent,
|
||||
});
|
||||
},
|
||||
|
||||
endEditMessage() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_END_EDIT,
|
||||
});
|
||||
},
|
||||
|
||||
editMessage(channelId: string, messageId: string, {content}: {content: string}) {
|
||||
const message = {channelId, messageId, content};
|
||||
MessageQueue.enqueue({type: 'edit', message}, this.endEditMessage);
|
||||
},
|
||||
|
||||
deleteMessage(channelId: string, messageId: string, local: boolean = false) {
|
||||
if (local) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_DELETE,
|
||||
id: messageId,
|
||||
channelId,
|
||||
});
|
||||
} else {
|
||||
HTTPUtils.delete(`${Endpoints.MESSAGES(channelId)}/${messageId}`).then(() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_DELETE,
|
||||
id: messageId,
|
||||
channelId,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
revealMessage(channelId: string, messageId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_REVEAL,
|
||||
channelId,
|
||||
messageId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/MessageActionCreators.js
|
|
@ -0,0 +1,32 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import lodash from 'lodash';
|
||||
|
||||
export type ModalRender = ReactClass | ((props: Object) => ReactElement);
|
||||
|
||||
export default {
|
||||
push(modal: ModalRender, props?: Object) {
|
||||
const key = lodash.uniqueId('modal');
|
||||
Dispatcher.dispatch({type: ActionTypes.MODAL_PUSH, modal, props, key});
|
||||
return key;
|
||||
},
|
||||
|
||||
update(key: string, props: Object, partial: boolean = true) {
|
||||
Dispatcher.dispatch({type: ActionTypes.MODAL_UPDATE, key, props, partial});
|
||||
},
|
||||
|
||||
pop() {
|
||||
Dispatcher.dispatch({type: ActionTypes.MODAL_POP});
|
||||
},
|
||||
|
||||
popWithKey(key: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.MODAL_POP, key});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ModalActionCreators.js
|
|
@ -0,0 +1,115 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import type UserRecord from '../records/UserRecord';
|
||||
|
||||
function saveAccountChanges(body) {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.ME,
|
||||
body,
|
||||
}).then(
|
||||
res => {
|
||||
const user = res.body;
|
||||
if (user.token) {
|
||||
const token = user.token;
|
||||
delete user.token;
|
||||
Dispatcher.dispatch({type: ActionTypes.UPDATE_TOKEN, token});
|
||||
}
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_UPDATE, user});
|
||||
Dispatcher.dispatch({type: ActionTypes.NEW_USER_FLOW_ACCOUNT_SUCCESS});
|
||||
},
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_ACCOUNT_FAILURE,
|
||||
errors: res.body,
|
||||
});
|
||||
return Promise.reject(res);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
setStep(step: number = -1) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_SET_STEP,
|
||||
step,
|
||||
});
|
||||
},
|
||||
|
||||
updateTemporaryGuild(update: {name?: string, icon?: string, region?: string}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_GUILD_UPDATE,
|
||||
guild: update,
|
||||
});
|
||||
},
|
||||
|
||||
createGuild(name: string, region: string, icon: ?string = null) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_GUILD_SUBMITTED,
|
||||
});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILDS,
|
||||
body: {name, region, icon},
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.NEW_USER_FLOW_GUILD_CREATED}),
|
||||
res => Dispatcher.dispatch({type: ActionTypes.NEW_USER_FLOW_GUILD_FAILURE, errors: res.body})
|
||||
);
|
||||
},
|
||||
|
||||
updateAccount(settings: {email: string, password: string}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_ACCOUNT_UPDATE,
|
||||
settings,
|
||||
});
|
||||
},
|
||||
|
||||
claimAccount(user: UserRecord) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_ACCOUNT_SUBMITTED,
|
||||
});
|
||||
return saveAccountChanges(user);
|
||||
},
|
||||
|
||||
updateAvatar(avatar: string) {
|
||||
return saveAccountChanges({avatar});
|
||||
},
|
||||
|
||||
// New User Onboarding Actions
|
||||
advanceStep() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_ADVANCE_STEP,
|
||||
});
|
||||
},
|
||||
|
||||
skipOnboarding() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_SKIP,
|
||||
});
|
||||
},
|
||||
|
||||
micTestStart() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_MIC_TEST_START,
|
||||
});
|
||||
},
|
||||
|
||||
micTestStop() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_MIC_TEST_STOP,
|
||||
});
|
||||
},
|
||||
|
||||
selectMicInput(id: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NEW_USER_FLOW_SELECT_MIC_INPUT,
|
||||
id,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/NewUserActionCreators.js
|
|
@ -0,0 +1,15 @@
|
|||
/* @flow */
|
||||
|
||||
import {Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
updateNote(userId: string, note: string) {
|
||||
HTTPUtils.put({url: `${Endpoints.NOTES}/${userId}`, body: {note}});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/NoteActionCreators.js
|
|
@ -0,0 +1,31 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
show(noticeType: string, message?: string, buttonText?: string, callback?: Function, id?: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTICE_SHOW,
|
||||
notice: {
|
||||
id,
|
||||
type: noticeType,
|
||||
message,
|
||||
buttonText,
|
||||
callback,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
dismiss(id?: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTICE_DISMISS,
|
||||
id,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/NoticeActionCreators.js
|
|
@ -0,0 +1,72 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, NotificationPermissionTypes} from '../Constants';
|
||||
import NotificationUtils from '../utils/NotificationUtils';
|
||||
import type {NotificationOptions} from '../utils/NotificationUtils';
|
||||
|
||||
export default {
|
||||
setDesktopType(desktopType: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATIONS_SET_DESKTOP_TYPE,
|
||||
desktopType,
|
||||
});
|
||||
},
|
||||
|
||||
setTTSType(ttsType: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATIONS_SET_TTS_TYPE,
|
||||
ttsType,
|
||||
});
|
||||
},
|
||||
|
||||
setDisabledSounds(sounds: Array<string>) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATIONS_SET_DISABLED_SOUNDS,
|
||||
sounds,
|
||||
});
|
||||
},
|
||||
|
||||
setDisableUnreadBadge(disableUnreadBadge: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATIONS_SET_DISABLE_UNREAD_BADGE,
|
||||
disableUnreadBadge,
|
||||
});
|
||||
},
|
||||
|
||||
requestPermission(source: string) {
|
||||
NotificationUtils.requestPermission(enabled => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATIONS_SET_PERMISSION_STATE,
|
||||
enabled: enabled ? NotificationPermissionTypes.ENABLED : NotificationPermissionTypes.BLOCKED,
|
||||
source,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
showNotification(icon: ?string, title: string, body: string, options: NotificationOptions) {
|
||||
// Sometimes notifications are dispatched in stores.
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.NOTIFICATION_CREATE,
|
||||
icon,
|
||||
title,
|
||||
body,
|
||||
options: {
|
||||
...options,
|
||||
onClick() {
|
||||
options.onClick && options.onClick();
|
||||
this.clickedNotification();
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
clickedNotification() {
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.NOTIFICATION_CLICK});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/NotificationActionCreators.js
|
|
@ -0,0 +1,44 @@
|
|||
/* @flow */
|
||||
|
||||
import {ActionTypes, Endpoints, ME} from '../Constants';
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import type {NotificationSettings} from '../flow/Server';
|
||||
|
||||
export default {
|
||||
open(guildId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATION_SETTINGS_MODAL_OPEN,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.NOTIFICATION_SETTINGS_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
updateNotificationSettings(guildId: string, settings: NotificationSettings, partial: boolean = false) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.USER_GUILD_SETTINGS(guildId || ME),
|
||||
body: settings,
|
||||
});
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_GUILD_SETTINGS_UPDATE,
|
||||
// eslint-disable-next-line camelcase
|
||||
userGuildSettings: [{guild_id: guildId, ...settings}],
|
||||
partial,
|
||||
});
|
||||
},
|
||||
|
||||
updateChannelOverrideSettings(guildId: string, channelId: string, settings: NotificationSettings) {
|
||||
// eslint-disable-next-line camelcase
|
||||
this.updateNotificationSettings(guildId, {channel_overrides: {[channelId]: settings}}, true);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/NotificationSettingsModalActionCreators.js
|
|
@ -0,0 +1,84 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
setLocked(locked: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SET_LOCKED,
|
||||
locked,
|
||||
});
|
||||
},
|
||||
|
||||
setEnabled(enabled: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SET_ENABLED,
|
||||
enabled,
|
||||
});
|
||||
},
|
||||
|
||||
selectGuild(guildId: ?string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SELECT_GUILD,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
selectCall(callId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SELECT_CALL,
|
||||
callId,
|
||||
});
|
||||
},
|
||||
|
||||
openUserPopout(userId: string, guildId: string, channelId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_OPEN_USER_POPOUT,
|
||||
userId,
|
||||
guildId,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
setDisplayNameMode(mode: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SET_DISPLAY_NAME_MODE,
|
||||
mode,
|
||||
});
|
||||
},
|
||||
|
||||
setDisplayUserMode(mode: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SET_DISPLAY_USER_MODE,
|
||||
mode,
|
||||
});
|
||||
},
|
||||
|
||||
setAvatarSizeMode(mode: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SET_AVATAR_SIZE_MODE,
|
||||
mode,
|
||||
});
|
||||
},
|
||||
|
||||
setPosition(x: number, y: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_SET_POSITION,
|
||||
x,
|
||||
y,
|
||||
});
|
||||
},
|
||||
|
||||
setInstallProgress(progress: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.OVERLAY_INSTALL_PROGRESS,
|
||||
progress,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/OverlayActionCreators.js
|
|
@ -0,0 +1,34 @@
|
|||
/* @flow */
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, PaymentModelModes} from '../Constants';
|
||||
|
||||
import type {CardInfo} from './BillingActionCreators';
|
||||
type PaymentModelModeValues = 'NEW' | 'CHANGE' | 'RESUBSCRIBE';
|
||||
|
||||
export default {
|
||||
show(mode: PaymentModelModeValues = PaymentModelModes.NEW, plan: ?string = null) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PAYMENT_MODAL_OPEN,
|
||||
mode,
|
||||
plan,
|
||||
});
|
||||
},
|
||||
|
||||
submit(cardInfo: CardInfo) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PAYMENT_MODAL_SUBMIT,
|
||||
cardInfo,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PAYMENT_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PaymentModalActionCreators.js
|
|
@ -0,0 +1,30 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
clearVADWarning() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PERMISSION_CLEAR_VAD_WARNING,
|
||||
});
|
||||
},
|
||||
|
||||
clearSuppressWarning(forever: boolean = false) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PERMISSION_CLEAR_SUPPRESS_WARNING,
|
||||
forever,
|
||||
});
|
||||
},
|
||||
|
||||
clearPTTAdminWarning() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PERMISSION_CLEAR_PTT_ADMIN_WARNING,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PermissionActionCreators.js
|
|
@ -0,0 +1,48 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
let PhoneVerificationActionCreators = {};
|
||||
if (__IOS__) {
|
||||
PhoneVerificationActionCreators = require('./ios/PhoneVerificationActionCreators');
|
||||
}
|
||||
|
||||
export default {
|
||||
...PhoneVerificationActionCreators,
|
||||
|
||||
addPhone(phone: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.VERIFICATION_ADDING_PHONE});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.PHONE,
|
||||
body: {
|
||||
phone,
|
||||
},
|
||||
}).then(
|
||||
() => Dispatcher.dispatch({type: ActionTypes.VERIFICATION_ADD_PHONE_SUCCESS}),
|
||||
({body}) => Dispatcher.dispatch({type: ActionTypes.VERIFICATION_ADD_PHONE_FAILURE, error: body.message})
|
||||
);
|
||||
},
|
||||
|
||||
verifyPhone(code: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.VERIFICATION_VERIFYING_PHONE});
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.VERIFY_PHONE,
|
||||
body: {
|
||||
code,
|
||||
},
|
||||
}).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.VERIFICATION_VERIFY_PHONE_SUCCESS});
|
||||
Dispatcher.dispatch({type: ActionTypes.MODAL_POP});
|
||||
},
|
||||
({body}) => Dispatcher.dispatch({type: ActionTypes.VERIFICATION_VERIFY_PHONE_FAILURE, error: body.message})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PhoneVerificationActionCreators.js
|
|
@ -0,0 +1,44 @@
|
|||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import type {PictureInPictureOffsets} from '../flow/Client';
|
||||
|
||||
type Identifier = string | number;
|
||||
|
||||
export function open(
|
||||
id: Identifier,
|
||||
component: string | Function,
|
||||
position: string,
|
||||
props: any = {},
|
||||
offsets: PictureInPictureOffsets
|
||||
) {
|
||||
// This can be called during channel/guild changes
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.PICTURE_IN_PICTURE_OPEN,
|
||||
id,
|
||||
component,
|
||||
position,
|
||||
props,
|
||||
offsets,
|
||||
});
|
||||
}
|
||||
|
||||
export function close(id: Identifier) {
|
||||
// This can be called during channel/guild changes
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.PICTURE_IN_PICTURE_CLOSE,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
export function moveTo(id: Identifier, position: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PICTURE_IN_PICTURE_MOVE,
|
||||
id,
|
||||
position,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PictureInPictureActionCreators.js
|
|
@ -0,0 +1,38 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import {fetchAccounts} from '../lib/ContactImporter';
|
||||
|
||||
export default {
|
||||
detectAccounts(platformTypes: Array<string>, location?: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PLATFORM_ACCOUNTS_DETECTING,
|
||||
platformTypes,
|
||||
location,
|
||||
});
|
||||
|
||||
return fetchAccounts(platformTypes).then(accounts => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PLATFORM_ACCOUNTS_DETECTED,
|
||||
platformTypes,
|
||||
accounts,
|
||||
location,
|
||||
});
|
||||
return accounts;
|
||||
});
|
||||
},
|
||||
|
||||
ignore(platformTypes?: Array<string>) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PLATFORM_ACCOUNTS_IGNORE, platformTypes});
|
||||
},
|
||||
|
||||
changeDisplayType(displayType: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.DETECTED_PLATFORM_ACCOUNTS_DISPLAY_TYPE_CHANGE, displayType});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PlatformAccountsActionCreators.js
|
|
@ -0,0 +1,52 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import type {PopoutProps} from '../flow/Client';
|
||||
|
||||
export default {
|
||||
open(popout: PopoutProps) {
|
||||
// This is a Flux anti-pattern. Since popout trigger can be updated due to change
|
||||
// in text or position it will trigger and upodate and that update might be
|
||||
// happening during a dispatch that caused a re-render.
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.POPOUT_OPEN,
|
||||
popout,
|
||||
});
|
||||
},
|
||||
|
||||
close(key?: number | string) {
|
||||
// This is a Flux anti-pattern. Since popout unmount can be triggered by
|
||||
// other Dispatcher actions we must force the hide to occur on the next tick
|
||||
// outside of the current dispatch.
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.POPOUT_CLOSE,
|
||||
key,
|
||||
});
|
||||
},
|
||||
|
||||
closeAll() {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.POPOUT_CLOSE_ALL,
|
||||
});
|
||||
},
|
||||
|
||||
rerender(key: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.POPOUT_NEEDS_RERENDER,
|
||||
key,
|
||||
});
|
||||
},
|
||||
|
||||
didRerender(key: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.POPOUT_DID_RERENDER,
|
||||
key,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PopoutActionCreators.js
|
|
@ -0,0 +1,14 @@
|
|||
/* @flow */
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
call(callback: Function): void {
|
||||
Dispatcher.dispatch({type: ActionTypes.POST_CONNECTION_PENDING_CALLBACK, callback});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PostConnectionCallbackActionCreators.js
|
|
@ -0,0 +1,24 @@
|
|||
/* @flow */
|
||||
|
||||
import {ActionTypes} from '../Constants';
|
||||
import Dispatcher from '../Dispatcher';
|
||||
|
||||
export default {
|
||||
open(guildId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PRIVACY_SETTINGS_MODAL_OPEN,
|
||||
guildId,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PRIVACY_SETTINGS_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PrivacySettingsModalActionCreators.js
|
|
@ -0,0 +1,19 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
selectParticipant(participantId: ?string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_CALL_SELECT_PARTICIPANT, participantId});
|
||||
},
|
||||
|
||||
updateLayout(channelId: string, layout: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_CALL_UPDATE_LAYOUT, channelId, layout});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PrivateChannelCallActionCreators.js
|
|
@ -0,0 +1,32 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import UserRecord from '../records/UserRecord';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
search(query: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_RECIPIENTS_INVITE_QUERY, query});
|
||||
},
|
||||
|
||||
clear() {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_RECIPIENTS_INVITE_QUERY, query: ''});
|
||||
},
|
||||
|
||||
select(row: number) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_RECIPIENTS_INVITE_SELECT, row});
|
||||
},
|
||||
|
||||
addUser(user: UserRecord) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_RECIPIENTS_ADD_USER, user});
|
||||
},
|
||||
|
||||
removeUser(user: UserRecord) {
|
||||
Dispatcher.dispatch({type: ActionTypes.PRIVATE_CHANNEL_RECIPIENTS_REMOVE_USER, user});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PrivateChannelRecipientsInviteActionCreators.js
|
|
@ -0,0 +1,56 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
open(guildId: string, days: number = 7) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PRUNE_GUILD_MODAL_OPEN,
|
||||
guildId,
|
||||
days,
|
||||
estimate: 0,
|
||||
});
|
||||
HTTPUtils.get({
|
||||
url: Endpoints.GUILD_PRUNE(guildId),
|
||||
query: {days},
|
||||
}).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PRUNE_GUILD_MODAL_OPEN,
|
||||
guildId,
|
||||
days,
|
||||
estimate: res.body['pruned'],
|
||||
});
|
||||
},
|
||||
() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PRUNE_GUILD_MODAL_OPEN,
|
||||
guildId,
|
||||
days,
|
||||
estimate: 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.PRUNE_GUILD_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
prune(guildId: string, days: number) {
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.GUILD_PRUNE(guildId),
|
||||
query: {days},
|
||||
});
|
||||
this.close();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/PruneGuildModalActionCreators.js
|
|
@ -0,0 +1,195 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import ChannelStore from '../stores/ChannelStore';
|
||||
import DelayedSelectionActionCreators from '../actions/DelayedSelectionActionCreators';
|
||||
import SelectedChannelActionCreators from '../actions/SelectedChannelActionCreators';
|
||||
import DimensionActionCreators from '../actions/DimensionActionCreators';
|
||||
import ChannelActionCreators from '../actions/ChannelActionCreators';
|
||||
import SelectedChannelStore from '../stores/SelectedChannelStore';
|
||||
import AuthenticationStore from '../stores/AuthenticationStore';
|
||||
import QuickSwitcherStore from '../stores/QuickSwitcherStore';
|
||||
import SelectedGuildStore from '../stores/SelectedGuildStore';
|
||||
import AnalyticsUtils from '../utils/AnalyticsUtils';
|
||||
import {findNextSelected} from '../utils/QuickSwitcherUtils';
|
||||
import {ActionTypes, QuickSwitcherResultTypes, ME, AnalyticEvents} from '../Constants';
|
||||
import type {QuickSwitcherResult} from '../flow/Client';
|
||||
|
||||
function trackOpen(source) {
|
||||
if (QuickSwitcherStore.isOpen()) {
|
||||
return;
|
||||
}
|
||||
const guildId = SelectedGuildStore.getGuildId();
|
||||
const channelId = SelectedChannelStore.getChannelId(guildId);
|
||||
let channelType;
|
||||
if (channelId != null) {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
channelType = channel != null ? channel.type : null;
|
||||
}
|
||||
AnalyticsUtils.track(AnalyticEvents.QUICKSWITCHER_OPENED, {
|
||||
source,
|
||||
/* eslint-disable camelcase */
|
||||
current_guild_id: guildId,
|
||||
current_channel_id: channelId,
|
||||
current_channel_type: channelType,
|
||||
/* eslint-enable camelcase */
|
||||
});
|
||||
}
|
||||
|
||||
type QuickSwitcherCloseParams = {
|
||||
current_channel_id: ?string,
|
||||
current_channel_type?: ?number,
|
||||
current_guild_id: ?string,
|
||||
query_mode: string,
|
||||
query_length: number,
|
||||
top_result_type: ?string,
|
||||
top_result_score: ?number,
|
||||
num_results_total: number,
|
||||
num_results_users: number,
|
||||
num_results_text_channels: number,
|
||||
num_results_voice_channels: number,
|
||||
num_results_guilds: number,
|
||||
num_results_group_dms: number,
|
||||
selected_type?: string,
|
||||
selected_score?: number,
|
||||
selected_index?: number,
|
||||
selected_channel_id?: ?string,
|
||||
selected_guild_id?: ?string,
|
||||
selected_user_id?: string,
|
||||
};
|
||||
|
||||
function trackClose(event, result) {
|
||||
const {results, queryMode, query} = QuickSwitcherStore.getState();
|
||||
const guildId = SelectedGuildStore.getGuildId();
|
||||
const channelId = SelectedChannelStore.getChannelId(guildId);
|
||||
const initialResult = results[findNextSelected('down', -1, results)];
|
||||
const params: QuickSwitcherCloseParams = {
|
||||
/* eslint-disable camelcase */
|
||||
current_channel_id: channelId,
|
||||
current_guild_id: guildId,
|
||||
query_mode: queryMode ? queryMode : 'GENERAL',
|
||||
query_length: query.length,
|
||||
top_result_type: initialResult ? initialResult.type : null,
|
||||
top_result_score: initialResult ? initialResult.score : null,
|
||||
num_results_total: QuickSwitcherStore.getResultTotals(),
|
||||
num_results_users: QuickSwitcherStore.getResultTotals(QuickSwitcherResultTypes.USER),
|
||||
num_results_text_channels: QuickSwitcherStore.getResultTotals(QuickSwitcherResultTypes.TEXT_CHANNEL),
|
||||
num_results_voice_channels: QuickSwitcherStore.getResultTotals(QuickSwitcherResultTypes.VOICE_CHANNEL),
|
||||
num_results_guilds: QuickSwitcherStore.getResultTotals(QuickSwitcherResultTypes.GUILD),
|
||||
num_results_group_dms: QuickSwitcherStore.getResultTotals(QuickSwitcherResultTypes.GROUP_DM),
|
||||
/* eslint-enable camelcase */
|
||||
};
|
||||
if (channelId != null) {
|
||||
const channel = ChannelStore.getChannel(channelId);
|
||||
params['current_channel_type'] = channel != null ? channel.type : null;
|
||||
}
|
||||
if (result != null) {
|
||||
const {type, score, record} = result;
|
||||
params['selected_type'] = type;
|
||||
params['selected_score'] = score;
|
||||
params['selected_index'] = results.indexOf(result);
|
||||
switch (type) {
|
||||
case QuickSwitcherResultTypes.GUILD:
|
||||
params['selected_guild_id'] = record.id;
|
||||
break;
|
||||
case QuickSwitcherResultTypes.TEXT_CHANNEL:
|
||||
case QuickSwitcherResultTypes.VOICE_CHANNEL:
|
||||
params['selected_guild_id'] = record.guild_id;
|
||||
params['selected_channel_id'] = record.id;
|
||||
break;
|
||||
case QuickSwitcherResultTypes.GROUP_DM:
|
||||
params['selected_channel_id'] = record.id;
|
||||
break;
|
||||
case QuickSwitcherResultTypes.USER:
|
||||
params['selected_user_id'] = record.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AnalyticsUtils.track(event, params);
|
||||
}
|
||||
|
||||
function hideQuickSwitcher() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.QUICKSWITCHER_HIDE,
|
||||
});
|
||||
}
|
||||
|
||||
export function show(source: string = 'KEYBIND') {
|
||||
trackOpen(source);
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.QUICKSWITCHER_SHOW,
|
||||
});
|
||||
}
|
||||
|
||||
export function hide() {
|
||||
trackClose(AnalyticEvents.QUICKSWITCHER_CLOSED);
|
||||
hideQuickSwitcher();
|
||||
}
|
||||
|
||||
export function toggle(source: string = 'KEYBIND') {
|
||||
if (QuickSwitcherStore.isOpen()) {
|
||||
hide();
|
||||
} else {
|
||||
show(source);
|
||||
}
|
||||
}
|
||||
|
||||
export function search(query: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.QUICKSWITCHER_SEARCH,
|
||||
query,
|
||||
});
|
||||
}
|
||||
|
||||
export function selectResult(selectedIndex: number) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.QUICKSWITCHER_SELECT,
|
||||
selectedIndex,
|
||||
});
|
||||
}
|
||||
|
||||
export function switchToResult(result: QuickSwitcherResult) {
|
||||
trackClose(AnalyticEvents.QUICKSWITCHER_RESULT_SELECTED, result);
|
||||
const {type, record} = result;
|
||||
let channel;
|
||||
switch (type) {
|
||||
case QuickSwitcherResultTypes.GUILD:
|
||||
channel = SelectedChannelStore.getChannelId(record.id);
|
||||
if (channel) {
|
||||
DelayedSelectionActionCreators.selectChannel(record.id, channel);
|
||||
} else {
|
||||
DelayedSelectionActionCreators.selectGuild(record.id);
|
||||
}
|
||||
DelayedSelectionActionCreators.flushSelection(true);
|
||||
break;
|
||||
case QuickSwitcherResultTypes.TEXT_CHANNEL:
|
||||
channel = ChannelStore.getChannel(record.id);
|
||||
if (channel != null) {
|
||||
DelayedSelectionActionCreators.selectChannel(channel.guild_id, record.id);
|
||||
DelayedSelectionActionCreators.flushSelection(true);
|
||||
}
|
||||
break;
|
||||
case QuickSwitcherResultTypes.VOICE_CHANNEL:
|
||||
channel = ChannelStore.getChannel(record.id);
|
||||
if (channel != null) {
|
||||
SelectedChannelActionCreators.selectVoiceChannel(channel.guild_id, record.id);
|
||||
}
|
||||
break;
|
||||
case QuickSwitcherResultTypes.USER:
|
||||
ChannelActionCreators.openPrivateChannel(AuthenticationStore.getId(), [record.id]);
|
||||
DimensionActionCreators.channelListScrollTo(ME, record.id);
|
||||
break;
|
||||
case QuickSwitcherResultTypes.GROUP_DM:
|
||||
DelayedSelectionActionCreators.selectChannel(ME, record.id);
|
||||
DelayedSelectionActionCreators.flushSelection(true);
|
||||
DimensionActionCreators.channelListScrollTo(ME, record.id);
|
||||
break;
|
||||
}
|
||||
Dispatcher.dispatch({type: ActionTypes.QUICKSWITCHER_SWITCH_TO, result});
|
||||
hideQuickSwitcher();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/QuickSwitcherActionCreators.js
|
|
@ -0,0 +1,107 @@
|
|||
/* @flow */
|
||||
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import i18n from '../i18n';
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import AuthenticationStore from '../stores/AuthenticationStore';
|
||||
import AlertActionCreators from './AlertActionCreators';
|
||||
import {ComponentDispatch} from '../utils/ComponentDispatchUtils';
|
||||
import {Endpoints, ActionTypes, AbortCodes, ComponentActions} from '../Constants';
|
||||
|
||||
type ReactionEmoji = {id: ?string, name: string};
|
||||
|
||||
function checkReactionResponse(res, retry) {
|
||||
const {status, body} = res;
|
||||
if (status === 429) {
|
||||
// Rate limit on this endpoint is really low so just retry.
|
||||
setTimeout(retry, res.body['retry_after']);
|
||||
return false;
|
||||
} else if (status === 403) {
|
||||
switch (body && body.code) {
|
||||
case AbortCodes.TOO_MANY_REACTIONS:
|
||||
AlertActionCreators.show({
|
||||
title: i18n.Messages.TOO_MANY_REACTIONS_ALERT_HEADER,
|
||||
body: i18n.Messages.TOO_MANY_REACTIONS_ALERT_BODY,
|
||||
confirmText: i18n.Messages.OKAY,
|
||||
});
|
||||
break;
|
||||
case AbortCodes.REACTION_BLOCKED:
|
||||
ComponentDispatch.dispatch(ComponentActions.SHAKE_APP, {duration: 200, intensity: 2});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function optimisticDispatch(
|
||||
type: 'MESSAGE_REACTION_ADD' | 'MESSAGE_REACTION_REMOVE',
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
emoji: ReactionEmoji,
|
||||
userId?: string
|
||||
) {
|
||||
Dispatcher.dispatch({
|
||||
type,
|
||||
channelId,
|
||||
messageId,
|
||||
userId: userId || AuthenticationStore.getId(),
|
||||
emoji,
|
||||
optimistic: true,
|
||||
});
|
||||
}
|
||||
|
||||
function makeURL(channelId: string, messageId: string, emoji: ReactionEmoji, userId?: string): string {
|
||||
const emojiCode = encodeURIComponent(emoji.id != null ? `${emoji.name}:${emoji.id}` : emoji.name);
|
||||
return userId != null
|
||||
? Endpoints.REACTION(channelId, messageId, emojiCode, userId)
|
||||
: Endpoints.REACTIONS(channelId, messageId, emojiCode, userId);
|
||||
}
|
||||
|
||||
export function getReactions(channelId: string, messageId: string, emoji: ReactionEmoji, after?: string): Promise {
|
||||
return HTTPUtils.get({
|
||||
url: makeURL(channelId, messageId, emoji),
|
||||
query: {
|
||||
limit: 100,
|
||||
after: after,
|
||||
},
|
||||
}).then(res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.MESSAGE_REACTION_ADD_USERS,
|
||||
channelId,
|
||||
messageId,
|
||||
users: res.body,
|
||||
emoji,
|
||||
});
|
||||
|
||||
return res.body;
|
||||
});
|
||||
}
|
||||
|
||||
export function addReaction(channelId: string, messageId: string, emoji: ReactionEmoji) {
|
||||
optimisticDispatch(ActionTypes.MESSAGE_REACTION_ADD, channelId, messageId, emoji);
|
||||
HTTPUtils.put(makeURL(channelId, messageId, emoji, '@me')).catch(res => {
|
||||
if (checkReactionResponse(res, () => addReaction(channelId, messageId, emoji))) {
|
||||
optimisticDispatch(ActionTypes.MESSAGE_REACTION_REMOVE, channelId, messageId, emoji);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function removeAllReactions(channelId: string, messageId: string) {
|
||||
HTTPUtils.delete(Endpoints.REMOVE_REACTIONS(channelId, messageId)).catch(res => {
|
||||
checkReactionResponse(res, () => removeAllReactions(channelId, messageId));
|
||||
});
|
||||
}
|
||||
|
||||
export function removeReaction(channelId: string, messageId: string, emoji: ReactionEmoji, userId?: string) {
|
||||
optimisticDispatch(ActionTypes.MESSAGE_REACTION_REMOVE, channelId, messageId, emoji, userId);
|
||||
HTTPUtils.delete(makeURL(channelId, messageId, emoji, userId || '@me')).catch(res => {
|
||||
if (checkReactionResponse(res, () => removeReaction(channelId, messageId, emoji, userId))) {
|
||||
optimisticDispatch(ActionTypes.MESSAGE_REACTION_ADD, channelId, messageId, emoji, userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ReactionActionCreators.js
|
|
@ -0,0 +1,37 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
|
||||
export default {
|
||||
ack(channelId: string, immediate: boolean = false, force: boolean = false) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_ACK,
|
||||
channelId,
|
||||
immediate,
|
||||
force,
|
||||
});
|
||||
},
|
||||
|
||||
localAck(channelId: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.CHANNEL_LOCAL_ACK,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
manualAck(channelId: string, messageId: string) {
|
||||
HTTPUtils.post({
|
||||
url: `${Endpoints.MESSAGES(channelId)}/${messageId}/ack`,
|
||||
body: {
|
||||
manual: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ReadStateActionCreators.js
|
|
@ -0,0 +1,33 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
fetchDefaultRegions() {
|
||||
return this.fetchRegions(null);
|
||||
},
|
||||
|
||||
fetchRegions(guildId: ?string) {
|
||||
HTTPUtils.get({
|
||||
url: Endpoints.REGIONS(guildId),
|
||||
retries: 1,
|
||||
}).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOAD_REGIONS, regions: res.body, guildId}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.LOAD_REGIONS, regions: [], guildId})
|
||||
);
|
||||
},
|
||||
|
||||
changeCallRegion(channelId: string, region: string) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.CALL(channelId),
|
||||
body: {region},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/RegionActionCreators.js
|
|
@ -0,0 +1,88 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints, AbortCodes, UserSettingsSections} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import i18n from '../i18n';
|
||||
import AlertActionCreators from './AlertActionCreators';
|
||||
import UserSettingsModalActionCreators from './UserSettingsModalActionCreators';
|
||||
import UserProfileModalActionCreators from './UserProfileModalActionCreators';
|
||||
import ContextMenuActionCreators from './ContextMenuActionCreators';
|
||||
|
||||
function showAlert(options) {
|
||||
ContextMenuActionCreators.close();
|
||||
UserProfileModalActionCreators.close();
|
||||
AlertActionCreators.show(options);
|
||||
}
|
||||
|
||||
function checkRelationshipAddResponse(res) {
|
||||
const {status, body} = res;
|
||||
if (status === 429) {
|
||||
showAlert({
|
||||
title: i18n.Messages.FRIEND_REQUEST_RATE_LIMITED_HEADER,
|
||||
body: i18n.Messages.FRIEND_REQUEST_RATE_LIMITED_BODY,
|
||||
confirmText: i18n.Messages.FRIEND_REQUEST_RATE_LIMITED_BUTTON,
|
||||
});
|
||||
} else if (status === 403) {
|
||||
const code = body && body.code;
|
||||
if (code === AbortCodes.EMAIL_VERIFICATION_REQUIRED) {
|
||||
showAlert({
|
||||
title: i18n.Messages.FRIEND_REQUEST_REQUIRES_EMAIL_VALIDATION_HEADER,
|
||||
body: i18n.Messages.FRIEND_REQUEST_REQUIRES_EMAIL_VALIDATION_BODY,
|
||||
confirmText: i18n.Messages.FRIEND_REQUEST_REQUIRES_EMAIL_VALIDATION_BUTTON,
|
||||
onConfirm: () => {
|
||||
UserSettingsModalActionCreators.open(UserSettingsSections.ACCOUNT);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
throw res;
|
||||
}
|
||||
|
||||
type Context = {
|
||||
location: string,
|
||||
};
|
||||
|
||||
export default {
|
||||
sendRequest(discordTag: string, context?: Context): Promise<*> {
|
||||
let [username, discriminator] = discordTag.split('#');
|
||||
discriminator = parseInt(discriminator);
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.USER_RELATIONSHIPS(),
|
||||
body: {
|
||||
username,
|
||||
discriminator,
|
||||
},
|
||||
context,
|
||||
}).catch(checkRelationshipAddResponse);
|
||||
},
|
||||
|
||||
addRelationship(userId: string, context?: Context, type?: number) {
|
||||
HTTPUtils.put({
|
||||
url: `${Endpoints.USER_RELATIONSHIPS()}/${userId}`,
|
||||
body: {
|
||||
type,
|
||||
},
|
||||
context,
|
||||
}).catch(checkRelationshipAddResponse);
|
||||
},
|
||||
|
||||
removeRelationship(userId: string, context?: Context) {
|
||||
HTTPUtils.delete({
|
||||
url: `${Endpoints.USER_RELATIONSHIPS()}/${userId}`,
|
||||
context,
|
||||
});
|
||||
},
|
||||
|
||||
fetchRelationships() {
|
||||
HTTPUtils.get(Endpoints.USER_RELATIONSHIPS()).then(
|
||||
res => Dispatcher.dispatch({type: ActionTypes.LOAD_RELATIONSHIPS_SUCCESS, relationships: res.body}),
|
||||
() => Dispatcher.dispatch({type: ActionTypes.LOAD_RELATIONSHIPS_FAILURE})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/RelationshipActionCreators.js
|
|
@ -0,0 +1,32 @@
|
|||
/* @flow */
|
||||
|
||||
import {Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
type ReportData = {
|
||||
guild_id?: ?string,
|
||||
message_id?: ?string,
|
||||
user_id?: ?string,
|
||||
reason?: ?number,
|
||||
};
|
||||
|
||||
export default {
|
||||
report(body: ReportData) {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.REPORT,
|
||||
body,
|
||||
});
|
||||
},
|
||||
|
||||
getReasons(query: ReportData) {
|
||||
return HTTPUtils.get({
|
||||
url: Endpoints.REPORT,
|
||||
query,
|
||||
}).then(res => res.body);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/ReportModalActionCreators.js
|
|
@ -0,0 +1,121 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import SearchStore from '../stores/SearchStore';
|
||||
import AppAnalyticsUtils from '../utils/AppAnalyticsUtils';
|
||||
import GuildNSFWAgreeStore from '../stores/GuildNSFWAgreeStore';
|
||||
import {ActionTypes, SEARCH_PAGE_SIZE} from '../Constants';
|
||||
import type {SearchQuery} from '../flow/Server';
|
||||
import type {EditorState} from 'draft-js';
|
||||
import type {Token} from '../lib/QueryTokenizer';
|
||||
import type {CursorScope} from '../flow/Client';
|
||||
|
||||
export function search(searchId: string, query: SearchQuery, queryString: ?string) {
|
||||
if (GuildNSFWAgreeStore.didAgree(searchId)) {
|
||||
// eslint-disable-next-line camelcase
|
||||
query.include_nsfw = true;
|
||||
}
|
||||
const modifierKeys = Object.keys(query);
|
||||
AppAnalyticsUtils.trackWithMetadata('search_started', {
|
||||
/* eslint-disable camelcase */
|
||||
prev_search_id: SearchStore.getAnalyticsId(searchId),
|
||||
num_modifiers: modifierKeys.length,
|
||||
/* eslint-enable camelcase */
|
||||
modifiers: modifierKeys.reduce((acc, k) => {
|
||||
const modifierValue = query[k];
|
||||
acc[k] = Array.isArray(modifierValue) ? modifierValue.length : 1;
|
||||
return acc;
|
||||
}, {}),
|
||||
});
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.SEARCH_START,
|
||||
query,
|
||||
searchId,
|
||||
queryString,
|
||||
});
|
||||
}
|
||||
|
||||
export function searchByMode(searchId: string, sortBy: string) {
|
||||
const query = SearchStore.getQuery(searchId);
|
||||
return search(searchId, {
|
||||
...query,
|
||||
offset: 0,
|
||||
// eslint-disable-next-line camelcase
|
||||
sort_by: sortBy,
|
||||
});
|
||||
}
|
||||
|
||||
export function searchNextPage(searchId: string, numResults: number = SEARCH_PAGE_SIZE) {
|
||||
return searchPage(searchId, numResults);
|
||||
}
|
||||
|
||||
export function searchPreviousPage(searchId: string, numResults: number = SEARCH_PAGE_SIZE) {
|
||||
return searchPage(searchId, -numResults);
|
||||
}
|
||||
|
||||
function searchPage(searchId: string, numResults: number) {
|
||||
const currentOffset = SearchStore.getOffset(searchId);
|
||||
const query = SearchStore.getQuery(searchId);
|
||||
const offset = currentOffset + numResults;
|
||||
const totalResults = SearchStore.getTotalResults(searchId);
|
||||
|
||||
if (offset < 0 || offset > totalResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
return search(searchId, {
|
||||
...query,
|
||||
offset,
|
||||
});
|
||||
}
|
||||
|
||||
export function clearHistory(searchId: ?string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.SEARCH_CLEAR_HISTORY, searchId});
|
||||
}
|
||||
|
||||
export function ensureSearchState(searchId: ?string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.SEARCH_ENSURE_SEARCH_STATE, searchId});
|
||||
}
|
||||
|
||||
export function setSearchState(searchId: ?string, editorState: EditorState) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.SEARCH_EDITOR_STATE_CHANGE,
|
||||
searchId,
|
||||
editorState,
|
||||
});
|
||||
}
|
||||
|
||||
export function clearSearchState(searchId: string) {
|
||||
AppAnalyticsUtils.trackWithMetadata('search_closed', {
|
||||
// eslint-disable-next-line camelcase
|
||||
search_id: SearchStore.getAnalyticsId(searchId),
|
||||
});
|
||||
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.SEARCH_EDITOR_STATE_CLEAR,
|
||||
searchId,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAutocompleteQuery(searchId: ?string, tokens: Array<Token>, cursorScope: ?CursorScope) {
|
||||
// This can be fired under various circumstances we cannot control
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.SEARCH_AUTOCOMPLETE_QUERY_UPDATE,
|
||||
searchId,
|
||||
tokens,
|
||||
cursorScope,
|
||||
});
|
||||
}
|
||||
|
||||
export function setShowBlockedResults(searchId: string, showBlocked: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.SEARCH_SET_SHOW_BLOCKED_RESULTS,
|
||||
searchId,
|
||||
showBlocked,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/SearchActionCreators.js
|
|
@ -0,0 +1,46 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import MediaEngineStore from '../stores/MediaEngineStore';
|
||||
import {ActionTypes, ME, Routes} from '../Constants';
|
||||
import RouterUtils from '../utils/RouterUtils';
|
||||
|
||||
export default {
|
||||
selectChannel(guildId: ?string, channelId: ?string, messageId?: string) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.CHANNEL_SELECT,
|
||||
guildId: guildId === ME ? null : guildId,
|
||||
channelId,
|
||||
messageId,
|
||||
});
|
||||
},
|
||||
|
||||
selectPrivateChannel(channelId: string) {
|
||||
// TODO: remove this once RouterUtils is resolved iOS and Web
|
||||
if (__WEB__) {
|
||||
RouterUtils.transitionTo(Routes.CHANNEL(ME, channelId));
|
||||
} else {
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.GUILD_SELECT, guildId: null});
|
||||
this.selectChannel(ME, channelId);
|
||||
}
|
||||
},
|
||||
|
||||
selectVoiceChannel(guildId: ?string, channelId: ?string) {
|
||||
if (MediaEngineStore.isSupported()) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.VOICE_CHANNEL_SELECT,
|
||||
guildId,
|
||||
channelId,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
clearVoiceChannel() {
|
||||
Dispatcher.dispatch({type: ActionTypes.VOICE_CHANNEL_CLEAR});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/SelectedChannelActionCreators.js
|
|
@ -0,0 +1,45 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
const BASE_MAINTENANCES_URL = 'https://status.discordapp.com/api/v2/scheduled-maintenances';
|
||||
const INCIDENTS_URL = 'https://status.discordapp.com/api/v2/incidents/unresolved.json';
|
||||
|
||||
export default {
|
||||
checkIncidents() {
|
||||
Promise.all([
|
||||
HTTPUtils.get(`${BASE_MAINTENANCES_URL}/active.json`),
|
||||
HTTPUtils.get(INCIDENTS_URL),
|
||||
]).then(([res1, res2]) => {
|
||||
const [maintenance] = res1.body['scheduled_maintenances'];
|
||||
const [incident] = res2.body['incidents'];
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.STATUS_PAGE_INCIDENT,
|
||||
incident: incident || maintenance,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
checkScheduledMaintenances() {
|
||||
HTTPUtils.get(`${BASE_MAINTENANCES_URL}/upcoming.json`).then(res => {
|
||||
const [maintenance] = res.body['scheduled_maintenances'];
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.STATUS_PAGE_SCHEDULED_MAINTENANCE,
|
||||
maintenance,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
ackScheduledMaintenance() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.STATUS_PAGE_SCHEDULED_MAINTENANCE_ACK,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/StatusPageActionCreators.js
|
|
@ -0,0 +1,34 @@
|
|||
/* @flow */
|
||||
|
||||
import {ActionTypes} from '../Constants';
|
||||
import Dispatcher from '../Dispatcher';
|
||||
|
||||
export type StreamerModeOptions = {
|
||||
enabled?: boolean,
|
||||
autoToggle?: boolean,
|
||||
hidePersonalInformation?: boolean,
|
||||
hideInstantInvites?: boolean,
|
||||
disableSounds?: boolean,
|
||||
disableNotifications?: boolean,
|
||||
};
|
||||
|
||||
export default {
|
||||
setEnabled(enabled: boolean) {
|
||||
this.update({enabled});
|
||||
},
|
||||
|
||||
update(settings: StreamerModeOptions) {
|
||||
for (const key of Object.keys(settings)) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.STREAMER_MODE_UPDATE,
|
||||
key,
|
||||
value: ((settings[key]: any): boolean),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/StreamerModeActionCreators.js
|
|
@ -0,0 +1,44 @@
|
|||
/* @flow */
|
||||
|
||||
import lodash from 'lodash';
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
const delayedReset = lodash.debounce(() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.TEXT_MESSAGE_DOWNLOAD_LINK_RESET});
|
||||
}, 3000);
|
||||
|
||||
export default {
|
||||
sendTextLink(phoneNumber: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.TEXT_MESSAGE_DOWNLOAD_LINK_SUBMITTING,
|
||||
});
|
||||
|
||||
HTTPUtils.post({
|
||||
url: Endpoints.MOBILE_TEXT_LINK,
|
||||
body: {
|
||||
phone_number: phoneNumber, // eslint-disable-line camelcase
|
||||
},
|
||||
}).then(
|
||||
() => {
|
||||
Dispatcher.dispatch({type: ActionTypes.TEXT_MESSAGE_DOWNLOAD_LINK_SUCCESS});
|
||||
delayedReset.cancel();
|
||||
delayedReset();
|
||||
},
|
||||
response => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.TEXT_MESSAGE_DOWNLOAD_LINK_FAILURE,
|
||||
errors: response ? response.body.message : [['Something went wrong']],
|
||||
});
|
||||
delayedReset.cancel();
|
||||
delayedReset();
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/TextMessageFormActionCreators.js
|
|
@ -0,0 +1,35 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export type TooltipProps = {
|
||||
position: string,
|
||||
text: string,
|
||||
type: string,
|
||||
targetWidth: number,
|
||||
targetHeight: number,
|
||||
x: number,
|
||||
y: number,
|
||||
};
|
||||
|
||||
export default {
|
||||
show(id: number, tooltip: TooltipProps) {
|
||||
// This is a Flux anti-pattern. Since tooltip trigger can be updated due to change
|
||||
// in text or position it will trigger and upodate and that update might be
|
||||
// happening during a dispatch that caused a re-render.
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.TOOLTIP_SHOW, id, tooltip});
|
||||
},
|
||||
|
||||
hide(id: number) {
|
||||
// This is a Flux anti-pattern. Since tooltip unmount can be triggered by
|
||||
// other Dispatcher actions we must force the hide to occur on the next tick
|
||||
// outside of the current dispatch.
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.TOOLTIP_HIDE, id});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/TooltipActionCreators.js
|
|
@ -0,0 +1,34 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
show(tutorialId: string, renderData: any) {
|
||||
// This is a Flux anti-pattern. We need to be able to dispatch while we're mounting in response
|
||||
// to a dispatch.
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.TUTORIAL_INDICATOR_SHOW, tutorialId, renderData});
|
||||
},
|
||||
|
||||
hide(tutorialId: string) {
|
||||
// This is a Flux anti-pattern. We need to be able to dispatch while we're unmounting in response
|
||||
// to a dispatch.
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.TUTORIAL_INDICATOR_HIDE, tutorialId});
|
||||
},
|
||||
|
||||
dismiss(tutorialId: string) {
|
||||
Dispatcher.dispatch({type: ActionTypes.TUTORIAL_INDICATOR_DISMISS, tutorialId});
|
||||
HTTPUtils.put(`${Endpoints.TUTORIAL_INDICATORS}/${tutorialId}`);
|
||||
},
|
||||
|
||||
suppressAll() {
|
||||
Dispatcher.dispatch({type: ActionTypes.TUTORIAL_INDICATOR_SUPPRESS_ALL});
|
||||
HTTPUtils.post(`${Endpoints.TUTORIAL_INDICATORS}/suppress`);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/TutorialActionCreators.js
|
|
@ -0,0 +1,15 @@
|
|||
/* @flow */
|
||||
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import {Endpoints} from '../Constants';
|
||||
|
||||
export default {
|
||||
sendTyping(channelId: string) {
|
||||
HTTPUtils.post(Endpoints.TYPING(channelId));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/TypingActionCreators.js
|
|
@ -0,0 +1,15 @@
|
|||
/* @flow */
|
||||
|
||||
let UploadActionCreators = {};
|
||||
if (__IOS__) {
|
||||
UploadActionCreators = require('./ios/UploadActionCreators');
|
||||
} else {
|
||||
UploadActionCreators = require('./web/UploadActionCreators');
|
||||
}
|
||||
|
||||
export default UploadActionCreators;
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UploadActionCreators.js
|
|
@ -0,0 +1,29 @@
|
|||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
popFirstFile() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.UPLOAD_MODAL_POP_FILE,
|
||||
});
|
||||
},
|
||||
|
||||
pushFiles({files, channelId}: {files: Array<File>, channelId: string}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.UPLOAD_MODAL_PUSH_FILES,
|
||||
files,
|
||||
channelId,
|
||||
});
|
||||
},
|
||||
|
||||
clearAll() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.UPLOAD_MODAL_CLEAR_ALL_FILES,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UploadModalActionCreators.js
|
|
@ -0,0 +1,19 @@
|
|||
/* @flow */
|
||||
|
||||
import {Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export function acceptAgreements(terms: boolean = true, privacy: boolean = true): Promise<boolean> {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.USER_AGREEMENTS,
|
||||
body: {
|
||||
terms,
|
||||
privacy,
|
||||
},
|
||||
}).then(() => true, () => false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UserActionCreators.js
|
|
@ -0,0 +1,72 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import UserStore from '../stores/UserStore';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
|
||||
function fetchProfile(userId) {
|
||||
HTTPUtils.get(Endpoints.USER_PROFILE(userId)).then(
|
||||
res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_PROFILE_MODAL_FETCH_SUCCESS,
|
||||
...res.body,
|
||||
});
|
||||
},
|
||||
({body}) => console.warn(`fetchProfile error: ${body.code} - ${body.message}`)
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
open(userId: string) {
|
||||
if (!UserStore.getUser(userId).bot) {
|
||||
fetchProfile(userId);
|
||||
}
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_PROFILE_MODAL_OPEN,
|
||||
userId,
|
||||
});
|
||||
},
|
||||
|
||||
push(userId: string) {
|
||||
fetchProfile(userId);
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_PROFILE_MODAL_PUSH,
|
||||
userId,
|
||||
});
|
||||
},
|
||||
|
||||
pop() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_PROFILE_MODAL_POP,
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_PROFILE_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
setSection(section: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_PROFILE_MODAL_SET_SECTION,
|
||||
section,
|
||||
});
|
||||
},
|
||||
|
||||
fetchMutualFriends(userId: string) {
|
||||
HTTPUtils.get(Endpoints.USER_RELATIONSHIPS(userId)).then(res => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.LOAD_MUTUAL_FRIENDS,
|
||||
userId,
|
||||
mutualFriends: res.body,
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UserProfileModalActionCreators.js
|
|
@ -0,0 +1,105 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import i18n from '../i18n';
|
||||
import {ActionTypes, Endpoints, DEVICE_TOKEN, DEVICE_PUSH_PROVIDER} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import mfaInterceptor from '../utils/MFAInterceptionUtils';
|
||||
import Storage from '../lib/Storage';
|
||||
import {hasAnimatedAvatar} from '../utils/AvatarUtils';
|
||||
import AnalyticsUtils from '../utils/AnalyticsUtils';
|
||||
|
||||
export function accountDetailsInit() {
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_SETTINGS_ACCOUNT_INIT});
|
||||
}
|
||||
|
||||
export function accountDetailsClose() {
|
||||
// This has to be dirty, since it could be firing at the same time as logout
|
||||
Dispatcher.dirtyDispatch({type: ActionTypes.USER_SETTINGS_ACCOUNT_CLOSE});
|
||||
}
|
||||
|
||||
export function toggleEditingProfile(isEditingProfile: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_ACCOUNT_TOGGLE_EDITING_PROFILE,
|
||||
isEditingProfile,
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleEditingPassword(isEditingPassword: boolean) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_ACCOUNT_TOGGLE_EDITING_PASSWORD,
|
||||
isEditingPassword,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAccountInfo(update: {[key: string]: ?string}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_ACCOUNT_UPDATE_INFO,
|
||||
update,
|
||||
});
|
||||
}
|
||||
|
||||
export function saveAccountChanges(
|
||||
username: string,
|
||||
email: string,
|
||||
password: string,
|
||||
avatar: ?string,
|
||||
newPassword: ?string
|
||||
) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_ACCOUNT_SUBMIT,
|
||||
});
|
||||
|
||||
mfaInterceptor(
|
||||
{
|
||||
title: i18n.Messages.TWO_FA_CHANGE_ACCOUNT,
|
||||
},
|
||||
extraBody => {
|
||||
const body = {
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
avatar,
|
||||
// eslint-disable-next-line camelcase
|
||||
new_password: newPassword,
|
||||
...extraBody,
|
||||
};
|
||||
// Only send these values if we have them.
|
||||
const pushToken = Storage.get(DEVICE_TOKEN);
|
||||
if (DEVICE_PUSH_PROVIDER != null && pushToken != null) {
|
||||
body['push_provider'] = DEVICE_PUSH_PROVIDER;
|
||||
body['push_token'] = pushToken;
|
||||
}
|
||||
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.ME,
|
||||
body,
|
||||
});
|
||||
},
|
||||
{
|
||||
// Only open mfa modal if the server wants the code. In this case, we do not want to optimistically open it.
|
||||
checkEnabled: false,
|
||||
hooks: {
|
||||
onEarlyClose: () => Dispatcher.dispatch({type: ActionTypes.USER_SETTINGS_ACCOUNT_SUBMIT_FAILURE, errors: {}}),
|
||||
},
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
const user = res.body;
|
||||
const token = user.token;
|
||||
AnalyticsUtils.track('user_avatar_updated', {animated: hasAnimatedAvatar(user)});
|
||||
delete user.token;
|
||||
Dispatcher.dispatch({type: ActionTypes.UPDATE_TOKEN, token});
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_UPDATE, user});
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_SETTINGS_ACCOUNT_SUBMIT_SUCCESS});
|
||||
},
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_SETTINGS_ACCOUNT_SUBMIT_FAILURE, errors: res.body});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UserSettingsAccountActionCreators.js
|
|
@ -0,0 +1,68 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import type {UserSettings} from '../flow/Server';
|
||||
|
||||
const KEY_MAPPING = {
|
||||
showCurrentGame: 'show_current_game',
|
||||
inlineAttachmentMedia: 'inline_attachment_media',
|
||||
inlineEmbedMedia: 'inline_embed_media',
|
||||
renderEmbeds: 'render_embeds',
|
||||
renderReactions: 'render_reactions',
|
||||
sync: 'sync',
|
||||
theme: 'theme',
|
||||
enableTTSCommand: 'enable_tts_command',
|
||||
messageDisplayCompact: 'message_display_compact',
|
||||
locale: 'locale',
|
||||
convertEmoticons: 'convert_emoticons',
|
||||
restrictedGuilds: 'restricted_guilds',
|
||||
friendSourceFlags: 'friend_source_flags',
|
||||
developerMode: 'developer_mode',
|
||||
guildPositions: 'guild_positions',
|
||||
detectPlatformAccounts: 'detect_platform_accounts',
|
||||
status: 'status',
|
||||
explicitContentFilter: 'explicit_content_filter',
|
||||
defaultGuildsRestricted: 'default_guilds_restricted',
|
||||
afkTimeout: 'afk_timeout',
|
||||
};
|
||||
|
||||
type OpaqueSettings = {[key: string]: any};
|
||||
|
||||
function convertKeys(settings: OpaqueSettings, reverse: boolean): OpaqueSettings {
|
||||
const convertedSettings = {};
|
||||
for (const camelKey of Object.keys(KEY_MAPPING)) {
|
||||
const snakeKey = KEY_MAPPING[camelKey];
|
||||
const value = settings[reverse ? snakeKey : camelKey];
|
||||
if (value != null) {
|
||||
convertedSettings[reverse ? camelKey : snakeKey] = value;
|
||||
}
|
||||
}
|
||||
return convertedSettings;
|
||||
}
|
||||
|
||||
export default {
|
||||
updateLocalSettings(settings: OpaqueSettings, convert: boolean = false) {
|
||||
if (convert) {
|
||||
settings = convertKeys(settings, true);
|
||||
}
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_UPDATE,
|
||||
settings,
|
||||
});
|
||||
},
|
||||
|
||||
updateRemoteSettings(settings: UserSettings) {
|
||||
HTTPUtils.patch({
|
||||
url: Endpoints.SETTINGS,
|
||||
body: convertKeys(settings, false),
|
||||
});
|
||||
this.updateLocalSettings(settings);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UserSettingsActionCreators.js
|
|
@ -0,0 +1,122 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import i18n from '../i18n';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
import mfaInterceptor from '../utils/MFAInterceptionUtils';
|
||||
import Storage from '../lib/Storage';
|
||||
import {hasAnimatedAvatar} from '../utils/AvatarUtils';
|
||||
import AnalyticsUtils from '../utils/AnalyticsUtils';
|
||||
import {pushLayer} from './LayerActionCreators';
|
||||
import {ActionTypes, Endpoints, DEVICE_TOKEN, DEVICE_PUSH_PROVIDER, Layers} from '../Constants';
|
||||
|
||||
function init(section: ?string = null, subsection: ?string = null) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_MODAL_INIT,
|
||||
section,
|
||||
subsection,
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
open(section: ?string = null, subsection: ?string = null) {
|
||||
if (__IOS__) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_MODAL_OPEN,
|
||||
section,
|
||||
subsection,
|
||||
});
|
||||
} else {
|
||||
init(section, subsection);
|
||||
pushLayer(Layers.USER_SETTINGS);
|
||||
}
|
||||
},
|
||||
|
||||
init,
|
||||
|
||||
close() {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_MODAL_CLOSE,
|
||||
});
|
||||
},
|
||||
|
||||
setSection(section: string, subsection: ?string = null) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_MODAL_SET_SECTION,
|
||||
section,
|
||||
subsection,
|
||||
});
|
||||
},
|
||||
|
||||
updateAccount(settings: {
|
||||
username?: string,
|
||||
email?: string,
|
||||
password?: string,
|
||||
newPassword?: ?string,
|
||||
avatar?: ?string,
|
||||
}) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_MODAL_UPDATE_ACCOUNT,
|
||||
settings,
|
||||
});
|
||||
},
|
||||
|
||||
saveAccountChanges(username: string, email: string, password: string, avatar: string, newPassword: string) {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.USER_SETTINGS_MODAL_SUBMIT,
|
||||
});
|
||||
|
||||
mfaInterceptor(
|
||||
{
|
||||
title: i18n.Messages.TWO_FA_CHANGE_ACCOUNT,
|
||||
},
|
||||
extraBody => {
|
||||
const body = {
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
avatar,
|
||||
// eslint-disable-next-line camelcase
|
||||
new_password: newPassword,
|
||||
...extraBody,
|
||||
};
|
||||
// Only send these values if we have them.
|
||||
const pushToken = Storage.get(DEVICE_TOKEN);
|
||||
if (DEVICE_PUSH_PROVIDER != null && pushToken != null) {
|
||||
body['push_provider'] = DEVICE_PUSH_PROVIDER;
|
||||
body['push_token'] = pushToken;
|
||||
}
|
||||
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.ME,
|
||||
body,
|
||||
});
|
||||
},
|
||||
{
|
||||
// Only open mfa modal if the server wants the code. In this case, we do not want to optimistically open it.
|
||||
checkEnabled: false,
|
||||
hooks: {
|
||||
onEarlyClose: () => Dispatcher.dispatch({type: ActionTypes.USER_SETTINGS_MODAL_SUBMIT_FAILURE, errors: {}}),
|
||||
},
|
||||
}
|
||||
).then(
|
||||
res => {
|
||||
const user = res.body;
|
||||
const token = user.token;
|
||||
AnalyticsUtils.track('user_avatar_updated', {animated: hasAnimatedAvatar(user)});
|
||||
delete user.token;
|
||||
Dispatcher.dispatch({type: ActionTypes.UPDATE_TOKEN, token});
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_UPDATE, user});
|
||||
this.close(true);
|
||||
},
|
||||
res => {
|
||||
Dispatcher.dispatch({type: ActionTypes.USER_SETTINGS_MODAL_SUBMIT_FAILURE, errors: res.body});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/UserSettingsModalActionCreators.js
|
|
@ -0,0 +1,88 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes, Endpoints} from '../Constants';
|
||||
import HTTPUtils from '../utils/HTTPUtils';
|
||||
|
||||
export default {
|
||||
fetchForGuild(guildId: string): void {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.WEBHOOKS_FETCHING,
|
||||
guildId,
|
||||
});
|
||||
HTTPUtils.get(Endpoints.GUILD_WEBHOOKS(guildId))
|
||||
.then(({body}) =>
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.WEBHOOKS_UPDATE,
|
||||
guildId,
|
||||
webhooks: body,
|
||||
})
|
||||
)
|
||||
.catch(({body}) => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.WEBHOOKS_UPDATE,
|
||||
guildId,
|
||||
error: body.message,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
fetchForChannel(guildId: string, channelId: string): void {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.WEBHOOKS_FETCHING,
|
||||
guildId,
|
||||
channelId,
|
||||
});
|
||||
HTTPUtils.get(Endpoints.CHANNEL_WEBHOOKS(channelId)).then(({body}) =>
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.WEBHOOKS_UPDATE,
|
||||
guildId,
|
||||
channelId,
|
||||
webhooks: body,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
create(guildId: string, channelId: string, body: Object): Promise {
|
||||
return HTTPUtils.post({
|
||||
url: Endpoints.CHANNEL_WEBHOOKS(channelId),
|
||||
body,
|
||||
}).then(({body}) => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.WEBHOOK_CREATE,
|
||||
guildId,
|
||||
webhook: body,
|
||||
});
|
||||
return body;
|
||||
});
|
||||
},
|
||||
|
||||
delete(guildId: string, webhookId: string): Promise {
|
||||
return HTTPUtils.delete(Endpoints.WEBHOOK(webhookId)).then(() => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.WEBHOOK_DELETE,
|
||||
guildId,
|
||||
webhookId,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
update(guildId: string, webhookId: string, body: Object): Promise {
|
||||
return HTTPUtils.patch({
|
||||
url: Endpoints.WEBHOOK(webhookId),
|
||||
body,
|
||||
}).then(({body}) => {
|
||||
Dispatcher.dispatch({
|
||||
type: ActionTypes.WEBHOOK_UPDATE,
|
||||
guildId,
|
||||
webhook: body,
|
||||
});
|
||||
return body;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/WebhooksActionCreators.js
|
|
@ -0,0 +1,24 @@
|
|||
/* @flow */
|
||||
|
||||
import Dispatcher from '../Dispatcher';
|
||||
import {ActionTypes} from '../Constants';
|
||||
|
||||
export default {
|
||||
focus(focused: boolean) {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.WINDOW_FOCUS,
|
||||
focused,
|
||||
});
|
||||
},
|
||||
|
||||
resized() {
|
||||
Dispatcher.dirtyDispatch({
|
||||
type: ActionTypes.WINDOW_RESIZED,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/WindowActionCreators.js
|
|
@ -0,0 +1,54 @@
|
|||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import ModalActionCreators from '../ModalActionCreators';
|
||||
import Alert from '../../components/Alert';
|
||||
|
||||
export default {
|
||||
show({
|
||||
title,
|
||||
body,
|
||||
confirmText,
|
||||
cancelText,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
iconUrl,
|
||||
minorText,
|
||||
onConfirmSecondary,
|
||||
className,
|
||||
}: {
|
||||
title: string,
|
||||
body: string,
|
||||
confirmText: string,
|
||||
cancelText: string,
|
||||
onConfirm: Function,
|
||||
onCancel: Function,
|
||||
iconUrl: string,
|
||||
minorText: string,
|
||||
onConfirmSecondary: Function,
|
||||
className: string,
|
||||
}) {
|
||||
ModalActionCreators.push(props => {
|
||||
return (
|
||||
<Alert
|
||||
{...props}
|
||||
title={title}
|
||||
body={body}
|
||||
confirmText={confirmText}
|
||||
cancelText={cancelText}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
iconUrl={iconUrl}
|
||||
minorText={minorText}
|
||||
onConfirmSecondary={onConfirmSecondary}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// WEBPACK FOOTER //
|
||||
// ./discord_app/actions/web/AlertActionCreators.js
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue