Add files

This commit is contained in:
DoomRye 2022-07-26 10:06:20 -07:00
commit bb80829159
18195 changed files with 2122994 additions and 0 deletions

605
509bba001a76711dd089.css Executable file

File diff suppressed because one or more lines are too long

1
509bba001a76711dd089.css.map Executable file
View File

@ -0,0 +1 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"509bba001a76711dd089.css","sourceRoot":""}

2
509bba001a76711dd089.js Executable file

File diff suppressed because one or more lines are too long

1
509bba001a76711dd089.js.map Executable file

File diff suppressed because one or more lines are too long

191208
509bba001a76711dd089_formatted.js Executable file

File diff suppressed because one or more lines are too long

215639
509bba001a76711dd089_mapped.js Executable file

File diff suppressed because one or more lines are too long

2718
509bba0_supplemented_files_log.txt Executable file

File diff suppressed because it is too large Load Diff

8
509bba0_unpacked/CHANGELOG.md Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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