Skip to content

Commit

Permalink
Connect thrash checker (keybase#21506)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisnojima authored Dec 11, 2019
1 parent 2f3a518 commit 9936476
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 108 deletions.
1 change: 1 addition & 0 deletions shared/__mocks__/feature-flags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ff: FeatureFlags = {
airdrop: true,
audioAttachments: true,
chatIndexProfilingEnabled: false,
connectThrashCheck: true,
dbCleanEnabled: false,
fastAccountSwitch: true,
foldersInProfileTab: true,
Expand Down
103 changes: 68 additions & 35 deletions shared/chat/conversation/info-panel/menu/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,18 @@ const moreThanOneSubscribedChannel = (
})
}

// TODO convProps was being made all the time and thrashing
// how this works should change. i just normalized this so it doesn't thrash
export default Container.namedConnect(
(state, {conversationIDKey, isSmallTeam, teamID: _teamID, visible}: OwnProps) => {
let convProps: ConvProps | undefined
let _convPropsFullname: ConvProps['fullname'] | undefined
let _convPropsIgnored: ConvProps['ignored'] | undefined
let _convPropsMuted: ConvProps['muted'] | undefined
let _convPropsParticipants: ConvProps['participants'] | undefined
let _convPropsTeamID: ConvProps['teamID'] | undefined
let _convPropsTeamType: ConvProps['teamType'] | undefined
let _convPropsTeamname: ConvProps['teamname'] | undefined

let teamDetails: TeamTypes.TeamDetails | undefined
let teamname: string = ''
let teamID: TeamTypes.TeamID = TeamTypes.noTeamID
Expand All @@ -61,15 +70,13 @@ export default Container.namedConnect(
teamDetails = isTeam ? TeamConstants.getTeamDetails(state, meta.teamID) : undefined
teamname = meta.teamname
teamID = meta.teamID
convProps = {
fullname,
ignored: meta.status === RPCChatTypes.ConversationStatus.ignored,
muted: meta.isMuted,
participants,
teamID: meta.teamID,
teamType: meta.teamType,
teamname,
}
_convPropsFullname = fullname
_convPropsIgnored = meta.status === RPCChatTypes.ConversationStatus.ignored
_convPropsMuted = meta.isMuted
_convPropsParticipants = participants
_convPropsTeamID = meta.teamID
_convPropsTeamType = meta.teamType
_convPropsTeamname = teamname
} else if (_teamID) {
teamID = _teamID
teamDetails = TeamConstants.getTeamDetails(state, teamID)
Expand All @@ -78,9 +85,15 @@ export default Container.namedConnect(
// skip a bunch of stuff for menus that aren't visible
if (!visible) {
return {
_convPropsFullname,
_convPropsIgnored,
_convPropsMuted,
_convPropsParticipants,
_convPropsTeamID,
_convPropsTeamType,
_convPropsTeamname,
badgeSubscribe: false,
canAddPeople: false,
convProps,
isSmallTeam: false,
manageChannelsSubtitle: '',
manageChannelsTitle: '',
Expand All @@ -99,10 +112,16 @@ export default Container.namedConnect(
: 'Subscribe to channels...'
const manageChannelsSubtitle = isSmallTeam ? 'Turns this into a big team' : ''
return {
_teamID: convProps?.teamID ?? teamID,
_convPropsFullname,
_convPropsIgnored,
_convPropsMuted,
_convPropsParticipants,
_convPropsTeamID,
_convPropsTeamType,
_convPropsTeamname,
_teamID: _convPropsTeamID ?? teamID,
badgeSubscribe,
canAddPeople: yourOperations.manageMembers,
convProps,
isSmallTeam,
manageChannelsSubtitle,
manageChannelsTitle,
Expand Down Expand Up @@ -148,27 +167,41 @@ export default Container.namedConnect(
onMuteConv: (muted: boolean) => dispatch(ChatGen.createMuteConversation({conversationIDKey, muted})),
onUnhideConv: () => dispatch(ChatGen.createUnhideConversation({conversationIDKey})),
}),
(s, d, o) => ({
attachTo: o.attachTo,
badgeSubscribe: s.badgeSubscribe,
canAddPeople: s.canAddPeople,
convProps: s.convProps,
isSmallTeam: s.isSmallTeam,
manageChannelsSubtitle: s.manageChannelsSubtitle,
manageChannelsTitle: s.manageChannelsTitle,
memberCount: s.memberCount,
onAddPeople: () => d._onAddPeople(s.convProps?.teamID || undefined),
onBlockConv: () => d._onBlockConv(s.teamname, s.convProps?.participants ?? []),
onHidden: o.onHidden,
onHideConv: d.onHideConv,
onInvite: () => d._onInvite(s._teamID),
onLeaveTeam: () => d._onLeaveTeam(s._teamID),
onManageChannels: () => d._onManageChannels(s.teamname),
onMuteConv: d.onMuteConv,
onUnhideConv: d.onUnhideConv,
onViewTeam: () => d._onViewTeam(s._teamID || undefined),
teamname: s.teamname,
visible: o.visible,
}),
(s, d, o) => {
const convProps: ConvProps | undefined = s._convPropsFullname
? {
fullname: s._convPropsFullname!,
ignored: s._convPropsIgnored!,
muted: s._convPropsMuted!,
participants: s._convPropsParticipants!,
teamID: s._convPropsTeamID!,
teamType: s._convPropsTeamType!,
teamname: s._convPropsTeamname!,
}
: undefined

return {
attachTo: o.attachTo,
badgeSubscribe: s.badgeSubscribe,
canAddPeople: s.canAddPeople,
convProps,
isSmallTeam: s.isSmallTeam,
manageChannelsSubtitle: s.manageChannelsSubtitle,
manageChannelsTitle: s.manageChannelsTitle,
memberCount: s.memberCount,
onAddPeople: () => d._onAddPeople(s._convPropsTeamID || undefined),
onBlockConv: () => d._onBlockConv(s.teamname, s._convPropsParticipants ?? []),
onHidden: o.onHidden,
onHideConv: d.onHideConv,
onInvite: () => d._onInvite(s._teamID),
onLeaveTeam: () => d._onLeaveTeam(s._teamID),
onManageChannels: () => d._onManageChannels(s.teamname),
onMuteConv: d.onMuteConv,
onUnhideConv: d.onUnhideConv,
onViewTeam: () => d._onViewTeam(s._teamID || undefined),
teamname: s.teamname,
visible: o.visible,
}
},
'TeamDropdownMenu'
)(InfoPanelMenu)
62 changes: 29 additions & 33 deletions shared/chat/conversation/messages/cards/team-journey/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,40 +146,36 @@ const TeamJourneyContainer = (props: Props) => {
) : null
}

const mapStateToProps = (state: Container.TypedState, ownProps: OwnProps) => {
const conv = Constants.getMeta(state, ownProps.message.conversationIDKey)
const {channelname, conversationIDKey, teamname, teamID} = conv
return {
_channelInfos: TeamConstants.getTeamChannelInfos(state, teamname),
_teamID: teamID,
channelname,
conversationIDKey,
teamType: TeamConstants.getTeamType(state, teamname),
teamname,
}
}

const mapDispatchToProps = (dispatch: Container.TypedDispatch) => ({
_onAddPeopleToTeam: (teamID: TeamTypes.TeamID) => dispatch(appendNewTeamBuilder(teamID)),
_onBrowseChannels: (teamname: string) =>
dispatch(
RouteTreeGen.createNavigateAppend({path: [{props: {teamname}, selected: 'chatManageChannels'}]})
),
_onCreateChatChannels: (teamname: string) =>
dispatch(
RouteTreeGen.createNavigateAppend({path: [{props: {teamname}, selected: 'chatManageChannels'}]})
),
_onGoToChannel: (channelname: string, teamname: string) =>
dispatch(Chat2Gen.createPreviewConversation({channelname, reason: 'journeyCardPopular', teamname})),
_onLoadTeam: (teamname: string) => dispatch(TeamsGen.createGetChannels({teamname})),
_onPublishTeam: () => dispatch(RouteTreeGen.createNavigateAppend({path: ['profileShowcaseTeamOffer']})),
_onShowTeam: (teamID: TeamTypes.TeamID) =>
dispatch(RouteTreeGen.createNavigateAppend({path: [teamsTab, {props: {teamID}, selected: 'team'}]})),
})

const TeamJourneyConnected = Container.connect(
mapStateToProps,
mapDispatchToProps,
(state, ownProps: OwnProps) => {
const conv = Constants.getMeta(state, ownProps.message.conversationIDKey)
const {channelname, conversationIDKey, teamname, teamID} = conv
return {
_channelInfos: TeamConstants.getTeamChannelInfos(state, teamname),
_teamID: teamID,
channelname,
conversationIDKey,
teamType: TeamConstants.getTeamType(state, teamname),
teamname,
}
},
dispatch => ({
_onAddPeopleToTeam: (teamID: TeamTypes.TeamID) => dispatch(appendNewTeamBuilder(teamID)),
_onBrowseChannels: (teamname: string) =>
dispatch(
RouteTreeGen.createNavigateAppend({path: [{props: {teamname}, selected: 'chatManageChannels'}]})
),
_onCreateChatChannels: (teamname: string) =>
dispatch(
RouteTreeGen.createNavigateAppend({path: [{props: {teamname}, selected: 'chatManageChannels'}]})
),
_onGoToChannel: (channelname: string, teamname: string) =>
dispatch(Chat2Gen.createPreviewConversation({channelname, reason: 'journeyCardPopular', teamname})),
_onLoadTeam: (teamname: string) => dispatch(TeamsGen.createGetChannels({teamname})),
_onPublishTeam: () => dispatch(RouteTreeGen.createNavigateAppend({path: ['profileShowcaseTeamOffer']})),
_onShowTeam: (teamID: TeamTypes.TeamID) =>
dispatch(RouteTreeGen.createNavigateAppend({path: [teamsTab, {props: {teamID}, selected: 'team'}]})),
}),
(stateProps, dispatchProps, ownProps) => {
const {channelname, conversationIDKey, teamname, teamType} = stateProps
// Take the top three channels with most recent activity.
Expand Down
3 changes: 2 additions & 1 deletion shared/constants/chat2/meta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,11 @@ export const getConversationIDKeyMetasToLoad = (
return arr
}, [])

export const getRowParticipants = (meta: Types.ConversationMeta, username: string) =>
export const getRowParticipants = memoize((meta: Types.ConversationMeta, username: string) =>
meta.nameParticipants
// Filter out ourselves unless it's our 1:1 conversation
.filter((participant, _, list) => (list.length === 1 ? true : participant !== username))
)

export const timestampToString = (meta: Types.ConversationMeta) =>
formatTimeForConversationList(meta.timestamp)
Expand Down
5 changes: 3 additions & 2 deletions shared/constants/devices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as Types from './types/devices'
import * as WaitingConstants from './waiting'
import * as RPCTypes from './types/rpc-gen'
import * as Container from '../util/container'
import {memoize} from '../util/memoize'

export const rpcDeviceToDevice = (d: RPCTypes.DeviceDetail): Types.Device =>
makeDevice({
Expand Down Expand Up @@ -75,7 +76,7 @@ export const getDeviceIconNumberInner = (
deviceID: Types.DeviceID
): number => ((devices.get(deviceID) || {deviceNumberOfType: 0}).deviceNumberOfType % numBackgrounds) + 1

const getNextDeviceIconNumberInner = (devices: Map<Types.DeviceID, Types.Device>) => {
const getNextDeviceIconNumberInner = memoize((devices: Map<Types.DeviceID, Types.Device>) => {
// Find the max device number and add one (+ one more since these are 1-indexed)
const result = {backup: 1, desktop: 1, mobile: 1}
devices.forEach(device => {
Expand All @@ -84,7 +85,7 @@ const getNextDeviceIconNumberInner = (devices: Map<Types.DeviceID, Types.Device>
}
})
return {desktop: (result.desktop % numBackgrounds) + 1, mobile: (result.mobile % numBackgrounds) + 1}
}
})

export const getDeviceIconNumber = (state: Container.TypedState, deviceID: Types.DeviceID) =>
getDeviceIconNumberInner(state.devices.deviceMap, deviceID)
Expand Down
69 changes: 40 additions & 29 deletions shared/constants/teams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,13 @@ export const getEmailInviteError = (state: TypedState) => state.teams.emailInvit
export const isTeamWithChosenChannels = (state: TypedState, teamname: string): boolean =>
state.teams.teamsWithChosenChannels.has(teamname)

const noInfos = new Map<ChatTypes.ConversationIDKey, Types.ChannelInfo>()

export const getTeamChannelInfos = (
state: TypedState,
teamname: Types.Teamname
): Map<ChatTypes.ConversationIDKey, Types.ChannelInfo> => {
return (
state.teams.teamNameToChannelInfos.get(teamname) ||
new Map<ChatTypes.ConversationIDKey, Types.ChannelInfo>()
)
}
): Map<ChatTypes.ConversationIDKey, Types.ChannelInfo> =>
state.teams.teamNameToChannelInfos.get(teamname) ?? noInfos

export const getChannelInfoFromConvID = (
state: TypedState,
Expand All @@ -328,6 +326,37 @@ export const hasChannelInfos = (state: TypedState, teamname: Types.Teamname): bo
export const isLastOwner = (state: TypedState, teamname: Types.Teamname): boolean =>
isOwner(getRoleByName(state, teamname)) && !isMultiOwnerTeam(state, teamname)

const subteamsCannotHaveOwners = {owner: 'Subteams cannot have owners.'}
const onlyOwnersCanTurnTeamMembersInfoOwners = {owner: 'Only owners can turn team members into owners.'}
const roleChangeSub = {
admin: 'You must be at least an admin to make role changes.',
owner: 'Subteams cannot have owners',
reader: 'You must be at least an admin to make role changes.',
writer: 'You must be at least an admin to make role changes.',
}
const roleChangeNotSub = {
admin: 'You must be at least an admin to make role changes.',
owner: 'You must be at least an admin to make role changes.',
reader: 'You must be at least an admin to make role changes.',
writer: 'You must be at least an admin to make role changes.',
}

const anotherRoleChangeSub = {
admin: `Only owners can change another owner's role`,
owner: 'Subteams cannot have owners.',
reader: `Only owners can change another owner's role`,
writer: `Only owners can change another owner's role`,
}
const anotherRoleChangeNotSub = {
admin: `Only owners can change another owner's role`,
owner: `Only owners can change another owner's role`,
reader: `Only owners can change another owner's role`,
writer: `Only owners can change another owner's role`,
}

const notOwnerSub = {owner: 'Subteams cannot have owners.'}
const notOwnerNotSub = {owner: `Only owners can turn members into owners`}

export const getDisabledReasonsForRolePicker = (
state: TypedState,
teamID: Types.TeamID,
Expand All @@ -346,43 +375,25 @@ export const getDisabledReasonsForRolePicker = (
if (canManageMembers) {
// If you're an implicit admin, the tests below will fail for you, but you can still change roles.
return isSubteam(teamname)
? {owner: 'Subteams cannot have owners.'}
? subteamsCannotHaveOwners
: yourRole !== 'owner'
? {owner: 'Only owners can turn team members into owners.'}
? onlyOwnersCanTurnTeamMembersInfoOwners
: {}
}

// We shouldn't get here, but in case we do this is correct.
if (yourRole !== 'owner' && yourRole !== 'admin') {
return {
admin: 'You must be at least an admin to make role changes.',
owner: isSubteam(teamname)
? 'Subteams cannot have owners'
: 'You must be at least an admin to make role changes.',
reader: 'You must be at least an admin to make role changes.',
writer: 'You must be at least an admin to make role changes.',
}
return isSubteam(teamname) ? roleChangeSub : roleChangeNotSub
}

// We shouldn't get here, but in case we do this is correct.
if (theyAreOwner && yourRole !== 'owner') {
return {
admin: `Only owners can change another owner's role`,
owner: isSubteam(teamname)
? 'Subteams cannot have owners.'
: `Only owners can change another owner's role`,
reader: `Only owners can change another owner's role`,
writer: `Only owners can change another owner's role`,
}
return isSubteam(teamname) ? anotherRoleChangeSub : anotherRoleChangeNotSub
}

// We shouldn't get here, but in case we do this is correct.
if (yourRole !== 'owner') {
return {
owner: isSubteam(teamname)
? 'Subteams cannot have owners.'
: `Only owners can turn members into owners`,
}
return isSubteam(teamname) ? notOwnerSub : notOwnerNotSub
}

return {}
Expand Down
10 changes: 7 additions & 3 deletions shared/constants/wallets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {TypedState} from './reducer'
import HiddenString from '../util/hidden-string'
import flags from '../util/feature-flags'
import * as TeamBuildingConstants from './team-building'
import {memoize} from '../util/memoize'

export const balanceDeltaToString = invert(RPCTypes.BalanceDelta) as {
[K in RPCTypes.BalanceDelta]: keyof typeof RPCTypes.BalanceDelta
Expand Down Expand Up @@ -741,9 +742,11 @@ export const searchTrustlineAssetsWaitingKey = 'wallets:searchTrustlineAssets'
export const calculateBuildingAdvancedWaitingKey = 'wallets:calculateBuildingAdvanced'
export const sendPaymentAdvancedWaitingKey = 'wallets:sendPaymentAdvanced'

export const getAccountIDs = (state: TypedState) => [...state.wallets.accountMap.keys()]
const getAccountMapKeys = memoize((accountMap: Map<string, Types.Account>) => [...accountMap.keys()])
export const getAccountIDs = (state: TypedState) => getAccountMapKeys(state.wallets.accountMap)

export const getAccounts = (state: TypedState) => [...state.wallets.accountMap.values()]
const getAccountMapValues = memoize((accountMap: Map<string, Types.Account>) => [...accountMap.values()])
export const getAccounts = (state: TypedState) => getAccountMapValues(state.wallets.accountMap)

export const getAirdropSelected = (state: TypedState) =>
state.wallets.selectedAccount === Types.airdropAccountID
Expand Down Expand Up @@ -793,8 +796,9 @@ export const getDefaultAccount = (state: Types.State) => {

export const getExternalPartners = (state: TypedState) => state.wallets.externalPartners

const noAssets = []
export const getAssets = (state: TypedState, accountID: Types.AccountID): Array<Types.Assets> =>
state.wallets.assetsMap.get(accountID) ?? []
state.wallets.assetsMap.get(accountID) ?? noAssets

export const getFederatedAddress = (state: TypedState, accountID: Types.AccountID) => {
const account = state.wallets.accountMap.get(accountID) ?? unknownAccount
Expand Down
Loading

0 comments on commit 9936476

Please sign in to comment.