/* @flow */ import React from 'react'; import ReactDOM from 'react-dom'; import lodash from 'lodash'; import fuzzysearch from 'fuzzysearch'; import Flux from '../../lib/flux'; import GuildSettingsAuditLogStore from '../../stores/GuildSettingsAuditLogStore'; import {fetchLogs, fetchNextLogPage, filterByAction, filterByUserId} from '../../actions/AuditLogActionCreators'; import PopoutActionCreators from '../../actions/PopoutActionCreators'; import AuditLogRecord, {AuditLogChange, getActionType, getTargetType} from '../../records/AuditLogRecord'; import AuditLog, {AuditLogIcon} from '../../uikit/AuditLog'; import Avatar from '../../uikit/Avatar'; import MembersIcon from '../../uikit/icons/MembersIcon'; import EmptyState, {EmptyStateText, EmptyStateImage} from '../../uikit/EmptyState'; import Spinner from '../../uikit/Spinner'; import Flex from '../../uikit/Flex'; import Button from '../../uikit/Button'; import Scroller from '../../uikit/Scroller'; import {FormTitle, FormTitleTags, FormDivider} from '../../uikit/form'; import SelectableItem from '../../uikit/SelectableItem'; import SearchableQuickSelect from '../common/SearchableQuickSelect'; import ContextMenu from '../common/ContextMenu'; import StreamerModeEnabled from '../StreamerModeEnabled'; import UserContextMenu from '../contextmenus/UserContextMenu'; import ChannelContextMenu from '../contextmenus/ChannelContextMenu'; import DeveloperContextMenu from '../contextmenus/DeveloperContextMenu'; import GuildStore from '../../stores/GuildStore'; import ChannelStore from '../../stores/ChannelStore'; import StreamerModeStore from '../../stores/StreamerModeStore'; import UserStore from '../../stores/UserStore'; import EmojiStore from '../../stores/EmojiStore'; import UserSettingsStore from '../../stores/UserSettingsStore'; import GuildSettingsStore from '../../stores/GuildSettingsStore'; import InstantInviteUtils from '../../utils/InstantInviteUtils'; import {int2hex} from '../../../discord_common/js/utils/ColorUtils'; import UserRecord from '../../records/UserRecord'; import { AuditLogActions, AuditLogTargetTypes, AuditLogChangeKeys, AuditLogSubtargetTypes, Permissions, NOOP_NULL, ThemeTypes, Colors, ContextMenuTypes, } from '../../Constants'; import i18n from '../../i18n'; import type GuildRecord from '../../records/GuildRecord'; import type ChannelRecord from '../../records/ChannelRecord'; import './GuildSettingsAuditLog.styl'; type RectData = { lastExpanded: ?ClientRect, expanded: ?ClientRect, }; const MAX_GROUP_FETCH_NEXT_PAGES = 2; const USER_FILTER_POPOUT_ID = 'guild-settings-audit-logs-user-filter'; const ACTION_FILTER_POPOUT_ID = 'guild-settings-audit-logs-action-filter'; function getPermissionChanges(oldPermissions?, newPermissions?) { const oldPerms = typeof oldPermissions === 'number' ? oldPermissions : 0; const newPerms = typeof newPermissions === 'number' ? newPermissions : 0; const addedPermissionBits = newPerms & ~oldPerms; const removedPermissionBits = oldPerms & ~newPerms; const added = []; const removed = []; for (const key in Permissions) { const permission = +Permissions[key]; if ((addedPermissionBits & permission) === permission) { added.push(permission); } if ((removedPermissionBits & permission) === permission) { removed.push(permission); } } return {added, removed}; } function convertValue( change: AuditLogChange, convertFunction: (value: any) => ?T, toString?: (value: T) => string ): AuditLogChange { let newValue = change.newValue; let oldValue = change.oldValue; if (change.newValue != null) { newValue = convertFunction(change.newValue); if (toString != null && newValue != null) { newValue = toString(newValue); } } if (change.oldValue != null) { oldValue = convertFunction(change.oldValue); if (toString != null && oldValue != null) { oldValue = toString(oldValue); } } return new AuditLogChange(change.key, oldValue, newValue); } function getTargetValue( log: AuditLogRecord, keyForValue: string, getTarget: (id: string) => ?T, toString: (obj: T) => string | UserRecord | ChannelRecord, targetId?: string ) { let target = null; targetId = targetId || log.targetId; // Attempt to grab from a store or however the object is stored const targetFromParam = getTarget(targetId); if (targetFromParam != null) { target = toString(targetFromParam); } // Check deleted targets if (target == null) { const deletedTargets = GuildSettingsAuditLogStore.deletedTargets[log.targetType]; if (deletedTargets != null && deletedTargets[targetId]) { target = deletedTargets[targetId]; } } // If it is any other type of change, check the new and old values if (target == null && log.changes != null) { const change = log.changes.find(change => change.key === keyForValue); if (change != null) { target = change.newValue || change.oldValue; } } return target || log.targetId; } function convertSubtarget(id: string, getSubtarget: (id: string) => ?T, toString?: (obj: T) => string) { let subtarget = id; const subtargetFromParam = getSubtarget(id); if (subtargetFromParam != null && toString != null) { subtarget = toString(subtargetFromParam); } return subtarget; } function transformTarget(log: AuditLogRecord, guild: GuildRecord) { switch (log.targetType) { case AuditLogTargetTypes.GUILD: return guild; case AuditLogTargetTypes.CHANNEL: return getTargetValue( log, AuditLogChangeKeys.NAME, id => ChannelStore.getChannel(id), channel => channel.toString(true) ); case AuditLogTargetTypes.USER: return getTargetValue(log, AuditLogChangeKeys.NICK, id => UserStore.getUser(id), user => user); case AuditLogTargetTypes.ROLE: return getTargetValue(log, AuditLogChangeKeys.NAME, id => guild.getRole(id), role => role.name); case AuditLogTargetTypes.INVITE: return getTargetValue(log, AuditLogChangeKeys.CODE, NOOP_NULL, invite => invite.code); case AuditLogTargetTypes.WEBHOOK: return getTargetValue( log, AuditLogChangeKeys.NAME, id => GuildSettingsAuditLogStore.webhooks.find(webhook => webhook.id === id), webhook => webhook.name ); case AuditLogTargetTypes.EMOJI: return getTargetValue( log, AuditLogChangeKeys.NAME, id => EmojiStore.getGuildEmoji(guild.id).find(emoji => emoji.id === id), emoji => emoji.name ); default: console.warn('[GuildSettingsAuditLog] Unknown targetType for log', log); return null; } } function transformChange(change: AuditLogChange) { switch (change.key) { case AuditLogChangeKeys.OWNER_ID: return convertValue(change, v => UserStore.getUser(v)); case AuditLogChangeKeys.AFK_CHANNEL_ID: return convertValue(change, v => ChannelStore.getChannel(v), channel => channel.toString(true)); case AuditLogChangeKeys.AFK_TIMEOUT: return convertValue(change, v => v / 60); case AuditLogChangeKeys.BITRATE: return convertValue(change, v => v / 1000); case AuditLogChangeKeys.COLOR: return convertValue(change, v => int2hex(v).toUpperCase()); case AuditLogChangeKeys.MAX_AGE: { return convertValue(change, v => { const option = InstantInviteUtils.getMaxAgeOptions.find(({value}) => `${v}` === value); if (option) { return option.label; } else { return v; } }); } case AuditLogChangeKeys.CHANNEL_ID: { return convertValue(change, v => ChannelStore.getChannel(v), channel => channel.toString(true)); } case AuditLogChangeKeys.PERMISSIONS: { const newChanges = []; const {added, removed} = getPermissionChanges(change.oldValue, change.newValue); if (added.length > 0) { const addChange = new AuditLogChange(AuditLogChangeKeys.PERMISSIONS_GRANTED, null, added); newChanges.push(addChange); } if (removed.length) { const removeChange = new AuditLogChange(AuditLogChangeKeys.PERMISSIONS_DENIED, null, removed); newChanges.push(removeChange); } return newChanges; } case AuditLogChangeKeys.PERMISSIONS_GRANTED: case AuditLogChangeKeys.PERMISSIONS_DENIED: { const newChanges = []; const {added} = getPermissionChanges(change.oldValue, change.newValue); if (added.length > 0) { const addChange = new AuditLogChange(change.key, null, added); newChanges.push(addChange); } return newChanges; } } return change; } function transformOptions(log: AuditLogRecord) { if (log.options != null) { const newOptions = {...log.options}; switch (log.options.type) { case AuditLogSubtargetTypes.USER: { newOptions.subtarget = convertSubtarget(log.options.id, id => UserStore.getUser(id), user => user.toString()); break; } case AuditLogSubtargetTypes.ROLE: { newOptions.subtarget = convertSubtarget(log.options.role_name, NOOP_NULL); break; } } if (log.options.channel_id != null) { newOptions.channel = getTargetValue( log, '', id => ChannelStore.getChannel(id), channel => channel, log.options.channel_id ); } if (log.options.members_removed) { newOptions.count = log.options.members_removed; } return newOptions; } return log.options; } function transformLogs(logs: Array, guild: GuildRecord): Array { const newLogs = []; logs.forEach(log => { const newTarget = transformTarget(log, guild); const newUser = UserStore.getUser(log.userId); if ((newTarget == null && log.action !== AuditLogActions.MEMBER_PRUNE) || newUser == null) { return; } log = log.set('user', newUser); log = log.set('target', newTarget); log = log.set('options', transformOptions(log)); if (log.changes != null) { const newChanges = []; log.changes.forEach((change: AuditLogChange) => { const newChange = transformChange(change); if (Array.isArray(newChange)) { newChange.forEach(c => newChanges.push(c)); } else { newChanges.push(newChange); } }); log = log.set('changes', newChanges); } newLogs.push(log); }); return newLogs; } const ACTION_FILTER_ITEMS = () => [ { value: AuditLogActions.ALL, label: i18n.Messages.GUILD_SETTINGS_FILTER_ALL_ACTIONS, valueLabel: i18n.Messages.GUILD_SETTINGS_FILTER_ALL, }, {value: AuditLogActions.GUILD_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_GUILD_UPDATE}, {value: AuditLogActions.CHANNEL_CREATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_CHANNEL_CREATE}, {value: AuditLogActions.CHANNEL_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_CHANNEL_UPDATE}, {value: AuditLogActions.CHANNEL_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_CHANNEL_DELETE}, { value: AuditLogActions.CHANNEL_OVERWRITE_CREATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_CHANNEL_OVERWRITE_CREATE, }, { value: AuditLogActions.CHANNEL_OVERWRITE_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_CHANNEL_OVERWRITE_UPDATE, }, { value: AuditLogActions.CHANNEL_OVERWRITE_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_CHANNEL_OVERWRITE_DELETE, }, {value: AuditLogActions.MEMBER_KICK, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MEMBER_KICK}, {value: AuditLogActions.MEMBER_PRUNE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MEMBER_PRUNE}, {value: AuditLogActions.MEMBER_BAN_ADD, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MEMBER_BAN_ADD}, {value: AuditLogActions.MEMBER_BAN_REMOVE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MEMBER_BAN_REMOVE}, {value: AuditLogActions.MEMBER_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MEMBER_UPDATE}, {value: AuditLogActions.MEMBER_ROLE_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MEMBER_ROLE_UPDATE}, {value: AuditLogActions.ROLE_CREATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_ROLE_CREATE}, {value: AuditLogActions.ROLE_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_ROLE_UPDATE}, {value: AuditLogActions.ROLE_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_ROLE_DELETE}, {value: AuditLogActions.INVITE_CREATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_INVITE_CREATE}, {value: AuditLogActions.INVITE_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_INVITE_UPDATE}, {value: AuditLogActions.INVITE_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_INVITE_DELETE}, {value: AuditLogActions.WEBHOOK_CREATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_WEBHOOK_CREATE}, {value: AuditLogActions.WEBHOOK_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_WEBHOOK_UPDATE}, {value: AuditLogActions.WEBHOOK_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_WEBHOOK_DELETE}, {value: AuditLogActions.EMOJI_CREATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_EMOJI_CREATE}, {value: AuditLogActions.EMOJI_UPDATE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_EMOJI_UPDATE}, {value: AuditLogActions.EMOJI_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_EMOJI_DELETE}, {value: AuditLogActions.MESSAGE_DELETE, label: i18n.Messages.GUILD_SETTINGS_ACTION_FILTER_MESSAGE_DELETE}, ]; class AuditLogClickWrap extends React.PureComponent { constructor(props: any) { super(props); (this: any).handleHeaderClick = this.handleHeaderClick.bind(this); (this: any).handleUserContextMenu = this.handleUserContextMenu.bind(this); (this: any).handleChannelContextMenu = this.handleChannelContextMenu.bind(this); (this: any).handleTargetContextMenu = this.handleTargetContextMenu.bind(this); } render() { // eslint-disable-next-line no-unused-vars const {onHeaderClick, guildId, ...props} = this.props; return ( ); } handleHeaderClick(e: Event) { const {onHeaderClick, log} = this.props; onHeaderClick && onHeaderClick(log, e); } handleUserContextMenu(e: Event) { const {log, guildId} = this.props; ContextMenu.openContextMenu(e, props => ); } handleChannelContextMenu(e: Event) { const {log, guildId} = this.props; const guild = GuildStore.getGuild(guildId); if (log.options.channel != null && guild != null) { ContextMenu.openContextMenu(e, props => ); } } handleTargetContextMenu(e: Event) { const {log, guildId} = this.props; ContextMenu.openContextMenu(e, props => { switch (log.targetType) { case AuditLogTargetTypes.CHANNEL: const channel = ChannelStore.getChannel(log.targetId); const guild = GuildStore.getGuild(guildId); if (channel != null && guild != null) { return ( ); } else { return ; } case AuditLogTargetTypes.USER: const user = UserStore.getUser(log.targetId); if (user != null) { return ; } break; } return null; }); } } class GuildSettingsAuditLog extends React.PureComponent { _clickedInside = false; _scrollerRef: Scroller; _expandedRef: ?AuditLogClickWrap; _lastExpandedRef: ?AuditLogClickWrap; _prevRects: RectData; state: { expandedId: ?string, lastExpandedId: ?string, userFilterQuery: string, actionFilterQuery: string, }; constructor(props: any) { super(props); this.state = { expandedId: null, lastExpandedId: null, userFilterQuery: '', actionFilterQuery: '', }; lodash.bindAll(this, [ 'renderActionQuickSelectItem', 'renderUserQuickSelectItem', 'renderHeaderDropdowns', 'handleFilterActionChange', 'handleFilterUserChange', 'handleHeaderClick', 'handleOutsideClick', 'handleContentClick', 'handleOnScroll', 'handleSetScrollerRef', 'handleUserFilterQueryChange', 'handleUserFilterQueryClear', 'handleActionFilterQueryChange', 'handleActionFilterQueryClear', 'handleSetExpandedRef', 'handleSetLastExpandedRef', 'handleFetchNextPage', ]); } componentDidMount() { fetchLogs(this.props.guildId); document.addEventListener('click', this.handleOutsideClick); } componentWillUnmount() { document.removeEventListener('click', this.handleOutsideClick); } componentWillUpdate(_, nextState) { if (this.state.expandedId !== nextState.expandedId) { this._prevRects = this.getRects(); } } componentDidUpdate(prevProps, prevState) { if (this.state.expandedId !== prevState.expandedId) { this.fixScroll(); } if ( !this.props.showLoadMore && this.props.logs.length !== prevProps.logs.length && this.isScrollerAtBottom(this._scrollerRef) ) { fetchNextLogPage(this.props.guildId, true); } } isScrollerAtBottom(scroller: Scroller) { const {offsetHeight, scrollHeight, scrollTop} = scroller.getScrollData(); return scrollTop + offsetHeight >= scrollHeight; } fixScroll() { const rects = this.getRects(); const prevRects = this._prevRects; // If we are not transitioning from one open to another, the scroll doesn't have to change if (rects.expanded == null || rects.lastExpanded == null || prevRects.expanded == null) { return; } // If the opening log is above, the scroll doesn't have to change if (rects.expanded.top < rects.lastExpanded.top) { return; } const heightDiff = prevRects.expanded.height - rects.lastExpanded.height; const scrollData = this._scrollerRef.getScrollData(); const scrollTo = scrollData.scrollTop - heightDiff; this._scrollerRef.scrollTo(scrollTo); } getRects() { const lastExpandedNode = ReactDOM.findDOMNode(this._lastExpandedRef); const expandedNode = ReactDOM.findDOMNode(this._expandedRef); const rects: RectData = { lastExpanded: null, expanded: null, }; if (lastExpandedNode != null && lastExpandedNode instanceof Element) { rects.lastExpanded = lastExpandedNode.getBoundingClientRect(); } if (expandedNode != null && expandedNode instanceof Element) { rects.expanded = expandedNode.getBoundingClientRect(); } return rects; } renderActionQuickSelectItem(item: *, index: number) { const actionType = getActionType(item.value); const targetType = getTargetType(item.value); return ( this.handleFilterActionChange(item)}> {item.label} ); } renderUserQuickSelectItem(item: *, index: number) { let content = null; if (item.label instanceof UserRecord) { const user = item.label; content = [ , {user.username} #{user.discriminator} , ]; } else { content = [ , {item.label} , ]; } return ( this.handleFilterUserChange(item)} key={index} style={{height: 'auto'}}> {content} ); } renderUserQuickSelectValue(value) { if (value instanceof UserRecord) { return value.username; } else { return value.valueLabel || value.label; } } renderActionQuickSelectValue(value) { return value.valueLabel || value.label; } renderHeaderDropdowns() { const {actionFilter, hide, userIdFilter, moderators} = this.props; const {userFilterQuery, actionFilterQuery} = this.state; if (hide) { return null; } const allActionItems = ACTION_FILTER_ITEMS().map(action => ({...action, selected: action.value === actionFilter})); const actionItems = allActionItems.filter(action => fuzzysearch(actionFilterQuery.toLowerCase(), action.label.toLowerCase()) ); const actionValue = allActionItems.find(({value}) => actionFilter === value); const searchActionProps = { query: actionFilterQuery, onChange: this.handleActionFilterQueryChange, onClear: this.handleActionFilterQueryClear, placeholder: i18n.Messages.SEARCH_ACTIONS, }; const allUserIdOption = { label: i18n.Messages.GUILD_SETTINGS_FILTER_ALL_USERS, valueLabel: i18n.Messages.GUILD_SETTINGS_FILTER_ALL, value: null, selected: userIdFilter == null, }; const allUserItems = [allUserIdOption, ...moderators]; const userItems = allUserItems .filter(user => { const query = userFilterQuery.toLowerCase(); if (user instanceof UserRecord) { return fuzzysearch(query, user.username.toLowerCase()); } else { return fuzzysearch(query, user.label); } }) .map(user => { if (user instanceof UserRecord) { return {label: user, value: user.id, selected: user.id === userIdFilter}; } else { return user; } }); const userValue = allUserItems.find(user => { if (user instanceof UserRecord) { return user.id === userIdFilter; } return user.value === userIdFilter; }) || allUserIdOption; const searchUsersProps = { query: userFilterQuery, onChange: this.handleUserFilterQueryChange, onClear: this.handleUserFilterQueryClear, placeholder: i18n.Messages.SEARCH_MEMBERS, }; return [ , , ]; } renderHeader() { return ( {i18n.Messages.GUILD_SETTINGS_LABEL_AUDIT_LOG} {this.renderHeaderDropdowns()} ); } renderSpinner() { return ; } renderContent() { const {expandedId, lastExpandedId} = this.state; const {logs, theme, hide, isInitialLoading, isLoading, hasError, guildId} = this.props; if (hide) { return ; } if (isLoading || isInitialLoading) { return this.renderSpinner(); } if (logs.length === 0) { const body = hasError ? i18n.Messages.GUILD_SETTINGS_LABEL_AUDIT_LOG_ERROR_BODY : i18n.Messages.GUILD_SETTINGS_LABEL_AUDIT_LOG_EMPTY_BODY; const title = hasError ? i18n.Messages.GUILD_SETTINGS_LABEL_AUDIT_LOG_ERROR_TITLE : i18n.Messages.GUILD_SETTINGS_LABEL_AUDIT_LOG_EMPTY_TITLE; return ( {title} ); } return logs.map(log => { const expanded = expandedId === log.id; const lastExpanded = lastExpandedId === log.id; const ref = expanded ? this.handleSetExpandedRef : lastExpanded ? this.handleSetLastExpandedRef : null; return ( ); }); } renderLoadMore() { const {showLoadMore, hasOlderLogs} = this.props; if (showLoadMore && hasOlderLogs) { return ( ); } } render() { const {isLoadingNextPage, hide, isLoading, theme} = this.props; const backgroundColor = theme === ThemeTypes.DARK ? Colors.PRIMARY_600 : Colors.WHITE; return (
{this.renderHeader()} {this.renderContent()} {this.renderLoadMore()} {isLoadingNextPage && !hide && !isLoading ? this.renderSpinner() : null}
); } handleFilterActionChange({value}) { PopoutActionCreators.close(ACTION_FILTER_POPOUT_ID); filterByAction(value, this.props.guildId); } handleFilterUserChange({value}) { PopoutActionCreators.close(USER_FILTER_POPOUT_ID); filterByUserId(value, this.props.guildId); } handleHeaderClick(log: AuditLogRecord) { const {expandedId} = this.state; if (expandedId !== log.id) { this._clickedInside = true; this.setState({expandedId: log.id, lastExpandedId: expandedId}); } else { this.setState({expandedId: null, lastExpandedId: null}); } } handleOutsideClick() { if (this.state.expandedId != null && !this._clickedInside) { this.setState({expandedId: null, lastExpandedId: null}); } else if (this.state.expandedId != null) { this._clickedInside = false; } } handleContentClick(e: Event) { this._clickedInside = true; e.stopPropagation(); } handleSetScrollerRef(ref: Scroller) { this._scrollerRef = ref; } handleOnScroll(scroller: Scroller) { if (this.isScrollerAtBottom(scroller)) { this.handleFetchNextPage(); } } handleFetchNextPage() { fetchNextLogPage(this.props.guildId); } handleUserFilterQueryChange(query: string) { this.setState({userFilterQuery: query}); PopoutActionCreators.rerender(USER_FILTER_POPOUT_ID); } handleUserFilterQueryClear() { this.setState({userFilterQuery: ''}); PopoutActionCreators.rerender(USER_FILTER_POPOUT_ID); } handleActionFilterQueryChange(query: string) { this.setState({actionFilterQuery: query}); PopoutActionCreators.rerender(ACTION_FILTER_POPOUT_ID); } handleActionFilterQueryClear() { this.setState({actionFilterQuery: ''}); PopoutActionCreators.rerender(ACTION_FILTER_POPOUT_ID); } handleSetExpandedRef(ref: AuditLogClickWrap) { this._expandedRef = ref; } handleSetLastExpandedRef(ref: AuditLogClickWrap) { this._lastExpandedRef = ref; } } export default Flux.connectStores( [GuildSettingsStore, GuildSettingsAuditLogStore, GuildStore, ChannelStore, UserStore, EmojiStore, UserSettingsStore], () => { const guildId = GuildSettingsStore.getGuildId(); const guild = GuildStore.getGuild(guildId); const logs = GuildSettingsAuditLogStore.logs; const moderators = GuildSettingsAuditLogStore.userIds.map(id => UserStore.getUser(id)); return { guildId, moderators, isInitialLoading: GuildSettingsAuditLogStore.isInitialLoading, isLoading: GuildSettingsAuditLogStore.isLoading, isLoadingNextPage: GuildSettingsAuditLogStore.isLoadingNextPage, showLoadMore: GuildSettingsAuditLogStore.groupedFetchCount > MAX_GROUP_FETCH_NEXT_PAGES, hasError: GuildSettingsAuditLogStore.hasError, hasOlderLogs: GuildSettingsAuditLogStore.hasOlderLogs, logs: logs != null && guild != null ? transformLogs(logs, guild) : [], actionFilter: GuildSettingsAuditLogStore.actionFilter, userIdFilter: GuildSettingsAuditLogStore.userIdFilter, theme: UserSettingsStore.theme, hide: StreamerModeStore.enabled, }; } )(GuildSettingsAuditLog); // WEBPACK FOOTER // // ./discord_app/components/guild_settings/GuildSettingsAuditLog.js