Handle bot actors

This commit is contained in:
Bruno Lemos
2018-01-14 16:53:51 -02:00
parent c35360e5f7
commit 1aef6be146
18 changed files with 210 additions and 53 deletions

View File

@@ -132,10 +132,14 @@ export default class EventCard extends PureComponent<IProps> {
return (
<View style={styles.container}>
<EventCardHeader
key={`event-card-header-${event.id}`}
actionText={actionText}
avatarURL={actor.avatar_url}
cardIconColor={cardIconColor}
cardIconName={cardIconName}
username={event.actor.login}
isBot={Boolean(actor.login && actor.login.indexOf('[bot]') >= 0)}
userLinkURL={actor.html_url || ''}
username={actor.display_login || actor.login}
/>
{repos.length > 0 && (
@@ -192,6 +196,7 @@ export default class EventCard extends PureComponent<IProps> {
{Boolean(pullRequest) && (
<IssueOrPullRequestRow
key={`pr-row-${pullRequest.id}`}
avatarURL={pullRequest.user.avatar_url}
iconColor={pullRequestIconColor!}
iconName={pullRequestIconName!}
isRead={isRead}
@@ -199,7 +204,8 @@ export default class EventCard extends PureComponent<IProps> {
theme={theme}
title={pullRequest.title}
url={pullRequestURL}
username={pullRequest.user.login}
userLinkURL={pullRequest.user.html_url || ''}
username={pullRequest.user.display_login || pullRequest.user.login}
/>
)}
@@ -215,6 +221,7 @@ export default class EventCard extends PureComponent<IProps> {
{Boolean(issue) && (
<IssueOrPullRequestRow
key={`issue-row-${issue.id}`}
avatarURL={issue.user.avatar_url}
iconColor={issueIconColor!}
iconName={issueIconName!}
isRead={isRead}
@@ -222,24 +229,22 @@ export default class EventCard extends PureComponent<IProps> {
theme={theme}
title={issue.title}
url={issueURL}
username={issue.user.login}
userLinkURL={issue.user.html_url || ''}
username={issue.user.display_login || issue.user.login}
/>
)}
{(type === 'IssuesEvent' &&
(payload as IIssuesEvent['payload']).action === 'opened' &&
Boolean((payload as IIssuesEvent['payload']).issue.body) && (
Boolean(issue.body) && (
<CommentRow
key={`issue-body-row-${
(payload as IIssuesEvent['payload']).issue.id
}`}
body={(payload as IIssuesEvent['payload']).issue.body}
key={`issue-body-row-${issue.id}`}
avatarURL={issue.user.avatar_url}
body={issue.body}
isRead={isRead}
url={
(payload as IIssuesEvent['payload']).issue.html_url ||
(payload as IIssuesEvent['payload']).issue.url
}
username={actor.login}
url={issue.html_url || issue.url}
userLinkURL={issue.user.html_url || ''}
username={issue.user.display_login || issue.user.login}
/>
)) ||
(type === 'PullRequestEvent' &&
@@ -247,25 +252,32 @@ export default class EventCard extends PureComponent<IProps> {
Boolean(pullRequest.body) && (
<CommentRow
key={`pr-body-row-${pullRequest.id}`}
avatarURL={pullRequest.user.avatar_url}
body={pullRequest.body}
isRead={isRead}
url={pullRequest.html_url || pullRequest.url}
username={actor.login}
userLinkURL={pullRequest.user.html_url || ''}
username={
pullRequest.user.display_login || pullRequest.user.login
}
/>
)) ||
(Boolean(comment && comment.body) && (
<CommentRow
key={`comment-row-${comment.id}`}
avatarURL={comment.user.avatar_url}
body={comment.body}
isRead={isRead}
url={comment.html_url || comment.url}
username={actor.login}
userLinkURL={comment.user.html_url || ''}
username={comment.user.display_login || comment.user.login}
/>
))}
{Boolean(release) && (
<ReleaseRow
key={`release-row-${release.id}`}
avatarURL={release.author.avatar_url}
body={release.body}
branch={release.target_commitish}
isRead={isRead}
@@ -275,6 +287,8 @@ export default class EventCard extends PureComponent<IProps> {
tagName={release.tag_name}
type={type}
url={release.html_url || release.url}
userLinkURL={release.author.html_url || ''}
username={release.author.display_login || release.author.login}
/>
)}
</View>

View File

@@ -101,6 +101,7 @@ export default class NotificationCard extends PureComponent<IProps> {
return (
<View style={styles.container}>
<NotificationCardHeader
key={`notification-card-header-${notification.id}`}
cardIconColor={cardIconColor}
cardIconName={cardIconName}
labelColor={labelColor}
@@ -131,6 +132,7 @@ export default class NotificationCard extends PureComponent<IProps> {
{!!issue && (
<IssueOrPullRequestRow
key={`issue-row-${issueOrPullRequestNumber}`}
avatarURL=""
iconColor={issueIconColor}
iconName={issueIconName}
isRead={isRead}
@@ -138,12 +140,15 @@ export default class NotificationCard extends PureComponent<IProps> {
theme={theme}
title={issue.title}
url={issue.latest_comment_url || issue.url}
userLinkURL=""
username=""
/>
)}
{!!pullRequest && (
<IssueOrPullRequestRow
key={`pr-row-${issueOrPullRequestNumber}`}
avatarURL=""
iconColor={pullRequestIconColor}
iconName={pullRequestIconName}
isRead={isRead}
@@ -151,12 +156,15 @@ export default class NotificationCard extends PureComponent<IProps> {
theme={theme}
title={pullRequest.title}
url={pullRequest.latest_comment_url || pullRequest.url}
userLinkURL=""
username=""
/>
)}
{!!release && (
<ReleaseRow
key={`release-row-${repo.id}`}
avatarURL=""
body={release.title}
isRead={isRead}
ownerName={repoOwnerName!}
@@ -165,6 +173,8 @@ export default class NotificationCard extends PureComponent<IProps> {
type="ReleaseEvent"
name={release.title}
tagName={release.title}
userLinkURL=""
username=""
/>
)}
@@ -172,8 +182,10 @@ export default class NotificationCard extends PureComponent<IProps> {
!!title && (
<CommentRow
key={`subject-row-${subject.url}`}
avatarURL=""
body={title}
isRead={isRead}
userLinkURL=""
username=""
/>
)}
@@ -183,7 +195,8 @@ export default class NotificationCard extends PureComponent<IProps> {
key={`comment-row-${comment.id}`}
body={comment.body}
url={comment.html_url}
username={comment.user.login}
userLinkURL={comment.user.html_url}
username={comment.user.display_login || comment.user.login}
/>
)} */}
</View>

View File

@@ -15,9 +15,12 @@ import { getUserPressHandler } from './rows/helpers'
export interface IProps {
actionText: string
avatarURL: string
cardIconColor: string
cardIconName: IGitHubIcon
isBot: boolean
username: string
userLinkURL: string
}
export interface IState {}
@@ -45,21 +48,42 @@ const styles = StyleSheet.create({
export default class EventCardHeader extends PureComponent<IProps> {
render() {
const { actionText, cardIconColor, cardIconName, username } = this.props
const {
actionText,
avatarURL,
cardIconColor,
cardIconName,
isBot,
username: _username,
userLinkURL,
} = this.props
const username = isBot ? _username!.replace('[bot]', '') : _username
return (
<View style={styles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={username} style={cardStyles.avatar} />
<Avatar
avatarURL={avatarURL}
isBot={isBot}
linkURL={userLinkURL}
username={username}
style={cardStyles.avatar}
/>
</View>
<View style={styles.rightColumnCentered}>
<View style={styles.outerContainer}>
<View style={styles.innerContainer}>
<View style={cardStyles.horizontal}>
<TouchableOpacity onPress={getUserPressHandler(username)}>
<TouchableOpacity
onPress={getUserPressHandler(username, { isBot })}
>
<Text style={cardStyles.usernameText}>{username}</Text>
</TouchableOpacity>
{!!isBot && (
<Text style={cardStyles.timestampText}>{` • BOT`}</Text>
)}
<Text style={cardStyles.timestampText}>
&nbsp;&nbsp;2h (13:59)
</Text>

View File

@@ -55,7 +55,15 @@ export default class EventCardHeader extends PureComponent<IProps> {
return (
<View style={styles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={repoOwnerName} small style={cardStyles.avatar} />
<Avatar
isBot={Boolean(
repoOwnerName && repoOwnerName.indexOf('[bot]') >= 0,
)}
linkURL=""
small
style={cardStyles.avatar}
username={repoOwnerName}
/>
</View>
<View style={styles.rightColumnCentered}>
@@ -65,8 +73,8 @@ export default class EventCardHeader extends PureComponent<IProps> {
<Label
color={labelColor}
isPrivate={isPrivate}
numberOfLines={1}
outline={isRead}
textProps={{ numberOfLines: 1 }}
>
{labelText}
</Label>

View File

@@ -35,7 +35,13 @@ const BranchRow: SFC<IProps> = ({
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
<Avatar
isBot={Boolean(ownerName && ownerName.indexOf('[bot]') >= 0)}
linkURL=""
small
style={cardStyles.avatar}
username={ownerName}
/>
</View>
<View style={cardStyles.rightColumn}>

View File

@@ -8,21 +8,25 @@ import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
avatarURL: string
body: string
isRead: boolean
numberOfLines?: number
username: string
url?: string
userLinkURL: string
username: string
}
export interface IState {}
const CommentRow: SFC<IProps> = ({
avatarURL,
body: _body,
isRead,
numberOfLines = 4,
url,
username,
userLinkURL,
}) => {
const body = trimNewLinesAndSpaces(_body, 400)
if (!body) return null
@@ -30,7 +34,14 @@ const CommentRow: SFC<IProps> = ({
return (
<View style={rowStyles.container}>
<View style={[cardStyles.leftColumn, cardStyles.leftColumnAlignTop]}>
<Avatar username={username} small style={cardStyles.avatar} />
<Avatar
avatarURL={avatarURL}
isBot={Boolean(username && username.indexOf('[bot]') >= 0)}
linkURL={userLinkURL}
small
style={cardStyles.avatar}
username={username}
/>
</View>
<View style={cardStyles.rightColumn}>

View File

@@ -49,6 +49,9 @@ const CommitRow: SFC<IProps> = ({
<View style={cardStyles.leftColumn}>
<Avatar
email={authorEmail}
isBot={Boolean(
authorUsername && authorUsername.indexOf('[bot]') >= 0,
)}
username={authorUsername}
small
style={cardStyles.avatar}

View File

@@ -19,6 +19,7 @@ import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
avatarURL: string
iconColor?: string
iconName: string
isRead: boolean
@@ -26,7 +27,8 @@ export interface IProps {
theme: ITheme
title: string
url: string
username?: string
username: string
userLinkURL: string
}
export interface IState {}
@@ -38,6 +40,7 @@ const styles = StyleSheet.create({
})
const IssueOrPullRequestRow: SFC<IProps> = ({
avatarURL,
iconColor,
iconName,
isRead,
@@ -46,6 +49,7 @@ const IssueOrPullRequestRow: SFC<IProps> = ({
title: _title,
url,
username,
userLinkURL,
}) => {
const title = trimNewLinesAndSpaces(_title)
if (!title) return null
@@ -56,7 +60,14 @@ const IssueOrPullRequestRow: SFC<IProps> = ({
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
{Boolean(username) && (
<Avatar username={username} small style={cardStyles.avatar} />
<Avatar
avatarURL={avatarURL}
isBot={Boolean(username && username.indexOf('[bot]') >= 0)}
linkURL={userLinkURL}
small
style={cardStyles.avatar}
username={username}
/>
)}
</View>

View File

@@ -13,8 +13,9 @@ import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
branch?: string
avatarURL: string
body: string
branch?: string
isRead: boolean
name: string
ownerName: string
@@ -22,27 +23,30 @@ export interface IProps {
tagName: string
type: IGitHubEventType
url: string
userLinkURL: string
username: string
}
export interface IState {}
const ReleaseRow: SFC<IProps> = ({
avatarURL,
body: _body,
branch,
isRead,
name: _name,
ownerName,
repositoryName,
tagName: _tagName,
type,
url,
username,
userLinkURL,
}) => {
const body = trimNewLinesAndSpaces(_body)
const name = trimNewLinesAndSpaces(_name)
const tagName = trimNewLinesAndSpaces(_tagName)
const { owner: ownerName, repo: repositoryName } = getOwnerAndRepo(
getRepoFullNameFromUrl(url),
)
return (
<View>
{!!(branch && ownerName && repositoryName) && (
@@ -58,7 +62,13 @@ const ReleaseRow: SFC<IProps> = ({
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
<Avatar
isBot={Boolean(ownerName && ownerName.indexOf('[bot]') >= 0)}
linkURL=""
small
style={cardStyles.avatar}
username={ownerName}
/>
</View>
<View style={cardStyles.rightColumn}>
@@ -83,7 +93,14 @@ const ReleaseRow: SFC<IProps> = ({
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
<Avatar
avatarURL={avatarURL}
isBot={Boolean(username && username.indexOf('[bot]') >= 0)}
linkURL={userLinkURL}
small
style={cardStyles.avatar}
username={username}
/>
</View>
<View style={cardStyles.rightColumn}>

View File

@@ -35,7 +35,13 @@ const RepositoryRow: SFC<IProps> = ({
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
<Avatar
isBot={Boolean(ownerName && ownerName.indexOf('[bot]') >= 0)}
linkURL=""
small
style={cardStyles.avatar}
username={ownerName}
/>
</View>
<View style={cardStyles.rightColumn}>

View File

@@ -20,7 +20,9 @@ export default class UserListRow extends React.PureComponent<IProps> {
<UserRow
key={`user-row-${user.id}`}
{...this.props}
username={user.login}
avatarURL={user.avatar_url}
userLinkURL={user.html_url || ''}
username={user.display_login || user.login}
/>
)
}

View File

@@ -7,16 +7,25 @@ import { getUserPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
avatarURL: string
isRead: boolean
username: string
userLinkURL: string
}
export interface IState {}
const UserRow: SFC<IProps> = ({ isRead, username }) => (
const UserRow: SFC<IProps> = ({ avatarURL, isRead, username, userLinkURL }) => (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={username} small style={cardStyles.avatar} />
<Avatar
avatarURL={avatarURL}
isBot={Boolean(username && username.indexOf('[bot]') >= 0)}
linkURL={userLinkURL}
small
style={cardStyles.avatar}
username={username}
/>
</View>
<View style={cardStyles.rightColumn}>

View File

@@ -39,6 +39,10 @@ export const getRepositoryPressHandler = R.memoize(
) as (owner?: string, repositoryName?: string) => () => void | undefined
export const getUserPressHandler = R.memoize(
(username?: string) =>
username ? getGithubURLPressHandler(`${baseURL}/${username}`) : undefined,
) as (username?: string) => () => void | undefined
(username: string, { isBot }: { isBot?: boolean } = {}) =>
username
? getGithubURLPressHandler(
`${baseURL}/${isBot ? 'apps/' : ''}${username}`,
)
: undefined,
) as (username: string, options?: { isBot?: boolean }) => () => void | undefined

View File

@@ -9,6 +9,7 @@ import {
import { avatarSize, radius, smallAvatarSize } from '../../styles/variables'
import {
getUserAvatarByAvatarURL,
getUserAvatarByEmail,
getUserAvatarByUsername,
} from '../../utils/helpers/github/shared'
@@ -20,7 +21,8 @@ import {
export interface IProps {
avatarURL?: string
email?: string
linkURL?: string
isBot: boolean
linkURL: string
size?: number
small?: boolean
style?: ImageStyle
@@ -36,8 +38,9 @@ const styles = StyleSheet.create({
})
const Avatar: SFC<IProps> = ({
avatarURL,
avatarURL: _avatarURL,
email,
isBot,
linkURL,
size: _size,
small,
@@ -46,18 +49,24 @@ const Avatar: SFC<IProps> = ({
...props
}) => {
const finalSize = _size || (small ? smallAvatarSize : avatarSize)
const avatarURL = _avatarURL
? getUserAvatarByAvatarURL(_avatarURL, { size: finalSize })
: ''
const uri =
(username && getUserAvatarByUsername(username, { size: finalSize })) ||
avatarURL ||
(username && getUserAvatarByUsername(username, { size: finalSize })) ||
(email && getUserAvatarByEmail(email, { size: finalSize }))
if (!uri) return null
return (
<TouchableOpacity
onPress={
username
? getUserPressHandler(username)
: linkURL ? getGithubURLPressHandler(linkURL) : undefined
linkURL
? getGithubURLPressHandler(linkURL)
: username ? getUserPressHandler(username, { isBot }) : undefined
}
>
<Image

View File

@@ -29,7 +29,13 @@ export default class AvatarNavBarButton extends PureComponent<
return (
<TouchableOpacity onPress={onPress} {...props}>
<Avatar username={username} size={size} style={style} />
<Avatar
isBot={Boolean(username && username.indexOf('[bot]') >= 0)}
linkURL=""
size={size}
style={style}
username={username}
/>
</TouchableOpacity>
)
}

View File

@@ -5,6 +5,7 @@ import {
TextProperties,
TextStyle,
View,
ViewProperties,
ViewStyle,
} from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
@@ -17,17 +18,18 @@ import {
import theme from '../../styles/themes/dark'
export interface IProps extends TextProperties {
export interface IProps {
borderColor?: string
children: ReactNode
color?: string
containerProps?: object
containerProps?: ViewProperties
containerStyle?: ViewStyle
isPrivate?: boolean
muted?: boolean
outline?: boolean
radius?: number
textColor?: string
textProps?: TextProperties
}
const styles = StyleSheet.create({
@@ -54,7 +56,7 @@ const Label: SFC<IProps> = ({
isPrivate,
radius = defaultRadius,
textColor,
...props
textProps = {},
}) => (
<View
style={[
@@ -81,7 +83,7 @@ const Label: SFC<IProps> = ({
},
muted && { opacity: mutedOpacity },
]}
{...props}
{...textProps}
>
{Boolean(isPrivate) && (
<Text>

View File

@@ -1,9 +1,11 @@
export interface IGitHubUser {
id: number
login: string
display_login?: string
display_login: string
gravatar_id: string
html_url?: string // https://github.com/brunolemos
url?: string // https://api.github.com/users/brunolemos
url: string // https://api.github.com/users/brunolemos
avatar_url: string // https://avatars.githubusercontent.com/u/2118189?
}
export interface IGitHubReaction {

View File

@@ -11,6 +11,16 @@ import {
} from '../../../types'
import { getSteppedSize } from '../shared'
export function getUserAvatarByAvatarURL(
avatarURL: string,
{ size }: { size?: number } = {},
) {
if (!avatarURL) return ''
const _avatarURL = avatarURL.indexOf('?') > 0 ? avatarURL : `${avatarURL}?`
return `${_avatarURL}&s=${getSteppedSize(size)}`
}
export function getUserAvatarByUsername(
username: string,
{ size }: { size?: number } = {},