Finished event cards components

This commit is contained in:
Bruno Lemos
2018-01-13 17:03:58 -02:00
parent d8e34b831c
commit 3e68a43f2d
45 changed files with 1677 additions and 186 deletions

View File

@@ -137,6 +137,7 @@ android {
}
dependencies {
compile project(':react-native-linear-gradient')
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules

View File

@@ -1,4 +1,6 @@
rootProject.name = 'devhub'
include ':react-native-linear-gradient'
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':react-native-navigation'

View File

@@ -13,6 +13,7 @@
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* devhubTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* devhubTests.m */; };
072BCFEF97034DD8A762C885 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D59EE38FE98B402B99250666 /* libBVLinearGradient.a */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
@@ -117,6 +118,20 @@
remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;
remoteInfo = "devhub-tvOS";
};
2E60B5072001851A0076D5AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 12697B233D884F2EB9A7D7F0 /* BVLinearGradient.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = BVLinearGradient;
};
2E60B5092001851A0076D5AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 12697B233D884F2EB9A7D7F0 /* BVLinearGradient.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 64AA15081EF7F30100718508;
remoteInfo = "BVLinearGradient-tvOS";
};
2E9757CD1FE54A870087D2A7 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D1C1CBC9A5564D089F2E18B2 /* ReactNativeNavigation.xcodeproj */;
@@ -346,6 +361,7 @@
00E356EE1AD99517003FC87E /* devhubTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = devhubTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* devhubTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = devhubTests.m; sourceTree = "<group>"; };
12697B233D884F2EB9A7D7F0 /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* devhub.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = devhub.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -376,6 +392,7 @@
B6BF65C496E44E78BF211275 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
C1F790D96FC04CEBA08381F0 /* SafariViewManager.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = SafariViewManager.xcodeproj; path = "../node_modules/react-native-safari-view/SafariViewManager.xcodeproj"; sourceTree = "<group>"; };
D1C1CBC9A5564D089F2E18B2 /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = "<group>"; };
D59EE38FE98B402B99250666 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = "<group>"; };
D8C408A943574FCE997797CD /* libReactNativeNavigation.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libReactNativeNavigation.a; sourceTree = "<group>"; };
F035D9E85FA74D8CBFBDAE1B /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
F1AA246742BC4C2A8C9988F3 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; };
@@ -413,6 +430,7 @@
C2619E3179B6481FA20E5CEB /* libRNGestureHandler.a in Frameworks */,
B71E99D6DCBF4278835E3D03 /* libReactNativeNavigation.a in Frameworks */,
DC69E7EC47C64276A45E93CF /* libSafariViewManager.a in Frameworks */,
072BCFEF97034DD8A762C885 /* libBVLinearGradient.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -555,6 +573,15 @@
name = Products;
sourceTree = "<group>";
};
2E60B5032001851A0076D5AF /* Products */ = {
isa = PBXGroup;
children = (
2E60B5082001851A0076D5AF /* libBVLinearGradient.a */,
2E60B50A2001851A0076D5AF /* libBVLinearGradient.a */,
);
name = Products;
sourceTree = "<group>";
};
2E9757CA1FE54A870087D2A7 /* Products */ = {
isa = PBXGroup;
children = (
@@ -578,6 +605,7 @@
B5DF78792708477FB2343101 /* libRNGestureHandler.a */,
D8C408A943574FCE997797CD /* libReactNativeNavigation.a */,
55B8B3E654F0420CA0ED33D3 /* libSafariViewManager.a */,
D59EE38FE98B402B99250666 /* libBVLinearGradient.a */,
);
name = "Recovered References";
sourceTree = "<group>";
@@ -635,6 +663,7 @@
1FD65CD116F747ACA2D47E1F /* RNGestureHandler.xcodeproj */,
D1C1CBC9A5564D089F2E18B2 /* ReactNativeNavigation.xcodeproj */,
C1F790D96FC04CEBA08381F0 /* SafariViewManager.xcodeproj */,
12697B233D884F2EB9A7D7F0 /* BVLinearGradient.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@@ -815,6 +844,10 @@
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 2E60B5032001851A0076D5AF /* Products */;
ProjectRef = 12697B233D884F2EB9A7D7F0 /* BVLinearGradient.xcodeproj */;
},
{
ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
@@ -947,6 +980,20 @@
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2E60B5082001851A0076D5AF /* libBVLinearGradient.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libBVLinearGradient.a;
remoteRef = 2E60B5072001851A0076D5AF /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2E60B50A2001851A0076D5AF /* libBVLinearGradient.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libBVLinearGradient.a;
remoteRef = 2E60B5092001851A0076D5AF /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
2E9757CE1FE54A870087D2A7 /* libReactNativeNavigation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -1311,6 +1358,7 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
"$(SRCROOT)/../node_modules/react-native-safari-view",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
);
INFOPLIST_FILE = devhubTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1319,6 +1367,7 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1340,6 +1389,7 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
"$(SRCROOT)/../node_modules/react-native-safari-view",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
);
INFOPLIST_FILE = devhubTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -1348,6 +1398,7 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1371,6 +1422,7 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
"$(SRCROOT)/../node_modules/react-native-safari-view",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
);
INFOPLIST_FILE = devhub/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1396,6 +1448,7 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
"$(SRCROOT)/../node_modules/react-native-safari-view",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
);
INFOPLIST_FILE = devhub/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1427,6 +1480,7 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
"$(SRCROOT)/../node_modules/react-native-safari-view",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
);
INFOPLIST_FILE = "devhub-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1434,6 +1488,7 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1465,6 +1520,7 @@
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
"$(SRCROOT)/../node_modules/react-native-safari-view",
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
);
INFOPLIST_FILE = "devhub-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1472,6 +1528,7 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
OTHER_LDFLAGS = (
"-ObjC",
@@ -1502,6 +1559,7 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.devhub-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1528,6 +1586,7 @@
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.devhub-tvOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -17,6 +17,7 @@
"react": "^16.2.0",
"react-native": "^0.51.0",
"react-native-gesture-handler": "^1.0.0-alpha.37",
"react-native-linear-gradient": "^2.4.0",
"react-native-navigation": "^1.1.334",
"react-native-safari-view": "^2.1.0",
"react-native-vector-icons": "^4.4.3",
@@ -50,7 +51,9 @@
},
"jest": {
"preset": "react-native",
"setupFiles": ["./jest/setup.js"],
"setupFiles": [
"./jest/setup.js"
],
"globals": {
"ts-jest": {
"useBabelrc": true

View File

@@ -3,14 +3,41 @@ import { StyleSheet, View, ViewStyle } from 'react-native'
import theme from '../../styles/themes/dark'
import { contentPadding } from '../../styles/variables'
import { IGitHubEvent } from '../../types/index'
import {
IForkEvent,
IGitHubCommit,
IGitHubCommitCommentEvent,
IGitHubEvent,
IGitHubPage,
IGitHubRepo,
IGitHubUser,
IGollumEvent,
IIssuesEvent,
IMemberEvent,
IPullRequestEvent,
IPushEvent,
IReleaseEvent,
} from '../../types/index'
import {
getEventIconAndColor,
getEventText,
} from '../../utils/helpers/github/events'
import { getOwnerAndRepo } from '../../utils/helpers/github/shared'
import {
getIssueIconAndColor,
getOwnerAndRepo,
getPullRequestIconAndColor,
} from '../../utils/helpers/github/shared'
import { getRepoFullNameFromObject } from '../../utils/helpers/github/url'
import EventCardHeader from './partials/EventCardHeader'
import BranchRow from './partials/rows/BranchRow'
import CommentRow from './partials/rows/CommentRow'
import CommitListRow from './partials/rows/CommitListRow'
import IssueOrPullRequestRow from './partials/rows/IssueOrPullRequestRow'
import ReleaseRow from './partials/rows/ReleaseRow'
import RepositoryListRow from './partials/rows/RepositoryListRow'
import RepositoryRow from './partials/rows/RepositoryRow'
import UserListRow from './partials/rows/UserListRow'
import WikiPageListRow from './partials/rows/WikiPageListRow'
export interface IProps {
event: IGitHubEvent
@@ -29,9 +56,44 @@ const styles = StyleSheet.create({
export default class EventCard extends PureComponent<IProps> {
render() {
const { event } = this.props
const { actor, payload, repo: _repo, type } = event
const repoFullName = event.repo.full_name || event.repo.name || ''
const { owner: orgName, repo: repoName } = getOwnerAndRepo(repoFullName)
const { comment } = payload as IGitHubCommitCommentEvent['payload']
const { commits: _commits } = payload as IPushEvent['payload']
const { forkee } = payload as IForkEvent['payload']
const { member: _member } = payload as IMemberEvent['payload']
const { release } = payload as IReleaseEvent['payload']
const { pages: _pages } = payload as IGollumEvent['payload']
const {
pull_request: pullRequest,
} = payload as IPullRequestEvent['payload']
const { issue } = payload as IIssuesEvent['payload']
const { ref: branchName } = payload as IPushEvent['payload']
const isRead = false // TODO
const commits: IGitHubCommit[] = (_commits || []).filter(Boolean)
const repos: IGitHubRepo[] = [_repo].filter(Boolean) // TODO
const users: IGitHubUser[] = [_member].filter(Boolean) // TODO
const pages: IGitHubPage[] = (_pages || []).filter(Boolean)
const repo = repos.length === 1 ? repos[0] : undefined
const commitIds = commits
.filter(Boolean)
.map((item: IGitHubCommit) => item.sha)
const pageIds = pages.filter(Boolean).map((item: IGitHubPage) => item.sha)
const repoIds = repos.filter(Boolean).map((item: IGitHubRepo) => item.id)
const userIds = users.filter(Boolean).map((item: IGitHubUser) => item.id)
const repoFullName = repo && getRepoFullNameFromObject(repo)
const { owner: repoOwnerName, repo: repoName } = getOwnerAndRepo(
repoFullName || '',
)
const forkRepoFullName = getRepoFullNameFromObject(forkee)
const { owner: forkRepoOwnerName, repo: forkRepoName } = getOwnerAndRepo(
forkRepoFullName,
)
const cardIconDetails = getEventIconAndColor(event, theme)
const cardIconName = cardIconDetails.subIcon || cardIconDetails.icon
@@ -39,6 +101,32 @@ export default class EventCard extends PureComponent<IProps> {
const actionText = getEventText(event)
const isPush = type === 'PushEvent'
const isForcePush = isPush && (payload as IPushEvent).forced
const {
icon: pullRequestIconName,
color: pullRequestIconColor,
} = pullRequest
? getPullRequestIconAndColor(pullRequest)
: { icon: undefined, color: undefined }
const pullRequestURL =
pullRequest &&
(comment && !comment.body && comment.html_url
? comment.html_url || comment.url
: pullRequest.html_url || pullRequest.url)
const { icon: issueIconName, color: issueIconColor } = issue
? getIssueIconAndColor(issue)
: { icon: undefined, color: undefined }
const issueURL =
issue &&
(comment && !comment.body && (comment.html_url || comment.url)
? comment.html_url || comment.url
: issue.html_url || issue.url)
return (
<View style={styles.container}>
<EventCardHeader
@@ -47,10 +135,147 @@ export default class EventCard extends PureComponent<IProps> {
cardIconName={cardIconName}
username={event.actor.login}
/>
{Boolean(event.repo && orgName && repoName) && (
{repos.length > 0 && (
<RepositoryListRow
key={`repo-list-row-${repoIds.join('-')}`}
isForcePush={isForcePush}
isPush={isPush}
isRead={isRead}
repos={repos}
theme={theme}
/>
)}
{Boolean(repo && repoOwnerName && repoName && branchName) && (
<BranchRow
key={`branch-row-${branchName}`}
branch={branchName}
isRead={isRead}
ownerName={repoOwnerName!}
repositoryName={repoName!}
type={type}
/>
)}
{Boolean(forkee && forkRepoOwnerName && forkRepoName) && (
<RepositoryRow
owner={orgName as string}
repository={repoName as string}
key={`fork-row-${forkee.id}`}
isForcePush={isForcePush}
isFork
isRead={isRead}
ownerName={forkRepoOwnerName!}
repositoryName={forkRepoName!}
/>
)}
{users.length > 0 && (
<UserListRow
key={`user-list-row-${userIds.join('-')}`}
isRead={isRead}
users={users}
theme={theme}
/>
)}
{pages.length > 0 && (
<WikiPageListRow
key={`wiki-page-list-row-${pageIds.join('-')}`}
isRead={isRead}
pages={pages}
theme={theme}
/>
)}
{Boolean(pullRequest) && (
<IssueOrPullRequestRow
key={`pr-row-${pullRequest.id}`}
iconColor={pullRequestIconColor!}
iconName={pullRequestIconName!}
isRead={isRead}
issueNumber={pullRequest.number}
theme={theme}
title={pullRequest.title}
url={pullRequestURL}
username={pullRequest.user.login}
/>
)}
{commits.length > 0 && (
<CommitListRow
key={`commit-list-row-${commitIds.join('-')}`}
commits={commits}
isRead={isRead}
theme={theme}
/>
)}
{Boolean(issue) && (
<IssueOrPullRequestRow
key={`issue-row-${issue.id}`}
iconColor={issueIconColor!}
iconName={issueIconName!}
isRead={isRead}
issueNumber={issue.number}
theme={theme}
title={issue.title}
url={issueURL}
username={issue.user.login}
/>
)}
{(type === 'IssuesEvent' &&
(payload as IIssuesEvent['payload']).action === 'opened' &&
Boolean((payload as IIssuesEvent['payload']).issue.body) && (
<CommentRow
key={`issue-body-row-${
(payload as IIssuesEvent['payload']).issue.id
}`}
body={(payload as IIssuesEvent['payload']).issue.body}
isRead={isRead}
type={type}
url={
(payload as IIssuesEvent['payload']).issue.html_url ||
(payload as IIssuesEvent['payload']).issue.url
}
username={actor.login}
/>
)) ||
(type === 'PullRequestEvent' &&
(payload as IPullRequestEvent['payload']).action === 'opened' &&
Boolean(pullRequest.body) && (
<CommentRow
key={`pr-body-row-${pullRequest.id}`}
body={pullRequest.body}
isRead={isRead}
url={pullRequest.html_url || pullRequest.url}
username={actor.login}
type={type}
/>
)) ||
(Boolean(comment && comment.body) && (
<CommentRow
key={`comment-row-${comment.id}`}
body={comment.body}
isRead={isRead}
type={type}
url={comment.html_url || comment.url}
username={actor.login}
/>
))}
{Boolean(release) && (
<ReleaseRow
key={`release-row-${release.id}`}
body={release.body}
branch={release.target_commitish}
isRead={isRead}
name={release.name}
ownerName={repoOwnerName!}
repositoryName={repoName!}
tagName={release.tag_name}
type={type}
url={release.html_url || release.url}
/>
)}
</View>

View File

@@ -1,7 +1,10 @@
import React from 'react'
import { FlatList } from 'react-native'
import theme from '../../styles/themes/dark'
import { contentPadding } from '../../styles/variables'
import { IGitHubEvent } from '../../types'
import TransparentTextOverlay from '../common/TransparentTextOverlay'
import CardItemSeparator from './partials/CardItemSeparator'
import SwipeableEventCard from './SwipeableEventCard'
@@ -11,7 +14,7 @@ export interface IProps {
class EventCards extends React.PureComponent<IProps> {
keyExtractor(event: IGitHubEvent) {
return event.id
return `${event.id}`
}
renderItem({ item: event }: { item: IGitHubEvent }) {
@@ -22,12 +25,18 @@ class EventCards extends React.PureComponent<IProps> {
const { events } = this.props
return (
<FlatList
data={events}
ItemSeparatorComponent={CardItemSeparator}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
<TransparentTextOverlay
color={theme.base02}
size={contentPadding}
from="vertical"
>
<FlatList
data={events}
ItemSeparatorComponent={CardItemSeparator}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
</TransparentTextOverlay>
)
}
}

View File

@@ -32,7 +32,9 @@ export default class NotificationCard extends PureComponent<IProps> {
const repoFullName =
notification.repository.full_name || notification.repository.name || ''
const { owner: orgName, repo: repoName } = getOwnerAndRepo(repoFullName)
const { owner: repoOwnerName, repo: repoName } = getOwnerAndRepo(
repoFullName,
)
const cardIconDetails = getNotificationIconAndColor(
notification,
@@ -54,10 +56,10 @@ export default class NotificationCard extends PureComponent<IProps> {
labelColor={labelColor}
labelText={labelText}
/>
{Boolean(orgName && repoName) && (
{Boolean(repoOwnerName && repoName) && (
<RepositoryRow
owner={orgName as string}
repository={repoName as string}
ownerName={repoOwnerName as string}
repositoryName={repoName as string}
/>
)}
</View>

View File

@@ -1,7 +1,10 @@
import React from 'react'
import { FlatList } from 'react-native'
import theme from '../../styles/themes/dark'
import { contentPadding } from '../../styles/variables'
import { IGitHubNotification } from '../../types'
import TransparentTextOverlay from '../common/TransparentTextOverlay'
import NotificationCard from './NotificationCard'
import CardItemSeparator from './partials/CardItemSeparator'
@@ -11,7 +14,7 @@ export interface IProps {
class NotificationCards extends React.PureComponent<IProps> {
keyExtractor(notification: IGitHubNotification) {
return notification.id
return `${notification.id}`
}
renderItem({ item: notification }: { item: IGitHubNotification }) {
@@ -26,12 +29,19 @@ class NotificationCards extends React.PureComponent<IProps> {
render() {
const { notifications } = this.props
return (
<FlatList
data={notifications}
ItemSeparatorComponent={CardItemSeparator}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
<TransparentTextOverlay
color={theme.base02}
size={contentPadding}
from="vertical"
>
<FlatList
key="notification-cards-flat-list"
data={notifications}
ItemSeparatorComponent={CardItemSeparator}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
</TransparentTextOverlay>
)
}
}

View File

@@ -21,8 +21,8 @@ export default class SwipeableEventCard extends PureComponent<IProps> {
leftActions={[
{
color: theme.blue,
icon: 'read',
key: 'read',
icon: 'isRead',
key: 'isRead',
label: 'Read',
onPress: this.onMarkAsRead,
type: 'FULL',

View File

@@ -0,0 +1,83 @@
import React, { SFC } from 'react'
import {
StyleSheet,
Text,
TextStyle,
TouchableOpacity,
ViewStyle,
} from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import { radius } from '../../../styles/variables'
import { IGitHubIcon, ITheme } from '../../../types'
import cardStyles from '../styles'
import { getGithubURLPressHandler } from './rows/helpers'
export interface IProps {
icon?: IGitHubIcon
id: number | string
isRead?: boolean
style?: ViewStyle
theme: ITheme
url: string
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
borderRadius: radius,
borderWidth: StyleSheet.hairlineWidth,
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 4,
} as ViewStyle,
text: {
backgroundColor: 'transparent',
fontSize: 12,
fontWeight: 'bold',
lineHeight: 20,
opacity: 0.9,
} as TextStyle,
})
export const CardItemId: SFC<IProps> = ({
icon,
id,
isRead,
style,
theme,
url,
}) => {
if (!id && !icon) return null
const parsedNumber = parseInt(`${id}`, 10) || id
return (
<TouchableOpacity
onPress={url ? getGithubURLPressHandler(url) : undefined}
style={[
styles.container,
{
backgroundColor: theme.base03,
borderColor: theme.base03,
},
style,
]}
>
<Text
style={[
styles.text,
{ color: isRead ? theme.base05 : theme.base04 },
isRead && cardStyles.mutedText,
isRead && { fontWeight: 'normal' },
]}
>
{icon ? <Icon name={icon} /> : ''}
{parsedNumber && icon ? ' ' : ''}
{typeof parsedNumber === 'number' ? '#' : ''}
{parsedNumber}
</Text>
</TouchableOpacity>
)
}

View File

@@ -0,0 +1,66 @@
import React, { SFC } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import { IGitHubEventType } from '../../../../types'
import Avatar from '../../../common/Avatar'
import cardStyles from '../../styles'
import { getBranchPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
branch: string
isRead?: boolean
ownerName: string
repositoryName: string
type: IGitHubEventType
}
export interface IState {}
const BranchRow: SFC<IProps> = ({
branch: _branch,
isRead,
ownerName,
repositoryName,
type,
}) => {
const branch = (_branch || '').replace('refs/heads/', '')
if (!branch) return null
const isBranchMainEventAction =
type === 'CreateEvent' || type === 'DeleteEvent'
if (branch === 'master' && !isBranchMainEventAction) return null
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getBranchPressHandler(ownerName, repositoryName, branch)}
style={rowStyles.mainContentContainer}
>
<Text
style={[
cardStyles.normalText,
(isRead || !isBranchMainEventAction) && cardStyles.mutedText,
]}
>
<Text
numberOfLines={1}
style={isRead ? cardStyles.mutedText : cardStyles.normalText}
>
<Icon name="git-branch" />{' '}
</Text>
{branch}
</Text>
</TouchableOpacity>
</View>
</View>
)
}
export default BranchRow

View File

@@ -0,0 +1,55 @@
import React, { SFC } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import { IGitHubEventType } from '../../../../types'
import { trimNewLinesAndSpaces } from '../../../../utils/helpers/shared'
import Avatar from '../../../common/Avatar'
import cardStyles from '../../styles'
import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
body: string
isRead?: boolean
numberOfLines?: number
username: string
type: IGitHubEventType
url?: string
}
export interface IState {}
const CommentRow: SFC<IProps> = ({
body: _body,
isRead,
numberOfLines = 4,
url,
username,
}) => {
const body = trimNewLinesAndSpaces(_body, 400)
if (!body) return null
return (
<View style={rowStyles.container}>
<View style={[cardStyles.leftColumn, cardStyles.leftColumnAlignTop]}>
<Avatar username={username} small style={cardStyles.avatar} />
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getGithubURLPressHandler(url)}
style={rowStyles.mainContentContainer}
>
<Text
numberOfLines={numberOfLines}
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
{body}
</Text>
</TouchableOpacity>
</View>
</View>
)
}
export default CommentRow

View File

@@ -0,0 +1,38 @@
import React from 'react'
import CommitRow from './CommitRow'
import RowList from './RowList'
import { IGitHubCommit, ITheme } from '../../../../types'
export interface IProps {
isRead?: boolean
maxHeight?: number
commits: IGitHubCommit[]
theme: ITheme
}
export default class CommitListRow extends React.PureComponent<IProps> {
renderItem({ item: commit }: { item: IGitHubCommit }) {
if (!(commit && commit.sha && commit.message)) return null
return (
<CommitRow
key={`commit-row-${commit.sha}`}
{...this.props}
authorEmail={commit.author.email}
authorName={commit.author.name}
message={commit.message}
url={commit.url}
/>
)
}
render() {
const { commits, ...props } = this.props
if (!(commits && commits.length > 0)) return null
return <RowList {...props} data={commits} renderItem={this.renderItem} />
}
}

View File

@@ -0,0 +1,91 @@
import React, { SFC } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import { tryGetUsernameFromGitHubEmail } from '../../../../utils/helpers/github/shared'
import {
getGitHubSearchURL,
getGitHubURLForUser,
} from '../../../../utils/helpers/github/url'
import { trimNewLinesAndSpaces } from '../../../../utils/helpers/shared'
import Avatar from '../../../common/Avatar'
import cardStyles from '../../styles'
import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
authorEmail?: string
authorName?: string
authorUsername?: string
isRead?: boolean
message: string
url: string
}
export interface IState {}
const CommitRow: SFC<IProps> = ({
authorEmail,
authorName,
authorUsername: _authorUsername,
isRead,
message: _message,
url,
}) => {
const message = trimNewLinesAndSpaces(_message)
if (!message) return null
const authorUsername =
_authorUsername || tryGetUsernameFromGitHubEmail(authorEmail)
let byText = authorName
if (authorUsername) byText += ` @${authorUsername}`
else if (authorEmail)
byText += byText ? ` <${authorEmail}>` : ` ${authorEmail}`
byText = trimNewLinesAndSpaces(byText)
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar
email={authorEmail}
username={authorUsername}
small
style={cardStyles.avatar}
linkURL={
authorUsername
? getGitHubURLForUser(authorUsername)
: getGitHubSearchURL({ q: authorEmail || '', type: 'Users' })
}
/>
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getGithubURLPressHandler(url)}
style={rowStyles.mainContentContainer}
>
<Text
numberOfLines={1}
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
<Icon name="git-commit" /> {message}
{Boolean(byText) && (
<Text
style={[
cardStyles.normalText,
cardStyles.smallText,
cardStyles.mutedText,
]}
>
{` by ${byText}`}
</Text>
)}
</Text>
</TouchableOpacity>
</View>
</View>
)
}
export default CommitRow

View File

@@ -0,0 +1,99 @@
import React, { SFC } from 'react'
import {
StyleSheet,
Text,
TouchableOpacity,
View,
ViewStyle,
} from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import defaultStyles from '../../../../styles/styles'
import { contentPadding } from '../../../../styles/variables'
import { ITheme } from '../../../../types'
import { trimNewLinesAndSpaces } from '../../../../utils/helpers/shared'
import Avatar from '../../../common/Avatar'
import cardStyles from '../../styles'
import { CardItemId } from '../CardItemId'
import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
iconColor?: string
iconName: string
isRead?: boolean
issueNumber: number
theme: ITheme
title: string
url: string
username: string
}
export interface IState {}
const styles = StyleSheet.create({
cardItemId: {
marginLeft: contentPadding,
} as ViewStyle,
})
const IssueOrPullRequestRow: SFC<IProps> = ({
iconColor,
iconName,
isRead,
issueNumber,
theme,
title: _title,
url,
username,
}) => {
const title = trimNewLinesAndSpaces(_title)
if (!title) return null
const byText = username ? `@${username}` : ''
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={username} small style={cardStyles.avatar} />
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getGithubURLPressHandler(url)}
style={rowStyles.mainContentContainer}
>
<Text numberOfLines={1} style={defaultStyles.full}>
<Text
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
<Icon color={iconColor} name={iconName} /> {title}
{Boolean(byText) && (
<Text
style={[
cardStyles.normalText,
cardStyles.smallText,
cardStyles.mutedText,
]}
>
{' '}
by {byText}
</Text>
)}
</Text>
</Text>
</TouchableOpacity>
<CardItemId
id={issueNumber}
isRead={isRead}
style={styles.cardItemId}
theme={theme}
url={url}
/>
</View>
</View>
)
}
export default IssueOrPullRequestRow

View File

@@ -0,0 +1,112 @@
import React, { SFC } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import { IGitHubEventType } from '../../../../types'
import { getOwnerAndRepo } from '../../../../utils/helpers/github/shared'
import { getRepoFullNameFromUrl } from '../../../../utils/helpers/github/url'
import { trimNewLinesAndSpaces } from '../../../../utils/helpers/shared'
import Avatar from '../../../common/Avatar'
import cardStyles from '../../styles'
import BranchRow from './BranchRow'
import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
branch: string
body: string
isRead?: boolean
name: string
ownerName: string
repositoryName: string
tagName: string
type: IGitHubEventType
url: string
}
export interface IState {}
const ReleaseRow: SFC<IProps> = ({
body: _body,
branch,
isRead,
name: _name,
tagName: _tagName,
type,
url,
}) => {
const body = trimNewLinesAndSpaces(_body)
const name = trimNewLinesAndSpaces(_name)
const tagName = trimNewLinesAndSpaces(_tagName)
const { owner: ownerName, repo: repositoryName } = getOwnerAndRepo(
getRepoFullNameFromUrl(url),
)
return (
<View>
{Boolean(branch && ownerName && repositoryName) && (
<BranchRow
key={`branch-row-${branch}`}
branch={branch}
ownerName={ownerName!}
repositoryName={repositoryName!}
type={type}
isRead={isRead}
/>
)}
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getGithubURLPressHandler(url)}
style={rowStyles.mainContentContainer}
>
<Text
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
<Text
numberOfLines={1}
style={isRead ? cardStyles.mutedText : cardStyles.normalText}
>
<Icon name="tag" />{' '}
</Text>
{name || tagName}
</Text>
</TouchableOpacity>
</View>
</View>
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getGithubURLPressHandler(url)}
style={rowStyles.mainContentContainer}
>
<Text
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
<Text
numberOfLines={1}
style={isRead ? cardStyles.mutedText : cardStyles.normalText}
>
<Icon name="megaphone" />{' '}
</Text>
{body}
</Text>
</TouchableOpacity>
</View>
</View>
</View>
)
}
export default ReleaseRow

View File

@@ -0,0 +1,48 @@
import React from 'react'
import RepositoryRow from './RepositoryRow'
import RowList from './RowList'
import { IGitHubRepo, ITheme } from '../../../../types'
import { getOwnerAndRepo } from '../../../../utils/helpers/github/shared'
import { getRepoFullNameFromObject } from '../../../../utils/helpers/github/url'
export interface IProps {
isForcePush?: boolean
isFork?: boolean
isPush?: boolean
isRead?: boolean
maxHeight?: number
repos: IGitHubRepo[]
theme: ITheme
}
export default class RepositoryListRow extends React.PureComponent<IProps> {
renderItem({ item: repo }: { item: IGitHubRepo }) {
if (!(repo && repo.id)) return null
const repoFullName = getRepoFullNameFromObject(repo)
const { owner: repoOwnerName, repo: repoName } = getOwnerAndRepo(
repoFullName,
)
if (!(repoOwnerName && repoName)) return null
return (
<RepositoryRow
key={`repo-row-${repo.id}`}
{...this.props}
ownerName={repoOwnerName!}
repositoryName={repoName!}
/>
)
}
render() {
const { repos, ...props } = this.props
if (!(repos && repos.length > 0)) return null
return <RowList {...props} data={repos} renderItem={this.renderItem} />
}
}

View File

@@ -1,5 +1,6 @@
import React, { SFC } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import Avatar from '../../../common/Avatar'
import cardStyles from '../../styles'
@@ -7,28 +8,60 @@ import { getRepositoryPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
repository: string
owner: string
isForcePush?: boolean
isFork?: boolean
isPush?: boolean
isRead?: boolean
ownerName: string
repositoryName: string
}
export interface IState {}
const RepositoryRow: SFC<IProps> = ({ owner, repository }) => (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={owner} small style={cardStyles.avatar} />
</View>
const RepositoryRow: SFC<IProps> = ({
isForcePush,
isFork,
isPush,
isRead,
ownerName,
repositoryName,
}) => {
const repoIcon =
(isForcePush && 'repo-force-push') ||
(isPush && 'repo-push') ||
(isFork && 'repo-forked') ||
'repo'
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getRepositoryPressHandler(owner, repository)}
style={rowStyles.mainContentContainer}
>
<Text style={rowStyles.repositoryText}>{repository}</Text>
<Text style={rowStyles.repositorySecondaryText}>&nbsp;{owner}</Text>
</TouchableOpacity>
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={ownerName} small style={cardStyles.avatar} />
</View>
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getRepositoryPressHandler(ownerName, repositoryName)}
style={rowStyles.mainContentContainer}
>
<Text
numberOfLines={1}
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
<Icon name={repoIcon} />{' '}
<Text style={rowStyles.repositoryText}>{repositoryName}</Text>
<Text
style={[
rowStyles.repositorySecondaryText,
isRead && cardStyles.mutedText,
]}
>
{` ${ownerName}`}
</Text>
</Text>
</TouchableOpacity>
</View>
</View>
</View>
)
)
}
export default RepositoryRow

View File

@@ -0,0 +1,49 @@
import React, { ReactNode, SFC } from 'react'
import { ScrollView } from 'react-native'
import { contentPadding } from '../../../../styles/variables'
import { ITheme } from '../../../../types'
import TransparentTextOverlay from '../../../common/TransparentTextOverlay'
export interface IProps {
data: any[]
maxHeight?: number
narrow?: boolean
renderItem: ({ item, index }: { item: any; index: number }) => ReactNode
theme: ITheme
}
const RowList: SFC<IProps> = ({
data,
maxHeight = 200,
narrow,
renderItem,
theme,
...props
}) => {
if (!(data && data.length > 0)) return null
if (data.length === 1) return renderItem({ item: data[0], index: 0 })
return (
<TransparentTextOverlay
{...props}
color={theme.base02}
size={narrow ? contentPadding / 2 : contentPadding}
from="vertical"
containerStyle={{ flex: 0 }}
>
<ScrollView
style={{ maxHeight }}
contentContainerStyle={{
paddingBottom: narrow ? contentPadding / 2 : contentPadding,
}}
alwaysBounceVertical={false}
>
{data.map((item, index) => renderItem({ item, index }))}
</ScrollView>
</TransparentTextOverlay>
)
}
export default RowList

View File

@@ -0,0 +1,35 @@
import React from 'react'
import RowList from './RowList'
import UserRow from './UserRow'
import { IGitHubUser, ITheme } from '../../../../types'
export interface IProps {
isRead?: boolean
maxHeight?: number
users: IGitHubUser[]
theme: ITheme
}
export default class UserListRow extends React.PureComponent<IProps> {
renderItem = ({ item: user }: { item: IGitHubUser }) => {
if (!(user && user.id && user.login)) return null
return (
<UserRow
key={`user-row-${user.id}`}
{...this.props}
username={user.login}
/>
)
}
render() {
const { users, ...props } = this.props
if (!(users && users.length > 0)) return null
return <RowList {...props} data={users} renderItem={this.renderItem} />
}
}

View File

@@ -7,12 +7,13 @@ import { getUserPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
isRead?: boolean
username: string
}
export interface IState {}
const UserRow: SFC<IProps> = ({ username }) => (
const UserRow: SFC<IProps> = ({ isRead, username }) => (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn}>
<Avatar username={username} small style={cardStyles.avatar} />
@@ -23,7 +24,9 @@ const UserRow: SFC<IProps> = ({ username }) => (
onPress={getUserPressHandler(username)}
style={rowStyles.mainContentContainer}
>
<Text style={rowStyles.usernameText}>{username}</Text>
<Text style={[rowStyles.usernameText, isRead && cardStyles.mutedText]}>
{username}
</Text>
</TouchableOpacity>
</View>
</View>

View File

@@ -0,0 +1,36 @@
import React from 'react'
import RowList from './RowList'
import WikiPageRow from './WikiPageRow'
import { IGitHubPage, ITheme } from '../../../../types'
export interface IProps {
isRead?: boolean
maxHeight?: number
pages: IGitHubPage[]
theme: ITheme
}
export default class WikiPageListRow extends React.PureComponent<IProps> {
renderItem = ({ item: page }: { item: IGitHubPage }) => {
if (!(page && page.sha && page.title)) return null
return (
<WikiPageRow
key={`page-row-${page.sha}`}
{...this.props}
title={page.title}
url={page.html_url || page.url}
/>
)
}
render() {
const { pages, ...props } = this.props
if (!(pages && pages.length > 0)) return null
return <RowList {...props} data={pages} renderItem={this.renderItem} />
}
}

View File

@@ -0,0 +1,44 @@
import React, { SFC } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import Icon from 'react-native-vector-icons/Octicons'
import { trimNewLinesAndSpaces } from '../../../../utils/helpers/shared'
import cardStyles from '../../styles'
import { getGithubURLPressHandler } from './helpers'
import rowStyles from './styles'
export interface IProps {
isRead?: boolean
name?: string
title: string
url: string
}
export interface IState {}
const WikiPageRow: SFC<IProps> = ({ isRead, name, title: _title, url }) => {
const title = trimNewLinesAndSpaces(_title || name)
if (!title) return null
return (
<View style={rowStyles.container}>
<View style={cardStyles.leftColumn} />
<View style={cardStyles.rightColumn}>
<TouchableOpacity
onPress={getGithubURLPressHandler(url)}
style={rowStyles.mainContentContainer}
>
<Text
numberOfLines={1}
style={[cardStyles.normalText, isRead && cardStyles.mutedText]}
>
<Icon name="book" /> {title}
</Text>
</TouchableOpacity>
</View>
</View>
)
}
export default WikiPageRow

View File

@@ -1,17 +1,36 @@
import R from 'ramda'
import SafariView from 'react-native-safari-view'
export const getUserPressHandler = R.memoize(
(username?: string) =>
username
? () => SafariView.show({ url: `https://github.com/${username}` })
import { fixURL } from '../../../../utils/helpers/github/url'
const baseURL = 'https://github.com'
export const getGithubURLPressHandler = R.memoize((url?: string) => {
const fixedURL = fixURL(url)
return fixedURL ? () => SafariView.show({ url: fixedURL }) : undefined
}) as (url?: string) => () => void | undefined
export const getBranchPressHandler = R.memoize(
(ownerName?: string, repositoryName?: string, branch?: string) =>
ownerName && repositoryName && branch
? getGithubURLPressHandler(
`${baseURL}/${ownerName}/${repositoryName}/tree/${branch}`,
)
: undefined,
) as (username?: string) => () => void | undefined
) as (
ownerName?: string,
repositoryName?: string,
branch?: string,
) => () => void | undefined
export const getRepositoryPressHandler = R.memoize(
(owner?: string, repository?: string) =>
owner && repository
? () =>
SafariView.show({ url: `https://github.com/${owner}/${repository}` })
(ownerName?: string, repositoryName?: string) =>
ownerName && repositoryName
? getGithubURLPressHandler(`${baseURL}/${ownerName}/${repositoryName}`)
: undefined,
) as (owner?: string, repository?: string) => () => void | undefined
) as (owner?: string, repositoryName?: string) => () => void | undefined
export const getUserPressHandler = R.memoize(
(username?: string) =>
username ? getGithubURLPressHandler(`${baseURL}/${username}`) : undefined,
) as (username?: string) => () => void | undefined

View File

@@ -3,7 +3,7 @@ import { StyleSheet, TextStyle, ViewStyle } from 'react-native'
import theme from '../../../../styles/themes/dark'
import {
contentPadding,
radius,
// radius,
smallTextSize,
} from '../../../../styles/variables'
@@ -17,12 +17,12 @@ export default StyleSheet.create({
mainContentContainer: {
alignItems: 'center',
borderColor: theme.base01,
borderRadius: radius,
// borderRadius: radius,
// borderWidth: 1,
flexDirection: 'row',
flexGrow: 1,
// paddingHorizontal: contentPadding,
paddingVertical: contentPadding / 2,
// paddingVertical: contentPadding / 2,
} as ViewStyle,
repositoryText: {

View File

@@ -18,14 +18,19 @@ export default StyleSheet.create({
width: avatarSize,
} as ViewStyle,
avatar: {
alignSelf: 'flex-end',
} as ImageStyle,
leftColumnAlignTop: {
alignSelf: 'flex-start',
} as ViewStyle,
rightColumn: {
flex: 1,
flexDirection: 'row',
} as ViewStyle,
avatar: {
alignSelf: 'flex-end',
} as ImageStyle,
usernameText: {
alignSelf: 'center',
color: theme.base04,
@@ -43,6 +48,14 @@ export default StyleSheet.create({
color: theme.base05,
} as TextStyle,
normalText: {
color: theme.base04,
} as TextStyle,
smallText: {
fontSize: smallTextSize,
} as TextStyle,
descriptionText: {
color: theme.base05,
lineHeight: 20,

View File

@@ -12,11 +12,15 @@ import {
getUserAvatarByEmail,
getUserAvatarByUsername,
} from '../../utils/helpers/github/shared'
import { getUserPressHandler } from '../cards/partials/rows/helpers'
import {
getGithubURLPressHandler,
getUserPressHandler,
} from '../cards/partials/rows/helpers'
export interface IProps {
avatarURL?: string
email?: string
linkURL?: string
size?: number
small?: boolean
style?: ImageStyle
@@ -34,6 +38,7 @@ const styles = StyleSheet.create({
const Avatar: SFC<IProps> = ({
avatarURL,
email,
linkURL,
size: _size,
small,
style,
@@ -48,7 +53,13 @@ const Avatar: SFC<IProps> = ({
if (!uri) return null
return (
<TouchableOpacity onPress={getUserPressHandler(username)}>
<TouchableOpacity
onPress={
username
? getUserPressHandler(username)
: linkURL ? getGithubURLPressHandler(linkURL) : undefined
}
>
<Image
{...props}
source={{ uri }}

View File

@@ -1,5 +1,3 @@
// @flow
import React, { ReactNode, SFC } from 'react'
import {
StyleSheet,
@@ -39,6 +37,7 @@ const styles = StyleSheet.create({
paddingHorizontal: contentPadding,
paddingVertical: 2,
} as ViewStyle,
labelText: {
fontSize: 14,
} as TextStyle,
@@ -70,6 +69,7 @@ const Label: SFC<IProps> = ({
{...containerProps}
>
<Text
numberOfLines={1}
style={[
styles.labelText,
{
@@ -85,7 +85,7 @@ const Label: SFC<IProps> = ({
>
{Boolean(isPrivate) && (
<Text>
<Icon name="lock" />&nbsp,
<Icon name="lock" />{' '}
</Text>
)}
{children}

View File

@@ -0,0 +1,121 @@
import React, { ReactNode, SFC } from 'react'
import { View, ViewStyle } from 'react-native'
import LinearGradient from '../../libs/linear-gradient'
import { fade } from '../../utils/helpers/color'
type From = 'top' | 'bottom' | 'left' | 'right'
type FromWithVH = 'vertical' | 'horizontal' | From
export interface IProps {
children?: ReactNode
color: string
containerStyle?: ViewStyle
from: FromWithVH
radius?: number
size: number
style?: ViewStyle
}
function getStyle(from: From, size: number): ViewStyle {
switch (from) {
case 'top':
return {
height: size,
left: 0,
position: 'absolute',
right: 0,
top: 0,
}
case 'bottom':
return {
bottom: 0,
height: size,
left: 0,
position: 'absolute',
right: 0,
}
case 'left':
return {
bottom: 0,
left: 0,
position: 'absolute',
top: 0,
width: size,
}
case 'right':
return {
bottom: 0,
position: 'absolute',
right: 0,
top: 0,
width: size,
}
default:
return {}
}
}
function getProps(from: From, size: number) {
switch (from) {
case 'top':
return { start: { x: 0, y: 1 }, end: { x: 0, y: 0 }, height: size }
case 'bottom':
return { start: { x: 0, y: 0 }, end: { x: 0, y: 1 }, height: size }
case 'left':
return { start: { x: 1, y: 0 }, end: { x: 0, y: 0 }, width: size }
default:
return { start: { x: 0, y: 0 }, end: { x: 1, y: 0 }, width: size }
}
}
const GradientLayerOverlay: SFC<IProps & { from: From }> = ({
color,
from,
radius,
size,
style,
...props
}) => (
<LinearGradient
colors={[fade(color, 0), color]}
style={[
getStyle(from, size),
Boolean(radius) && { borderRadius: radius },
style,
]}
{...getProps(from, size)}
{...props}
/>
)
const TransparentTextOverlay: SFC<IProps> = ({
children,
containerStyle,
from,
...props
}) => {
return (
<View style={[{ flex: 1, alignSelf: 'stretch' }, containerStyle]}>
{children}
{(from === 'vertical' || from === 'top') && (
<GradientLayerOverlay {...props} from="top" />
)}
{(from === 'vertical' || from === 'bottom') && (
<GradientLayerOverlay {...props} from="bottom" />
)}
{(from === 'horizontal' || from === 'left') && (
<GradientLayerOverlay {...props} from="left" />
)}
{(from === 'horizontal' || from === 'right') && (
<GradientLayerOverlay {...props} from="right" />
)}
</View>
)
}
export default TransparentTextOverlay

View File

@@ -96,7 +96,7 @@ const _data: IGitHubEvent[] = [
closed_at: null,
author_association: 'CONTRIBUTOR',
body:
'### Is this a bug report?\r\n\r\nYes. In the example `index.js` we have a line `let language = this.props.language...`. However, in `generate.js` and particularly `metadata.js` we are actually setting the permalink on the assumption that the language isn\'t set.\r\n\r\n```\r\nif (languages.length === 1 && !siteConfig.useEnglishUrl) {\r\n metadata.permalink = \'docs/\' + metadata.id + \'.html\';\r\n } else {\r\n metadata.permalink = \'docs/\' + language + \'/\' + metadata.id + \'.html\';\r\n }\r\n```\r\n\r\nBut in the code before this, we are actually setting this.props.language to `en` by default, I think. So we are conflicting.\r\n\r\nThere are a few ways I can fix this, but I need to come up with the best way.\r\n\r\n### Have you read the [Contributing Guidelines]\r\n\r\nYes, of course. I helped right them :)\r\n\r\n### Environment\r\n\r\nN/A\r\n\r\n### Steps to Reproduce\r\n\r\n1. `yarn global add docusaurus-init`\r\n2. `docusaurus-init`\r\n3. `mv docs-examples-from-docusaurus docs` && `mv website/blog-examples-from-docusaurus website/blog`\r\n4. `cd website`\r\n5. `yarn run start`\r\n6. Go to http://localhost:3000\r\n7. Click on the `Example Link` Button\r\n8. See 404.\r\n\r\n### Expected Behavior\r\n\r\nThe button links should go to an actual docs page.\r\n\r\n### Actual Behavior\r\n\r\nThe button links go to a 404-ish page.\r\n\r\n<img width="1324" alt="screenshot 2017-12-17 15 53 41" src="https://user-images.githubusercontent.com/3757713/34085287-1aaa43ce-e343-11e7-9e04-4edb68d232e7.png">\r\n\r\n<img width="563" alt="screenshot 2017-12-17 15 54 00" src="https://user-images.githubusercontent.com/3757713/34085290-2361cc80-e343-11e7-9957-3a7b39114f83.png">\r\n\r\n### Reproducible Demo\r\n\r\nRun the steps above.\r\n',
'### Is this a bug report?\r\n\r\nYes. In the example `index.js` we have a line `let language = this.props.language...`. However, in `generate.js` and particularly `metadata.js` we are actually setting the permalink on the assumption that the language isn\'t set.\r\n\r\n```\r\nif (languages.length === 1 && !siteConfig.useEnglishUrl) {\r\n metadata.permalink = \'docs/\' + metadata.id + \'.html\';\r\n } else {\r\n metadata.permalink = \'docs/\' + language + \'/\' + metadata.id + \'.html\';\r\n }\r\n```\r\n\r\nBut in the code before this, we are actually setting this.props.language to `en` by default, I think. So we are conflicting.\r\n\r\nThere are a few ways I can fix this, but I need to come up with the best way.\r\n\r\n### Have you isRead the [Contributing Guidelines]\r\n\r\nYes, of course. I helped right them :)\r\n\r\n### Environment\r\n\r\nN/A\r\n\r\n### Steps to Reproduce\r\n\r\n1. `yarn global add docusaurus-init`\r\n2. `docusaurus-init`\r\n3. `mv docs-examples-from-docusaurus docs` && `mv website/blog-examples-from-docusaurus website/blog`\r\n4. `cd website`\r\n5. `yarn run start`\r\n6. Go to http://localhost:3000\r\n7. Click on the `Example Link` Button\r\n8. See 404.\r\n\r\n### Expected Behavior\r\n\r\nThe button links should go to an actual docs page.\r\n\r\n### Actual Behavior\r\n\r\nThe button links go to a 404-ish page.\r\n\r\n<img width="1324" alt="screenshot 2017-12-17 15 53 41" src="https://user-images.githubusercontent.com/3757713/34085287-1aaa43ce-e343-11e7-9e04-4edb68d232e7.png">\r\n\r\n<img width="563" alt="screenshot 2017-12-17 15 54 00" src="https://user-images.githubusercontent.com/3757713/34085290-2361cc80-e343-11e7-9957-3a7b39114f83.png">\r\n\r\n### Reproducible Demo\r\n\r\nRun the steps above.\r\n',
},
comment: {
url:
@@ -148,83 +148,83 @@ const _data: IGitHubEvent[] = [
avatar_url: 'https://avatars.githubusercontent.com/u/69631?',
},
},
{
id: '6999953726',
type: 'PushEvent',
actor: {
id: 3757713,
login: 'JoelMarcey',
display_login: 'JoelMarcey',
gravatar_id: '',
url: 'https://api.github.com/users/JoelMarcey',
avatar_url: 'https://avatars.githubusercontent.com/u/3757713?',
},
repo: {
id: 94911145,
name: 'facebook/Docusaurus',
url: 'https://api.github.com/repos/facebook/Docusaurus',
},
payload: {
push_id: 2201568216,
size: 1,
distinct_size: 1,
ref: 'refs/heads/gh-pages',
head: '043929ac2164d24dbe0b87fed506c67cfa0a5846',
before: '6b3d635cc6cdde1a84e4ce9bfc2b3fe95f866f43',
commits: [
{
sha: '043929ac2164d24dbe0b87fed506c67cfa0a5846',
author: {
email: 'docusaurus@users.noreply.github.com',
name: 'Website Deployment Script',
},
message:
'Deploy website\n\nDeploy website version based on 6b3d635cc6cdde1a84e4ce9bfc2b3fe95f866f43',
distinct: true,
url:
'https://api.github.com/repos/facebook/Docusaurus/commits/043929ac2164d24dbe0b87fed506c67cfa0a5846',
},
],
},
public: true,
created_at: '2017-12-18T00:36:40Z',
org: {
id: 69631,
login: 'facebook',
gravatar_id: '',
url: 'https://api.github.com/orgs/facebook',
avatar_url: 'https://avatars.githubusercontent.com/u/69631?',
},
},
{
id: '6999952659',
type: 'WatchEvent',
actor: {
id: 16931088,
login: 'wagaman',
display_login: 'wagaman',
gravatar_id: '',
url: 'https://api.github.com/users/wagaman',
avatar_url: 'https://avatars.githubusercontent.com/u/16931088?',
},
repo: {
id: 29028775,
name: 'facebook/react-native',
url: 'https://api.github.com/repos/facebook/react-native',
},
payload: {
action: 'started',
},
public: true,
created_at: '2017-12-18T00:35:54Z',
org: {
id: 69631,
login: 'facebook',
gravatar_id: '',
url: 'https://api.github.com/orgs/facebook',
avatar_url: 'https://avatars.githubusercontent.com/u/69631?',
},
},
// {
// id: '6999953726',
// type: 'PushEvent',
// actor: {
// id: 3757713,
// login: 'JoelMarcey',
// display_login: 'JoelMarcey',
// gravatar_id: '',
// url: 'https://api.github.com/users/JoelMarcey',
// avatar_url: 'https://avatars.githubusercontent.com/u/3757713?',
// },
// repo: {
// id: 94911145,
// name: 'facebook/Docusaurus',
// url: 'https://api.github.com/repos/facebook/Docusaurus',
// },
// payload: {
// push_id: 2201568216,
// size: 1,
// distinct_size: 1,
// ref: 'refs/heads/gh-pages',
// head: '043929ac2164d24dbe0b87fed506c67cfa0a5846',
// before: '6b3d635cc6cdde1a84e4ce9bfc2b3fe95f866f43',
// commits: [
// {
// sha: '043929ac2164d24dbe0b87fed506c67cfa0a5846',
// author: {
// email: 'docusaurus@users.noreply.github.com',
// name: 'Website Deployment Script',
// },
// message:
// 'Deploy website\n\nDeploy website version based on 6b3d635cc6cdde1a84e4ce9bfc2b3fe95f866f43',
// distinct: true,
// url:
// 'https://api.github.com/repos/facebook/Docusaurus/commits/043929ac2164d24dbe0b87fed506c67cfa0a5846',
// },
// ],
// },
// public: true,
// created_at: '2017-12-18T00:36:40Z',
// org: {
// id: 69631,
// login: 'facebook',
// gravatar_id: '',
// url: 'https://api.github.com/orgs/facebook',
// avatar_url: 'https://avatars.githubusercontent.com/u/69631?',
// },
// },
// {
// id: '6999952659',
// type: 'WatchEvent',
// actor: {
// id: 16931088,
// login: 'wagaman',
// display_login: 'wagaman',
// gravatar_id: '',
// url: 'https://api.github.com/users/wagaman',
// avatar_url: 'https://avatars.githubusercontent.com/u/16931088?',
// },
// repo: {
// id: 29028775,
// name: 'facebook/react-native',
// url: 'https://api.github.com/repos/facebook/react-native',
// },
// payload: {
// action: 'started',
// },
// public: true,
// created_at: '2017-12-18T00:35:54Z',
// org: {
// id: 69631,
// login: 'facebook',
// gravatar_id: '',
// url: 'https://api.github.com/orgs/facebook',
// avatar_url: 'https://avatars.githubusercontent.com/u/69631?',
// },
// },
{
id: '6999952360',
type: 'PushEvent',

View File

@@ -0,0 +1,4 @@
import LinearGradient from 'react-native-linear-gradient'
export * from 'react-native-linear-gradient'
export default LinearGradient

View File

@@ -0,0 +1,30 @@
import React, { SFC } from 'react'
import { PointProperties, View, ViewStyle } from 'react-native'
export interface IProps {
colors: string[]
end: PointProperties
height: number
locations: string[]
start: PointProperties
style?: ViewStyle
width: number
}
const radToDeg = (angle: number) => angle * (180 / Math.PI)
const pointsToAngle = (p1: PointProperties, p2: PointProperties) =>
radToDeg(Math.atan2(p2.y - p1.y, p2.x - p1.x)) + 90
const propsToDeg = ({ start, end }: IProps) => `${pointsToAngle(start, end)}deg`
const propsToLinearGradient = (props: IProps) =>
`linear-gradient(${propsToDeg(props)}, ${props.colors.join(', ')})`
const LinearGradient: SFC<IProps> = props => (
<View
{...props}
style={[props.style, { backgroundColor: propsToLinearGradient(props) }]}
/>
)
export default LinearGradient

View File

@@ -1,5 +1,5 @@
import React from 'react'
import { Animated, StyleSheet, Text } from 'react-native'
import { Animated, StyleSheet, Text, TextStyle, ViewStyle } from 'react-native'
import { RectButton, Swipeable } from 'react-native-gesture-handler'
import BaseSwipeableRow, {
@@ -137,13 +137,14 @@ export default class AppleSwipeableRow extends BaseSwipeableRow<IAction> {
}
const styles = StyleSheet.create({
baseActionContainer: {
flex: 1,
justifyContent: 'center',
} as ViewStyle,
actionText: {
backgroundColor: 'transparent',
fontSize: 16,
padding: 10,
},
baseActionContainer: {
flex: 1,
justifyContent: 'center',
},
} as TextStyle,
})

View File

@@ -1,5 +1,5 @@
import React from 'react'
import { Animated, StyleSheet } from 'react-native'
import { Animated, StyleSheet, TextStyle, ViewStyle } from 'react-native'
import { RectButton, Swipeable } from 'react-native-gesture-handler'
import Icon from 'react-native-vector-icons/MaterialIcons'
@@ -136,13 +136,14 @@ export default class GoogleSwipeableRow extends BaseSwipeableRow {
}
const styles = StyleSheet.create({
baseActionContainer: {
flex: 1,
justifyContent: 'center',
} as TextStyle,
actionIcon: {
backgroundColor: 'transparent',
marginHorizontal: 10,
width: 30,
},
baseActionContainer: {
flex: 1,
justifyContent: 'center',
},
} as ViewStyle,
})

View File

@@ -3,12 +3,12 @@ import React from 'react'
import { init, startMainApp } from './screens'
export async function setup() {
if (__DEV__) {
const { whyDidYouUpdate } = require('why-did-you-update')
whyDidYouUpdate(React, {
exclude: /[^a-zA-Z0-9]|CellRenderer|Icon|Swipeable/,
})
}
// if (__DEV__) {
// const { whyDidYouUpdate } = require('why-did-you-update')
// whyDidYouUpdate(React, {
// exclude: /[^a-zA-Z0-9]|CellRenderer|Icon|Swipeable/,
// })
// }
await init()

View File

@@ -1,6 +1,10 @@
import { StyleSheet, ViewStyle } from 'react-native'
export default StyleSheet.create({
full: {
flex: 1,
} as ViewStyle,
horizontal: {
flexDirection: 'row',
} as ViewStyle,

View File

@@ -1,5 +1,6 @@
import Platform from '../../libs/platform'
import { ITheme } from '../../types'
import * as base from './base'
export const base00 = '#141c26' // page background
@@ -57,4 +58,4 @@ export default {
invert: () => require('./light').default, // tslint:disable-line
isDark: true,
name: 'dark-blue',
}
} as ITheme

View File

@@ -1,5 +1,6 @@
import { Platform } from 'react-native'
import { ITheme } from '../../types'
import { fade } from '../../utils/helpers/color'
import { mutedOpacity } from '../variables'
import * as base from './base'
@@ -59,4 +60,4 @@ export default {
invert: () => require('./light').default, // tslint:disable-line
isDark: true,
name: 'dark',
}
} as ITheme

View File

@@ -1,5 +1,6 @@
import { Platform } from 'react-native'
import { ITheme } from '../../types'
import { lighten } from '../../utils/helpers/color'
import { mutedOpacity } from '../variables'
import * as base from './base'
@@ -56,4 +57,4 @@ export default {
invert: () => require('./dark').default, // tslint:disable-line
isDark: false,
name: 'light',
}
} as ITheme

View File

@@ -137,6 +137,7 @@ export interface IGitHubRepo {
name: string
full_name?: string
url: string // https://api.github.com/repos/facebook/react
html_url: string // https://github.com/facebook/react
}
export interface IGitHubPage {
@@ -145,6 +146,23 @@ export interface IGitHubPage {
sha: string
title: string
html_url: string
url: string
}
export interface IGitHubRelease {
id: number // 1
tag_name: string // "v1.0.0"
target_commitish: string // "master"
name: string // "v1.0.0"
body: string // "Description of the release"
draft: boolean
prerelease: boolean
created_at: string // "2013-02-27T19:35:32Z"
published_at: string // "2013-02-27T19:35:32Z"
author: IGitHubUser
assets: any[] // see https://developer.github.com/v3/repos/releases/#get-a-single-release
url: string // "https://api.github.com/repos/octocat/Hello-World/releases/1"
html_url: string // "https://github.com/octocat/Hello-World/releases/v1.0.0"
}
/**
@@ -283,6 +301,8 @@ export interface IIssuesEvent {
assignee?: IGitHubUser | null // The optional user who was assigned or unassigned from the issue.
label?: IGitHubLabel // The optional label that was added or removed from the issue.
}
url: string
html_url: string
}
/**
@@ -401,6 +421,7 @@ export interface IPushEvent {
commits: IGitHubCommit[]
}
public?: boolean
forced?: boolean
}
/**
@@ -414,7 +435,7 @@ export interface IReleaseEvent {
repo: IGitHubRepo
payload: {
action: 'published'
release: object // https://developer.github.com/v3/repos/releases/#get-a-single-release
release: IGitHubRelease // https://developer.github.com/v3/repos/releases/#get-a-single-release
}
}

View File

@@ -2,6 +2,25 @@ export * from './github'
export type Theme = '' | 'auto' | 'light' | 'dark' | 'dark-blue'
export interface IBase16 {
base00: string
base01: string
base02: string
base03: string
base04: string
base05: string
base06: string | undefined
base07: string
base08: string
base09: string | undefined
base0A: string | undefined
base0B: string | undefined
base0C: string | undefined
base0D: string | undefined
base0E: string | undefined
base0F: string | undefined
}
export interface IBaseTheme {
blue: string
blueGray: string
@@ -23,21 +42,11 @@ export interface IBaseTheme {
yellow: string
}
export interface ITheme extends IBaseTheme {
base00: string
base01: string
base02: string
base03: string
base04: string
base05: string
base06: string | undefined
base07: string
base08: string
base09: string | undefined
base0A: string | undefined
base0B: string | undefined
base0C: string | undefined
base0D: string | undefined
base0E: string | undefined
base0F: string | undefined
export interface ITheme extends IBaseTheme, IBase16 {
cardBackground: string
invert: () => string
isDark: boolean
name: Theme
statusBarBackground: string
tabBarBackground: string
}

View File

@@ -20,7 +20,7 @@ export function getUserAvatarByUsername(
: ''
}
export function tryGetUsernameFromGitHubEmail(email: string) {
export function tryGetUsernameFromGitHubEmail(email?: string) {
if (!email) return ''
const emailSplit = email.split('@')
@@ -54,6 +54,8 @@ export function isPullRequest(issue: IGitHubIssue | IGitHubPullRequest) {
export function getOwnerAndRepo(
repoFullName: string,
): { owner: string | undefined; repo: string | undefined } {
if (!repoFullName) return { owner: '', repo: '' }
const repoSplitedNames = (repoFullName || '')
.trim()
.split('/')

View File

@@ -0,0 +1,132 @@
import { IGitHubRepo } from '../../../types'
export const baseURL = 'https://github.com'
export function getCommentIdFromUrl(url: string) {
if (!url) return null
const matches = url.match(/\/comments\/([0-9]+)([?].+)?$/)
return (matches && matches[1]) || null
}
export function getCommitShaFromUrl(url: string) {
if (!url) return null
const matches = url.match(/\/commits\/([a-zA-Z0-9]+)([?].+)?$/)
return (matches && matches[1]) || null
}
export function getIssueOrPullRequestNumberFromUrl(url: string) {
if (!url) return null
const matches = url.match(/\/(issues|pulls)\/([0-9]+)([?].+)?$/)
const n = matches && matches[2]
return (n && parseInt(n, 10)) || null
}
export function getReleaseIdFromUrl(url: string) {
if (!url) return null
const matches = url.match(/\/releases\/([0-9]+)([?].+)?$/)
return (matches && matches[1]) || null
}
/* eslint-disable-next-line no-useless-escape */
export const getRepoFullNameFromUrl = (url: string): string =>
url
? (url.match(
/(github.com\/(repos\/)?)([a-zA-Z0-9\-._]+\/[a-zA-Z0-9\-._]+[^/#$]?)/i,
) || [])[3] || ''
: ''
export const getRepoFullNameFromObject = (repo: IGitHubRepo): string =>
(repo &&
(repo.full_name ||
repo.name ||
getRepoFullNameFromUrl(repo.html_url || repo.url))) ||
''
export const getGitHubURLForUser = (user: string) =>
user ? `${baseURL}/${user}` : ''
const objToQueryParams = (obj: { [key: string]: string | number }) =>
Object.keys(obj)
.map(key => `${key}=${obj[key]}`)
.join('&')
export const getGitHubSearchURL = (queryParams: {
[key: string]: string | number
}) => (queryParams ? `${baseURL}/search?${objToQueryParams(queryParams)}` : '')
export const getGitHubURLForBranch = (repoFullName: string, branch: string) =>
repoFullName && branch ? `${baseURL}/${repoFullName}/tree/${branch}` : ''
export function githubHTMLUrlFromAPIUrl(
apiURL: string,
{ n }: { n?: number } = {},
): string {
if (!apiURL) return ''
const [, type, restOfURL] = apiURL.match(
'api.github.com/([a-zA-Z]+)/(.*)',
) as string[]
if (!(type && restOfURL)) return ''
if (type === 'repos') {
const repoFullName = getRepoFullNameFromUrl(apiURL)
const [type2, ...restOfURL2] = (
apiURL.split(`/repos/${repoFullName}/`)[1] || ''
).split('/')
if (restOfURL2[0]) {
switch (type2) {
case 'commits':
return `${baseURL}/${repoFullName}/commit/${restOfURL2.join('/')}`
case 'issues':
if (restOfURL2[0] === 'comments' && restOfURL2[1]) {
return n
? `${baseURL}/${repoFullName}/pull/${n}/comments#issuecomment-${
restOfURL2[1]
}`
: ''
}
return `${baseURL}/${repoFullName}/issues/${restOfURL2.join('/')}`
case 'pulls':
if (restOfURL2[0] === 'comments' && restOfURL2[1]) {
return n
? `${baseURL}/${repoFullName}/pull/${n}/comments#discussion_r${
restOfURL2[1]
}`
: ''
}
return `${baseURL}/${repoFullName}/pull/${restOfURL2.join('/')}`
case 'releases':
// it wont go directly to the release, but to the generic releases page.
// we would need to have the tag name to do that.
return `${baseURL}/${repoFullName}/releases/?${restOfURL2.join('/')}`
default:
return `${baseURL}/${restOfURL}`
}
}
}
return `${baseURL}/${restOfURL}`
}
export function fixURL(url?: string) {
if (!url) return
// sometimes the url come like this: '/facebook/react', so we add https://github.com
let uri =
url[0] === '/' && url.indexOf('github.com') < 0 ? `${baseURL}${url}` : url
uri = uri.indexOf('api.github.com') >= 0 ? githubHTMLUrlFromAPIUrl(uri) : uri
return uri
}

View File

@@ -18,3 +18,14 @@ export function getSteppedSize(size?: number, sizeSteps = 50) {
export function randomBetween(minNumber: number, maxNumber: number) {
return Math.floor(Math.random() * maxNumber) + minNumber
}
export function trimNewLinesAndSpaces(text?: string, maxLength: number = 100) {
if (!text || typeof text !== 'string') return ''
let newText = text.replace(/\s+/g, ' ').trim()
if (maxLength > 0 && newText.length > maxLength) {
newText = `${newText.substr(0, maxLength).trim()}...`
}
return newText
}

View File

@@ -4153,6 +4153,12 @@ react-native-gesture-handler@^1.0.0-alpha.37:
invariant "^2.2.2"
prop-types "^15.5.10"
react-native-linear-gradient@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.4.0.tgz#51d8ea12bb72a59bede9edc87b694b16b64cf435"
dependencies:
prop-types "^15.5.10"
react-native-navigation@^1.1.334:
version "1.1.334"
resolved "https://registry.yarnpkg.com/react-native-navigation/-/react-native-navigation-1.1.334.tgz#255cfe35ea5d7e6edd9d3583dd7a65b9d87736bd"