Created
March 22, 2020 14:42
-
-
Save dawsbot/f3244ee6dcbe549e72f03d06281752e8 to your computer and use it in GitHub Desktop.
Breaks vscode insiders syntax highlighting in relation to https://github.com/microsoft/vscode/issues/92308
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Skeleton from 'antd/lib/skeleton'; | |
import * as React from 'react'; | |
import InfiniteScroll from 'react-infinite-scroll-component'; | |
import { defineMessages, FormattedMessage as FM } from 'react-intl'; | |
import { connect } from 'react-redux'; | |
import { bindActionCreators, Dispatch } from 'redux'; | |
import styled from '@emotion/styled'; | |
import { ActivityActions } from '../../actions/activities'; | |
import { ArticleActions } from '../../actions/articles'; | |
import { ProfilesActions } from '../../actions/profiles'; | |
import { Tabs } from '../../components/tabs'; | |
import { | |
SelectCurrentlySelectedCountryCode, | |
SelectCurrentlySelectedDayJSLocaleCode, | |
SelectLanguagePacksKeyedByCountry, | |
SelectMessagesForLocale, | |
} from '../../reducers/i18n/intl-selectors'; | |
import { | |
BrowserIsMobile, | |
BrowserIsSmallScreen, | |
BrowserIsTablet, | |
GetBrowserInfo, | |
} from '../../selectors/browser.selectors'; | |
import { ChatExpandedStatus } from '../../selectors/chat.selectors'; | |
import { SelectedProposalProperties } from '../../selectors/proposal.selectors'; | |
import { AllProposalsObjectState, UserProfilesRoot } from '../../selectors/root.selectors'; | |
import { WalletAccountName } from '../../selectors/wallet.selectors'; | |
import { fetchUserStreaks, recentProposals } from '../../utils/endpoints'; | |
import { omit } from '../../utils/general'; | |
import { DarkGray } from '../styledComponents/Colors'; | |
import { FlexCustom } from '../styledComponents/Flex'; | |
import { ActivityCard } from './ActivityCard'; | |
import { ActivityCardsOwnProps, ActivityCardsState } from './activitycards.types'; | |
const messages = defineMessages({ | |
mostRecent: { | |
id: 'ActivityCards.MostRecent', | |
defaultMessage: 'MOST RECENT', | |
}, | |
expiringSoon: { | |
id: 'ActivityCards.ExpiringSoon', | |
defaultMessage: 'EXPIRING SOON', | |
}, | |
myActivity: { | |
id: 'ActivityCards.MyActivity', | |
defaultMessage: 'MY ACTIVITY', | |
}, | |
myEdits: { | |
id: 'ActivityCards.MyEdits', | |
defaultMessage: 'MY EDITS', | |
}, | |
edits: { | |
id: 'ActivityCards.edits', | |
defaultMessage: 'EDITS', | |
}, | |
votes: { | |
id: 'ActivityCards.Votes', | |
defaultMessage: 'VOTES', | |
}, | |
my_votes: { | |
id: 'ActivityCards.MyVotes', | |
defaultMessage: 'MY VOTES', | |
}, | |
}); | |
const ActivityCardsContainer = styled.div<{ chatExpanded?: boolean; isMobile: boolean; isTablet: boolean }>` | |
margin-right: ${(props) => (props.chatExpanded && !props.isMobile && !props.isTablet ? '25%' : '0px')}; | |
max-width: 1100px; | |
width: 100%; | |
width: ${(props) => (props.chatExpanded && !props.isMobile && !props.isTablet ? '75%' : '100%')}; | |
`; | |
const CardsWrapper = styled(InfiniteScroll)<{ padding: number }>` | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
width: 100%; | |
padding: ${(props) => props.padding}px; | |
`; | |
const SortingContainer = styled(FlexCustom)` | |
min-width: 300px; | |
max-width: 1100px; | |
margin: 20px 20px 0px 20px; | |
`; | |
const BlankCard = styled(FlexCustom)<{ | |
padding: string; | |
borderBottom: string; | |
marginBottom: number; | |
isMobile: boolean; | |
}>` | |
justify-content: space-between; | |
align-items: center; | |
width: 100%; | |
min-width: 300px; | |
max-width: 1100px; | |
padding: ${(props) => props.padding}; | |
margin-bottom: ${(props) => props.marginBottom}px; | |
background-color: #ffffff; | |
border-radius: ${(props) => (props.isMobile ? 0 : 8)}px; | |
border-bottom: ${(props) => props.borderBottom}; | |
`; | |
const mapStateToProps = (state: RootState) => ({ | |
rdxIntlMsgs: SelectMessagesForLocale(state), | |
selectedDayJSCode: SelectCurrentlySelectedDayJSLocaleCode(state), | |
proposalsObject: AllProposalsObjectState(state), | |
accountName: WalletAccountName(state), | |
proposalsProperties: SelectedProposalProperties(state), | |
chatExpanded: ChatExpandedStatus(state), | |
selectedCountryCode: SelectCurrentlySelectedCountryCode(state), | |
countryToLocaleMap: SelectLanguagePacksKeyedByCountry(state), | |
isMobile: BrowserIsMobile(state), | |
isTablet: BrowserIsTablet(state), | |
isSmallScreen: BrowserIsSmallScreen(state), | |
browserInfo: GetBrowserInfo(state), | |
userProfiles: UserProfilesRoot(state), | |
}); | |
const mapDispatchToProps = (dispatch: Dispatch) => ({ | |
articleActions: bindActionCreators(omit(ArticleActions, 'Type'), dispatch), | |
activityActions: bindActionCreators(omit(ActivityActions, 'Type'), dispatch), | |
profilesActions: bindActionCreators(omit(ProfilesActions, 'Type'), dispatch), | |
}); | |
export interface ActivityCardsUnconnected { | |
timer: any; | |
} | |
type ActivityCardsProps = ActivityCardsOwnProps & | |
ReturnType<typeof mapStateToProps> & | |
ReturnType<typeof mapDispatchToProps>; | |
export class ActivityCardsUnconnected extends React.PureComponent<ActivityCardsProps, ActivityCardsState> { | |
fetchedUserStreaks = {}; | |
constructor(props: ActivityCardsProps) { | |
super(props); | |
this.timer; | |
this.state = { retrievedItems: false }; // Used on dashboard to prevent tabs from disappearing when navigate between tabs. | |
} | |
componentDidMount() { | |
if (this.props.dashboard) this.props.activityActions.setProposalProperty({ sortBy: 'my-activity' }); | |
if (!this.props.dashboard) { | |
this.props.activityActions.setProposalProperty({ sortBy: 'latest' }); | |
this.timer = setInterval(() => this.loadData(0), 10000); // Check for new edits every 10 seconds and add them into the activity feed. | |
} | |
setTimeout(() => this.loadData(0), 1000); | |
// Create object of streaks already pulled | |
Object.keys(this.props.userProfiles).map((profileName) => { | |
if (this.props.userProfiles[profileName].streaks) this.fetchedUserStreaks[profileName] = true; | |
}); | |
} | |
componentDidUpdate(prevProps: ActivityCardsProps) { | |
if ( | |
prevProps.proposalsProperties.sortBy !== this.props.proposalsProperties.sortBy || | |
prevProps.selectedCountryCode !== this.props.selectedCountryCode || | |
prevProps.profile !== this.props.profile // From profile page | |
) { | |
this.props.activityActions.clearProposals(); | |
this.loadData(0); | |
} | |
} | |
componentWillUnmount() { | |
this.props.activityActions.clearProposals(); | |
clearInterval(this.timer); | |
} | |
selectProposal = (proposal: number) => { | |
this.props.activityActions.selectProposal({ proposal_id: proposal }); | |
}; | |
articleNav = (tab) => { | |
this.props.articleActions.articleNav(tab); | |
}; | |
private loadData = async (offset: number) => { | |
const { proposalsProperties, selectedCountryCode, countryToLocaleMap, profile, dashboard } = this.props; | |
const limit: number = 12; | |
const language = countryToLocaleMap[selectedCountryCode].lang_code; | |
const accountNameToUse = | |
proposalsProperties.sortBy === 'my-activity' || proposalsProperties.sortBy === 'my-votes' ? profile : null; | |
// console.log(proposalsProperties.sortBy); | |
if (dashboard && !profile) return; // Shouldn't pull activity feed if on dashboard with nonexistant profile; | |
const retrievedActivity = await recentProposals( | |
limit, | |
offset, | |
proposalsProperties.sortBy, | |
[language], | |
null, | |
accountNameToUse, | |
this.props.browserInfo.name, | |
proposalsProperties.sortBy === 'my-votes', | |
); | |
if (retrievedActivity && retrievedActivity.length > 0) { | |
this.props.activityActions.fetchProposals(retrievedActivity); | |
this.setState({ retrievedItems: true }); | |
} | |
}; | |
public changeRecentPage = () => { | |
if (this.props.proposalsObject) this.loadData(Math.floor(Object.keys(this.props.proposalsObject).length / 9)); | |
}; | |
public renderActivityCards = (proposalsObject) => { | |
const { | |
accountName, | |
proposalsProperties, | |
articleActions, | |
isMobile, | |
chatExpanded, | |
isTablet, | |
isSmallScreen, | |
dashboard, | |
} = this.props; | |
if (!proposalsObject || !Object.keys(proposalsObject).reduce || Object.keys(proposalsObject).length < 1) { | |
const blankCardArray = []; | |
const length = 10; | |
for (let i = 0; i < length; i++) { | |
blankCardArray.push(''); | |
} | |
return ( | |
blankCardArray.map && | |
blankCardArray.map((nothing, index) => { | |
return ( | |
<BlankCard | |
padding={isMobile ? '20px 5px 10px 5px' : '20px 20px 10px 20px'} | |
borderBottom={isMobile ? `1px solid ${DarkGray}` : 'none'} | |
marginBottom={isMobile ? 0 : 15} | |
isMobile={isMobile} | |
key={`blank-card-${index}`} | |
> | |
<Skeleton | |
active | |
avatar={{ size: 'large', shape: 'square' }} | |
paragraph={{ rows: 2, width: isMobile ? ['40%', '75%'] : ['20%', '50%'] }} | |
title={false} | |
/> | |
</BlankCard> | |
); | |
}) | |
); | |
} | |
const sorted: ProposalModel[] = Object.keys(proposalsObject).reduce((result, currentValue) => { | |
const val: ProposalModel = proposalsObject[currentValue]; | |
const endtime: number = val.info.endtime * 1000; // Convert from unix to milisecond time to compare to new Date().valueOf(). | |
const now: number = new Date().valueOf(); | |
if (!val.hide) { | |
if ( | |
proposalsProperties.sortBy === 'latest' || | |
proposalsProperties.sortBy === 'my-activity' || | |
proposalsProperties.sortBy === 'my-votes' | |
) | |
result.unshift(val); // Sorts by most recent edit | |
if (proposalsProperties.sortBy === 'expiring' && endtime > now) result.push(val); // Sorts by most recent edit | |
} | |
return result; | |
}, []); | |
const streaksToFetch = []; | |
const toReturn = | |
sorted.map && | |
sorted.map((item, index) => { | |
if (!this.fetchedUserStreaks[item.info.proposer]) streaksToFetch.push(item.info.proposer); | |
this.fetchedUserStreaks[item.info.proposer] = true; | |
const slug = item && item.info && item.info.slug; | |
return ( | |
<ActivityCard | |
key={`activity-card-${index}-${slug}`} | |
activity={item} | |
selectProposal={this.selectProposal} | |
articleNav={this.articleNav} | |
accountName={accountName} | |
dayjsLang={this.props.selectedDayJSCode} | |
passedMessages={this.props.rdxIntlMsgs} | |
articleActions={articleActions} | |
isMobile={isMobile} | |
isTablet={isTablet} | |
isSmallScreen={isSmallScreen} | |
chatExpanded={chatExpanded} | |
browserInfo={this.props.browserInfo} | |
proposerProfile={this.props.userProfiles && this.props.userProfiles[item.info.proposer]} | |
/> | |
); | |
}); | |
if (streaksToFetch.length > 0) this.getUserStreaks(streaksToFetch); | |
return toReturn; | |
}; | |
getTabProperty = (index: number) => { | |
if (!this.props.dashboard) { | |
switch (index) { | |
case 0: | |
return 'latest'; | |
case 1: | |
return 'expiring'; | |
default: | |
return 'latest'; | |
} | |
} | |
if (this.props.dashboard) { | |
switch (index) { | |
case 0: | |
return 'my-activity'; | |
case 1: | |
return 'my-votes'; | |
default: | |
return 'my-activity'; | |
} | |
} | |
}; | |
getTabIndex = (tabString: string) => { | |
if (!this.props.dashboard) { | |
switch (tabString) { | |
case 'latest': | |
return 0; | |
case 'expiring': | |
return 1; | |
} | |
} | |
if (this.props.dashboard) { | |
switch (tabString) { | |
case 'my-activity': | |
return 0; | |
case 'my-votes': | |
return 1; | |
} | |
} | |
}; | |
getUserStreaks = async (accountNames: string[]) => { | |
const { profilesActions } = this.props; | |
const userStreaks = await fetchUserStreaks(accountNames); | |
profilesActions.getStreakInfo(userStreaks); | |
profilesActions.getProfilesFromStreaks(userStreaks); | |
profilesActions.getProfileActivity(userStreaks); | |
}; | |
renderSelectedTab = () => { | |
const { | |
proposalsObject, | |
activityActions, | |
proposalsProperties, | |
chatExpanded, | |
isMobile, | |
isTablet, | |
dashboard, | |
accountName, | |
profile, | |
} = this.props; | |
const { sortBy } = proposalsProperties; | |
const selectedIndex = this.getTabIndex(sortBy); | |
// switch (selectedIndex) { | |
// case 0: { | |
// console.log(proposalsObject); | |
return ( | |
<CardsWrapper | |
dataLength={proposalsObject ? Object.keys(proposalsObject).length : 10} | |
next={this.changeRecentPage} | |
hasMore | |
scrollThreshold={0.6} | |
padding={isMobile ? 15 : 20} | |
loader={ | |
<div className="loader" key={0}> | |
<FM defaultMessage="Loading" id="ActivityCards.Loading" /> ... | |
</div> | |
} | |
> | |
{this.renderActivityCards(proposalsObject)} | |
</CardsWrapper> | |
); | |
// break; | |
// } | |
// } | |
}; | |
render() { | |
const { | |
proposalsObject, | |
activityActions, | |
proposalsProperties, | |
chatExpanded, | |
isMobile, | |
isTablet, | |
dashboard, | |
accountName, | |
profile, | |
} = this.props; | |
const { retrievedItems } = this.state; | |
const { sortBy } = proposalsProperties; | |
const selectedIndex = this.getTabIndex(sortBy); | |
if (dashboard && !proposalsObject && !retrievedItems) return <span />; // Shouldn't have tabs or skeleton on dashboard if no items loaded | |
const tabOptions = !dashboard | |
? [this.props.rdxIntlMsgs['ActivityCards.MostRecent'], this.props.rdxIntlMsgs['ActivityCards.ExpiringSoon']] | |
: [ | |
profile === accountName | |
? this.props.rdxIntlMsgs['ActivityCards.MyEdits'] | |
: `${this.props.rdxIntlMsgs['ActivityCards.edits']} ${ | |
profile ? '(' + this.props.profile.toUpperCase() + ')' : '' | |
}`, | |
profile === accountName | |
? this.props.rdxIntlMsgs['ActivityCards.MyVotes'] | |
: `${this.props.rdxIntlMsgs['ActivityCards.Votes']} ${ | |
profile ? '(' + this.props.profile.toUpperCase() + ')' : '' | |
}`, | |
]; | |
// if (this.state.dayjsLoaded) { | |
return ( | |
<ActivityCardsContainer chatExpanded={chatExpanded} isMobile={isMobile} isTablet={isTablet} role="tabpanel"> | |
{!isMobile && ( | |
<SortingContainer> | |
<Tabs | |
selectedIndex={selectedIndex} | |
tabOptions={tabOptions} | |
selectTab={(index) => | |
activityActions.setProposalProperty({ sortBy: this.getTabProperty(index) }) | |
} | |
tabMinWidth={'0px'} | |
borderBottom | |
isMobile={isMobile} | |
/> | |
</SortingContainer> | |
)} | |
{isMobile && ( | |
<Tabs | |
selectedIndex={selectedIndex} | |
tabOptions={tabOptions} | |
selectTab={(index) => | |
activityActions.setProposalProperty({ sortBy: this.getTabProperty(index) }) | |
} | |
tabMinWidth={'50vw'} | |
borderBottom | |
marginTop={15} | |
isMobile={isMobile} | |
/> | |
)} | |
{this.renderSelectedTab()} | |
</ActivityCardsContainer> | |
); | |
} | |
} | |
export const ActivityCards = connect(mapStateToProps, mapDispatchToProps)(ActivityCardsUnconnected as any); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment