Compare commits

..

12 Commits

Author SHA1 Message Date
João Ferreiro 7086b4a68c Revert "hotfix"
This reverts commit cd5175c663.
2022-12-13 16:30:46 +00:00
João Ferreiro cd5175c663 hotfix 2022-12-13 16:30:10 +00:00
João Ferreiro ed56365cc0 hotfix 2022-12-13 16:13:47 +00:00
João Ferreiro afea10e5dd hotfix 2022-12-13 16:00:13 +00:00
João Ferreiro 10360ecded update lib setup 2022-12-13 15:51:25 +00:00
João Ferreiro fe239abd26 adding more register usage (utils) 2022-12-13 15:14:42 +00:00
João Ferreiro 50818d73f6 adding more register usage 2022-12-13 11:20:02 +00:00
João Ferreiro 7021ea2745 remove release flag 2022-12-12 18:07:48 +00:00
João Ferreiro 441dd29685 using register 2022-12-12 18:07:30 +00:00
João Ferreiro 89ddcdfa52 Merge branch 'main' into improve-cold-boot-time 2022-12-12 16:59:03 +00:00
João Ferreiro a4422f1819 Revert "using register"
This reverts commit e28093ec35.
2022-12-12 16:58:33 +00:00
João Ferreiro e28093ec35 using register 2022-12-12 16:30:52 +00:00
461 changed files with 23396 additions and 21880 deletions
+4 -8
View File
@@ -2,18 +2,14 @@ module.exports = {
root: true,
extends: '@react-native-community',
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
ignorePatterns: [
'**/__mocks__/*.ts',
'src/third-party',
'ios',
'android',
'coverage',
],
// plugins: ['@typescript-eslint'],
overrides: [
{
files: ['*.js', '*.mjs', '*.ts', '*.tsx'],
rules: {
'@typescript-eslint/no-shadow': 'off',
'no-shadow': 'off',
'no-undef': 'off',
semi: [2, 'never'],
},
},
-38
View File
@@ -1,38 +0,0 @@
name: Lint
on:
pull_request:
branches:
- main
jobs:
linting:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: Yarn install
run: yarn
- name: Lint Reporter
uses: wearerequired/lint-action@v2.2.0
with:
eslint: true
prettier: true
- name: Typescript & Lint check
run: yarn lint
testing:
name: Run tests
runs-on: ubuntu-latest
steps:
- name: Install node 18
uses: actions/setup-node@v3
with:
node-version: 18
- name: Check out Git repository
uses: actions/checkout@v2
- name: Yarn install
run: yarn
- name: Run tests
run: |
yarn test --forceExit
-4
View File
@@ -59,7 +59,3 @@ buck-out/
# Ruby / CocoaPods
/ios/Pods/
/vendor/bundle/
# Testing
coverage/
junit.xml
-5
View File
@@ -1,5 +0,0 @@
ios
android
src/third-party
src/app.json
public
+1 -1
View File
@@ -1 +1 @@
2.7.6
2.7.4
+2 -2
View File
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby File.read(File.join(__dir__, '.ruby-version')).strip
ruby '2.7.4'
gem 'cocoapods', '~> 1.11', '>= 1.11.3'
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
+2 -4
View File
@@ -16,10 +16,8 @@ Uses:
- After initial setup:
- `cd ios ; pod install`
- Start the dev servers
- `git clone git@github.com:bluesky-social/atproto.git`
- `cd atproto`
- `yarn`
- `cd packages/dev-env && yarn start`
- `yarn dev-pds`
- `yarn dev-wallet`
- Run the dev app
- iOS: `yarn ios`
- Android: `yarn android`
-57
View File
@@ -1,57 +0,0 @@
import React from 'react'
import {View, ScrollView, Modal, FlatList, TextInput} from 'react-native'
const BottomSheetModalContext = React.createContext(null)
const BottomSheetModalProvider = (props: any) => {
return <BottomSheetModalContext.Provider {...props} value={{}} />
}
class BottomSheet extends React.Component {
snapToIndex() {}
snapToPosition() {}
expand() {}
collapse() {}
close() {
this.props.onClose?.()
}
forceClose() {}
render() {
return <View>{this.props.children}</View>
}
}
const BottomSheetModal = (props: any) => <Modal {...props} />
const BottomSheetBackdrop = (props: any) => <View {...props} />
const BottomSheetHandle = (props: any) => <View {...props} />
const BottomSheetFooter = (props: any) => <View {...props} />
const BottomSheetScrollView = (props: any) => <ScrollView {...props} />
const BottomSheetFlatList = (props: any) => <FlatList {...props} />
const BottomSheetTextInput = (props: any) => <TextInput {...props} />
const useBottomSheet = jest.fn()
const useBottomSheetModal = jest.fn()
const useBottomSheetSpringConfigs = jest.fn()
const useBottomSheetTimingConfigs = jest.fn()
const useBottomSheetInternal = jest.fn()
const useBottomSheetDynamicSnapPoints = jest.fn()
export {useBottomSheet}
export {useBottomSheetModal}
export {useBottomSheetSpringConfigs}
export {useBottomSheetTimingConfigs}
export {useBottomSheetInternal}
export {useBottomSheetDynamicSnapPoints}
export {
BottomSheetModalProvider,
BottomSheetBackdrop,
BottomSheetHandle,
BottomSheetModal,
BottomSheetFooter,
BottomSheetScrollView,
BottomSheetFlatList,
BottomSheetTextInput,
}
export default BottomSheet
-6
View File
@@ -1,6 +0,0 @@
export default {
requestPermission: jest.fn(),
onForegroundEvent: jest.fn(),
setBadgeCount: jest.fn(),
displayNotification: jest.fn(),
}
@@ -1,9 +0,0 @@
export const CameraRoll = {
getPhotos: jest.fn().mockResolvedValue({
edges: [
{node: {image: {uri: 'path/to/image1.jpg'}}},
{node: {image: {uri: 'path/to/image2.jpg'}}},
{node: {image: {uri: 'path/to/image3.jpg'}}},
],
}),
}
+1
View File
@@ -0,0 +1 @@
export default from '@react-native-async-storage/async-storage/jest/async-storage-mock'
@@ -1,4 +0,0 @@
export default {
configure: jest.fn().mockResolvedValue(0),
finish: jest.fn(),
}
-1
View File
@@ -1 +0,0 @@
export default {}
-9
View File
@@ -1,9 +0,0 @@
export const openPicker = jest
.fn()
.mockImplementation(() => Promise.resolve({uri: ''}))
export const openCamera = jest
.fn()
.mockImplementation(() => Promise.resolve({uri: ''}))
export const openCropper = jest
.fn()
.mockImplementation(() => Promise.resolve({uri: ''}))
-10
View File
@@ -1,10 +0,0 @@
jest.mock('rn-fetch-blob', () => {
return {
__esModule: true,
default: {
fs: {
unlink: jest.fn(),
},
},
}
})
-937
View File
@@ -1,937 +0,0 @@
import {LogModel} from './../src/state/models/log'
import {LRUMap} from 'lru_map'
import {RootStoreModel} from './../src/state/models/root-store'
import {NavigationTabModel} from './../src/state/models/navigation'
import {SessionModel} from '../src/state/models/session'
import {NavigationModel} from '../src/state/models/navigation'
import {ShellUiModel} from '../src/state/models/shell-ui'
import {MeModel} from '../src/state/models/me'
import {OnboardModel} from '../src/state/models/onboard'
import {ProfilesViewModel} from '../src/state/models/profiles-view'
import {LinkMetasViewModel} from '../src/state/models/link-metas-view'
import {MembershipsViewModel} from '../src/state/models/memberships-view'
import {FeedModel} from '../src/state/models/feed-view'
import {NotificationsViewModel} from '../src/state/models/notifications-view'
import {ProfileViewModel} from '../src/state/models/profile-view'
import {MembersViewModel} from '../src/state/models/members-view'
import {ProfileUiModel, Sections} from '../src/state/models/profile-ui'
import {SessionServiceClient} from '@atproto/api'
import {UserAutocompleteViewModel} from '../src/state/models/user-autocomplete-view'
import {UserLocalPhotosModel} from '../src/state/models/user-local-photos'
import {SuggestedActorsViewModel} from '../src/state/models/suggested-actors-view'
import {UserFollowersViewModel} from '../src/state/models/user-followers-view'
import {UserFollowsViewModel} from '../src/state/models/user-follows-view'
import {NotificationsViewItemModel} from './../src/state/models/notifications-view'
import {
PostThreadViewModel,
PostThreadViewPostModel,
} from '../src/state/models/post-thread-view'
import {FeedItemModel} from '../src/state/models/feed-view'
import {RepostedByViewModel} from '../src/state/models/reposted-by-view'
import {VotesViewModel} from '../src/state/models/votes-view'
export const mockedProfileStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: true,
error: '',
params: {
actor: '',
},
did: 'test did',
handle: 'testhandle',
declaration: {
cid: '',
actorType: '',
},
creator: 'test did',
displayName: '',
description: '',
avatar: '',
banner: '',
followersCount: 0,
followsCount: 0,
membersCount: 0,
postsCount: 0,
myState: {
follow: '',
member: '',
},
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
isUser: true,
isScene: false,
setup: jest.fn().mockResolvedValue({aborted: false}),
refresh: jest.fn().mockResolvedValue({}),
toggleFollowing: jest.fn().mockResolvedValue({}),
updateProfile: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _load, _replaceAll
} as unknown as ProfileViewModel
export const mockedMembersStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: true,
error: '',
params: {
actor: 'test actor',
},
subject: {
did: 'test did',
handle: '',
displayName: '',
declaration: {
cid: '',
actorType: '',
},
avatar: undefined,
},
members: [
{
did: 'test did2',
declaration: {
cid: '',
actorType: '',
},
handle: 'testhandle',
displayName: 'test name',
indexedAt: '',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
isMember: jest.fn(),
setup: jest.fn().mockResolvedValue({aborted: false}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
removeMember: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _replaceAll, _append
} as unknown as MembersViewModel
export const mockedMembershipsStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: true,
error: '',
params: {
actor: '',
limit: 1,
before: '',
},
subject: {
did: 'test did',
handle: '',
displayName: '',
declaration: {cid: '', actorType: ''},
avatar: undefined,
},
memberships: [
{
did: 'test did',
declaration: {
cid: '',
actorType: 'app.bsky.system.actorUser',
},
handle: ',',
displayName: '',
createdAt: '',
indexedAt: '',
_reactKey: 'item-1',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
isMemberOf: jest.fn(),
setup: jest.fn().mockResolvedValue({aborted: false}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _replaceAll, _append
} as unknown as MembershipsViewModel
export const mockedFeedItemStore = {
_reactKey: 'item-1',
_isThreadParent: false,
_isThreadChildElided: false,
_isThreadChild: false,
post: {
uri: 'testuri',
cid: 'test cid',
author: {
did: 'test did',
handle: 'test.handle',
displayName: 'test name',
declaration: {cid: '', actorType: ''},
},
record: {
$type: 'app.bsky.feed.post',
createdAt: '2022-12-29T16:39:57.919Z',
text: 'Sup',
},
replyCount: 0,
repostCount: 0,
upvoteCount: 0,
downvoteCount: 0,
indexedAt: '2022-12-29T16:39:57.919Z',
viewer: {},
},
postRecord: {
$type: 'app.bsky.feed.post',
text: 'test text',
createdAt: '1',
reply: {
root: {
uri: 'testuri',
cid: 'tes cid',
},
parent: {
uri: 'testuri',
cid: 'tes cid',
},
},
},
rootStore: {} as RootStoreModel,
copy: jest.fn(),
toggleUpvote: jest.fn().mockResolvedValue({}),
toggleDownvote: jest.fn(),
toggleRepost: jest.fn().mockResolvedValue({}),
delete: jest.fn().mockResolvedValue({}),
reasonRepost: {
by: {
did: 'test did',
handle: 'test.handle',
declaration: {cid: '', actorType: ''},
},
indexedAt: '',
},
reasonTrend: {
by: {
did: 'test did',
handle: 'test.handle',
declaration: {cid: '', actorType: ''},
},
indexedAt: '',
},
reply: {
parent: {
author: {
did: 'test did',
handle: 'test.handle',
displayName: 'test name',
declaration: {cid: '', actorType: ''},
},
cid: '',
downvoteCount: 0,
indexedAt: '2023-01-10T11:17:46.945Z',
record: {},
replyCount: 1,
repostCount: 0,
upvoteCount: 0,
uri: 'testuri',
viewer: {},
},
root: {
author: {
did: 'test did',
handle: 'test.handle',
displayName: 'test name',
declaration: {cid: '', actorType: ''},
},
cid: '',
downvoteCount: 0,
indexedAt: '2023-01-10T11:17:46.739Z',
record: {},
replyCount: 1,
repostCount: 0,
upvoteCount: 1,
uri: 'testuri',
viewer: {},
},
},
} as FeedItemModel
export const mockedFeedStore = {
isLoading: false,
isRefreshing: false,
hasNewLatest: false,
hasLoaded: true,
error: '',
hasMore: true,
params: {
actor: '',
limit: 1,
before: '',
},
feed: [],
rootStore: {} as RootStoreModel,
feedType: 'home',
hasContent: true,
hasError: false,
isEmpty: false,
nonReplyFeed: [
{
_reactKey: 'item-1',
post: {
author: {
handle: 'handle.test',
displayName: 'test name',
avatar: '',
},
cid: 'bafyreihkwjoy2vbfqld2lp3tv4ce6yfr354sqgp32qoplrudso4gyyjiwe',
downvoteCount: 0,
indexedAt: '2022-12-29T16:35:55.270Z',
record: {
$type: 'app.bsky.feed.post',
createdAt: '2022-12-29T16:39:57.919Z',
text: 'Sup',
},
replyCount: 0,
repostCount: 0,
upvoteCount: 0,
uri: 'at://did:plc:wcizmlgv3rdslk64t6q4silu/app.bsky.feed.post/3jkzce5kfvn2h',
viewer: {
handle: 'handle.test',
displayName: 'test name',
avatar: '',
},
},
reason: undefined,
reply: undefined,
},
],
setHasNewLatest: jest.fn(),
setup: jest.fn().mockResolvedValue({}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn().mockResolvedValue({}),
loadLatest: jest.fn(),
update: jest.fn(),
checkForLatest: jest.fn().mockRejectedValue('Error checking for latest'),
registerListeners: jest.fn().mockReturnValue(jest.fn()),
// unknown required because of the missing private methods: _xLoading, _xIdle, _pendingWork, _initialLoad, _loadLatest, _loadMore, _update, _replaceAll, _appendAll, _prependAll, _updateAll, _getFeed, loadMoreCursor, pollCursor, _loadPromise, _updatePromise, _loadLatestPromise, _loadMorePromise
} as unknown as FeedModel
export const mockedPostThreadViewPostStore = {
_reactKey: 'item-1',
_depth: 0,
_isHighlightedPost: false,
_hasMore: false,
postRecord: {
text: 'test text',
createdAt: '',
reply: {
root: {
uri: 'testuri',
cid: 'tes cid',
},
parent: {
uri: 'testuri',
cid: 'tes cid',
},
},
},
post: {
uri: 'testuri',
cid: 'testcid',
record: {},
author: {
did: 'test did',
handle: 'test.handle',
declaration: {cid: '', actorType: ''},
viewer: {
muted: true,
},
},
replyCount: 0,
repostCount: 0,
upvoteCount: 0,
downvoteCount: 0,
indexedAt: '',
viewer: {
repost: '',
upvote: '',
downvote: '',
},
},
rootStore: {} as RootStoreModel,
assignTreeModels: jest.fn(),
toggleRepost: jest.fn().mockRejectedValue({}),
toggleUpvote: jest.fn().mockRejectedValue({}),
toggleDownvote: jest.fn(),
delete: jest.fn(),
} as PostThreadViewPostModel
export const mockedPostThreadViewStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: false,
error: '',
notFound: false,
resolvedUri: 'testuri',
params: {
uri: 'testuri',
},
thread: mockedPostThreadViewPostStore,
hasContent: true,
hasError: false,
setup: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
update: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _resolveUri, _load, _replaceAll
} as unknown as PostThreadViewModel
export const mockedNotificationsViewItemStore = {
_reactKey: 'item-1',
uri: 'testuri',
cid: '',
author: {
did: 'test did',
handle: 'test.handle',
declaration: {cid: '', actorType: ''},
},
rootStore: {} as RootStoreModel,
copy: jest.fn(),
reason: 'test reason',
isRead: true,
indexedAt: '',
isUpvote: true,
isRepost: false,
isTrend: false,
isMention: false,
isReply: false,
isFollow: false,
isAssertion: false,
needsAdditionalData: false,
isInvite: false,
subjectUri: 'testuri',
toSupportedRecord: jest.fn().mockReturnValue({
text: 'test text',
createdAt: '',
}),
fetchAdditionalData: jest.fn(),
toNotifeeOpts: jest.fn(),
} as NotificationsViewItemModel
export const mockedNotificationsStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: true,
error: '',
params: {
limit: 1,
before: '',
},
hasMore: true,
notifications: [mockedNotificationsViewItemStore],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
setup: jest.fn().mockResolvedValue({aborted: false}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn().mockResolvedValue({}),
update: jest.fn().mockResolvedValue(null),
updateReadState: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _pendingWork, _initialLoad, _loadMore, _update, _replaceAll, _appendAll, _updateAll, loadMoreCursor, _loadPromise, _updatePromise, _loadLatestPromise, _loadMorePromise
} as unknown as NotificationsViewModel
export const mockedSessionStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
data: {
service: '',
refreshJwt: '',
accessJwt: '',
handle: '',
did: 'test did',
},
online: false,
attemptingConnect: false,
rootStore: {} as RootStoreModel,
hasSession: true,
clear: jest.fn(),
setState: jest.fn(),
setOnline: jest.fn(),
updateAuthTokens: jest.fn(),
connect: jest.fn(),
describeService: jest.fn().mockResolvedValue({
availableUserDomains: ['test'],
links: {
termsOfService: 'https://testTermsOfService',
privacyPolicy: 'https://testPrivacyPolicy',
},
}),
login: jest.fn(),
createAccount: jest.fn(),
logout: jest.fn(),
// unknown required because of the missing private methods: _connectPromise, configureApi & _connect
} as unknown as SessionModel
export const mockedNavigationTabStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
id: 0,
history: [
{
url: '',
ts: 0,
title: '',
id: 0,
},
],
index: 0,
isNewTab: false,
current: {
url: '',
ts: 0,
title: '',
id: 0,
},
canGoBack: false,
canGoForward: false,
backTen: [
{
url: '',
title: '',
index: 0,
id: 0,
},
],
forwardTen: [
{
url: '',
title: '',
index: 0,
id: 0,
},
],
navigate: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
goBack: jest.fn(),
fixedTabReset: jest.fn(),
goForward: jest.fn(),
goToIndex: jest.fn(),
setTitle: jest.fn(),
setIsNewTab: jest.fn(),
fixedTabPurpose: 0,
getBackList: () => [
{
url: '/',
title: '',
index: 1,
id: 1,
},
],
getForwardList: jest.fn(),
} as NavigationTabModel
export const mockedNavigationStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
tabs: [mockedNavigationTabStore],
tabIndex: 0,
clear: jest.fn(),
tab: mockedNavigationTabStore,
tabCount: 1,
isCurrentScreen: jest.fn(),
navigate: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
setTitle: jest.fn(),
handleLink: jest.fn(),
switchTo: jest.fn(),
setActiveTab: jest.fn(),
closeTab: jest.fn(),
newTab: jest.fn(),
} as NavigationModel
export const mockedShellStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
minimalShellMode: false,
isMainMenuOpen: false,
isModalActive: false,
activeModal: undefined,
isLightboxActive: false,
activeLightbox: undefined,
isComposerActive: false,
composerOpts: undefined,
darkMode: false,
setDarkMode: jest.fn(),
setMainMenuOpen: jest.fn(),
setMinimalShellMode: jest.fn(),
openModal: jest.fn(),
closeModal: jest.fn(),
closeComposer: jest.fn(),
closeLightbox: jest.fn(),
openComposer: jest.fn(),
openLightbox: jest.fn(),
} as ShellUiModel
export const mockedMeStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
did: 'test did',
handle: 'test',
displayName: 'test',
description: 'test',
avatar: '',
notificationCount: 0,
rootStore: {} as RootStoreModel,
memberships: mockedMembershipsStore,
mainFeed: mockedFeedStore,
notifications: mockedNotificationsStore,
clear: jest.fn(),
load: jest.fn(),
clearNotificationCount: jest.fn(),
fetchNotifications: jest.fn(),
refreshMemberships: jest.fn(),
} as MeModel
export const mockedOnboardStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
isOnboarding: false,
stage: '',
start: jest.fn(),
stop: jest.fn(),
next: jest.fn(),
} as OnboardModel
export const mockedProfilesStore = {
hydrate: jest.fn(),
serialize: jest.fn(),
cache: new LRUMap(100),
rootStore: {} as RootStoreModel,
getProfile: jest.fn().mockResolvedValue({data: {}}),
overwrite: jest.fn(),
} as ProfilesViewModel
export const mockedLinkMetasStore = {
hydrate: jest.fn(),
serialize: jest.fn(),
cache: new LRUMap(100),
rootStore: {} as RootStoreModel,
getLinkMeta: jest.fn(),
} as LinkMetasViewModel
export const mockedLogStore = {
entries: [],
serialize: jest.fn(),
hydrate: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
// unknown required because of the missing private methods: add
} as unknown as LogModel
export const mockedRootStore = {
api: {
com: {},
app: {
bsky: {
actor: {
searchTypeahead: jest.fn().mockResolvedValue({data: {users: []}}),
},
graph: {
getFollows: jest.fn().mockResolvedValue({data: {follows: []}}),
getFollowers: jest.fn().mockResolvedValue({}),
getMembers: jest.fn().mockResolvedValue({}),
},
},
},
} as unknown as SessionServiceClient,
resolveName: jest.fn(),
serialize: jest.fn(),
hydrate: jest.fn(),
fetchStateUpdate: jest.fn(),
clearAll: jest.fn(),
session: mockedSessionStore,
nav: mockedNavigationStore,
shell: mockedShellStore,
me: mockedMeStore,
onboard: mockedOnboardStore,
profiles: mockedProfilesStore,
linkMetas: mockedLinkMetasStore,
log: mockedLogStore,
} as RootStoreModel
export const mockedProfileUiStore = {
profile: mockedProfileStore,
feed: mockedFeedStore,
memberships: mockedMembershipsStore,
members: mockedMembersStore,
selectedViewIndex: 0,
rootStore: mockedRootStore,
params: {
user: 'test user',
},
currentView: mockedFeedStore,
isInitialLoading: false,
isRefreshing: false,
isUser: true,
isScene: false,
selectorItems: [Sections.Posts, Sections.PostsWithReplies, Sections.Scenes],
selectedView: Sections.Posts,
setSelectedViewIndex: jest.fn(),
setup: jest.fn().mockResolvedValue({aborted: false}),
update: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
} as ProfileUiModel
export const mockedAutocompleteViewStore = {
isLoading: false,
isActive: true,
prefix: '',
follows: [
{
did: 'test did',
declaration: {
cid: '',
actorType: 'app.bsky.system.actorUser',
},
handle: '',
displayName: '',
createdAt: '',
indexedAt: '',
},
],
searchRes: [
{
did: 'test did',
declaration: {
cid: '',
actorType: 'app.bsky.system.actorUser',
},
handle: '',
displayName: '',
},
],
knownHandles: new Set<string>(),
suggestions: [
{
handle: 'handle.test',
displayName: 'Test Display',
},
{
handle: 'handle2.test',
displayName: 'Test Display 2',
},
],
rootStore: {} as RootStoreModel,
setup: jest.fn(),
setActive: jest.fn(),
setPrefix: jest.fn(),
// unknown required because of the missing private methods: _searchPromise, _getFollows , _search
} as unknown as UserAutocompleteViewModel
export const mockedLocalPhotosStore = {
photos: {
node: {
type: '',
group_name: '',
image: {
filename: '',
extension: '',
uri: '',
height: 1000,
width: 1000,
fileSize: null,
playableDuration: 0,
},
timestamp: 1672847197,
location: null,
},
},
rootStore: {} as RootStoreModel,
setup: jest.fn(),
// unknown required because of the missing private methods: _getPhotos
} as unknown as UserLocalPhotosModel
export const mockedSuggestedActorsStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: false,
error: '',
suggestions: [
{
did: '1',
declaration: {
cid: '',
actorType: 'app.bsky.system.actorUser',
},
handle: 'handle1.test',
displayName: 'test name 1',
description: 'desc',
indexedAt: '',
_reactKey: '1',
},
{
did: '2',
declaration: {
cid: '',
actorType: 'app.bsky.system.actorUser',
},
handle: '',
displayName: 'handle2.test',
description: 'desc',
indexedAt: '',
_reactKey: '2',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
setup: jest.fn().mockResolvedValue(null),
refresh: jest.fn().mockResolvedValue({}),
// unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _appendAll, _append
} as unknown as SuggestedActorsViewModel
export const mockedUserFollowersStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: false,
error: '',
params: {
user: 'test user',
},
subject: {
did: 'test did',
handle: '',
declaration: {cid: '', actorType: ''},
},
followers: [
{
did: 'test did',
declaration: {cid: '', actorType: ''},
handle: 'testhandle',
displayName: 'test name',
indexedAt: '',
_reactKey: '1',
},
{
did: 'test did2',
declaration: {cid: '', actorType: ''},
handle: 'testhandle2',
displayName: 'test name 2',
indexedAt: '',
_reactKey: '2',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
setup: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
// unknown required because of the missing private methods: _xIdle, _xLoading, _fetch, _replaceAll, _append
} as unknown as UserFollowersViewModel
export const mockedUserFollowsStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: false,
error: '',
params: {
user: 'test user',
},
subject: {
did: 'test did',
handle: '',
declaration: {cid: '', actorType: ''},
},
follows: [
{
did: 'test did',
declaration: {cid: '', actorType: ''},
handle: 'testhandle',
displayName: 'test name',
indexedAt: '',
_reactKey: '1',
},
{
did: 'test did2',
declaration: {cid: '', actorType: ''},
handle: 'testhandle2',
displayName: 'test name 2',
indexedAt: '',
_reactKey: '2',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
setup: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
// unknown required because of the missing private methods: _xIdle, _xLoading, _fetch, _replaceAll, _append
} as unknown as UserFollowsViewModel
export const mockedRepostedByViewStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: false,
error: '',
resolvedUri: '',
params: {
uri: 'testuri',
},
uri: '',
repostedBy: [
{
_reactKey: '',
did: '',
handle: '',
displayName: '',
declaration: {cid: '', actorType: ''},
indexedAt: '',
},
],
hasContent: false,
hasError: false,
isEmpty: false,
setup: jest.fn().mockResolvedValue({}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn().mockResolvedValue({}),
// unknown required because of the missing private methods: _xIdle, _xLoading, _resolveUri, _fetch, _refresh, _replaceAll, _append
} as unknown as RepostedByViewModel
export const mockedVotesViewStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: false,
error: '',
resolvedUri: '',
params: {
uri: 'testuri',
},
uri: '',
votes: [
{
_reactKey: '',
direction: 'up',
indexedAt: '',
createdAt: '',
actor: {
did: '',
handle: '',
declaration: {cid: '', actorType: ''},
},
},
],
hasContent: false,
hasError: false,
isEmpty: false,
setup: jest.fn(),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn().mockResolvedValue({}),
// unknown required because of the missing private methods: _xIdle, _xLoading, _resolveUri, _fetch, _replaceAll, _append
} as unknown as VotesViewModel
+16
View File
@@ -0,0 +1,16 @@
// /**
// * @format
// */
// import 'react-native'
// import React from 'react'
// import App from '../src/App'
// // Note: test renderer must be required after react-native.
// import renderer from 'react-test-renderer'
// it('renders correctly', () => {
// renderer.act(() => {
// renderer.create(<App />)
// })
// })
-257
View File
@@ -1,257 +0,0 @@
import React from 'react'
import {MobileShell} from '../src/view/shell/mobile'
import {cleanup, fireEvent, render, waitFor} from '../jest/test-utils'
import {createServer, TestPDS} from '../jest/test-pds'
import {RootStoreModel, setupState} from '../src/state'
const WAIT_OPTS = {timeout: 5e3}
describe('Account flows', () => {
let pds: TestPDS | undefined
let rootStore: RootStoreModel | undefined
beforeAll(async () => {
jest.useFakeTimers()
pds = await createServer()
rootStore = await setupState(pds.pdsUrl)
})
afterAll(async () => {
jest.clearAllMocks()
cleanup()
await pds?.close()
})
it('renders initial screen', () => {
const {getByTestId} = render(<MobileShell />, rootStore)
const signUpScreen = getByTestId('signinOrCreateAccount')
expect(signUpScreen).toBeTruthy()
})
it('completes signin to the server', async () => {
const {getByTestId} = render(<MobileShell />, rootStore)
// move to signin view
fireEvent.press(getByTestId('signInButton'))
expect(getByTestId('signIn')).toBeTruthy()
expect(getByTestId('loginForm')).toBeTruthy()
// input the target server
expect(getByTestId('loginSelectServiceButton')).toBeTruthy()
fireEvent.press(getByTestId('loginSelectServiceButton'))
expect(getByTestId('serverInputModal')).toBeTruthy()
fireEvent.changeText(
getByTestId('customServerTextInput'),
pds?.pdsUrl || '',
)
fireEvent.press(getByTestId('customServerSelectBtn'))
await waitFor(() => {
expect(getByTestId('loginUsernameInput')).toBeTruthy()
}, WAIT_OPTS)
// enter username & pass
fireEvent.changeText(getByTestId('loginUsernameInput'), 'alice')
fireEvent.changeText(getByTestId('loginPasswordInput'), 'hunter2')
await waitFor(() => {
expect(getByTestId('loginNextButton')).toBeTruthy()
}, WAIT_OPTS)
fireEvent.press(getByTestId('loginNextButton'))
// signed in
await waitFor(() => {
expect(getByTestId('homeFeed')).toBeTruthy()
expect(rootStore?.me?.displayName).toBe('Alice')
expect(rootStore?.me?.handle).toBe('alice.test')
expect(rootStore?.session.accounts.length).toBe(1)
}, WAIT_OPTS)
expect(rootStore?.me?.displayName).toBe('Alice')
expect(rootStore?.me?.handle).toBe('alice.test')
expect(rootStore?.session.accounts.length).toBe(1)
})
it('opens the login screen when "add account" is pressed', async () => {
const {getByTestId, getAllByTestId} = render(<MobileShell />, rootStore)
await waitFor(() => expect(getByTestId('homeFeed')).toBeTruthy(), WAIT_OPTS)
// open side menu
fireEvent.press(getAllByTestId('viewHeaderBackOrMenuBtn')[0])
await waitFor(() => expect(getByTestId('menuView')).toBeTruthy(), WAIT_OPTS)
// nav to settings
fireEvent.press(getByTestId('menuItemButton-Settings'))
await waitFor(
() => expect(getByTestId('settingsScreen')).toBeTruthy(),
WAIT_OPTS,
)
// press '+ new account' in switcher
fireEvent.press(getByTestId('switchToNewAccountBtn'))
await waitFor(
() => expect(getByTestId('signinOrCreateAccount')).toBeTruthy(),
WAIT_OPTS,
)
})
it('shows the "choose account" form when a previous session has been created', async () => {
const {getByTestId} = render(<MobileShell />, rootStore)
// move to signin view
fireEvent.press(getByTestId('signInButton'))
expect(getByTestId('signIn')).toBeTruthy()
expect(getByTestId('chooseAccountForm')).toBeTruthy()
})
it('logs directly into the account due to still possessing session tokens', async () => {
const {getByTestId} = render(<MobileShell />, rootStore)
// move to signin view
fireEvent.press(getByTestId('signInButton'))
expect(getByTestId('signIn')).toBeTruthy()
expect(getByTestId('chooseAccountForm')).toBeTruthy()
// select the previous account
fireEvent.press(getByTestId('chooseAccountBtn-alice.test'))
// signs in immediately
await waitFor(() => {
expect(getByTestId('homeFeed')).toBeTruthy()
expect(rootStore?.me?.displayName).toBe('Alice')
expect(rootStore?.me?.handle).toBe('alice.test')
expect(rootStore?.session.accounts.length).toBe(1)
}, WAIT_OPTS)
expect(rootStore?.me?.displayName).toBe('Alice')
expect(rootStore?.me?.handle).toBe('alice.test')
expect(rootStore?.session.accounts.length).toBe(1)
})
it('logs into a second account via the switcher', async () => {
const {getByTestId, getAllByTestId} = render(<MobileShell />, rootStore)
await waitFor(() => expect(getByTestId('homeFeed')).toBeTruthy(), WAIT_OPTS)
// open side menu
fireEvent.press(getAllByTestId('viewHeaderBackOrMenuBtn')[0])
await waitFor(() => expect(getByTestId('menuView')).toBeTruthy(), WAIT_OPTS)
// nav to settings
fireEvent.press(getByTestId('menuItemButton-Settings'))
await waitFor(
() => expect(getByTestId('settingsScreen')).toBeTruthy(),
WAIT_OPTS,
)
// press '+ new account' in switcher
fireEvent.press(getByTestId('switchToNewAccountBtn'))
await waitFor(
() => expect(getByTestId('signinOrCreateAccount')).toBeTruthy(),
WAIT_OPTS,
)
// move to signin view
fireEvent.press(getByTestId('signInButton'))
expect(getByTestId('signIn')).toBeTruthy()
expect(getByTestId('chooseAccountForm')).toBeTruthy()
// select a new account
fireEvent.press(getByTestId('chooseNewAccountBtn'))
expect(getByTestId('loginForm')).toBeTruthy()
// input the target server
expect(getByTestId('loginSelectServiceButton')).toBeTruthy()
fireEvent.press(getByTestId('loginSelectServiceButton'))
expect(getByTestId('serverInputModal')).toBeTruthy()
fireEvent.changeText(
getByTestId('customServerTextInput'),
pds?.pdsUrl || '',
)
fireEvent.press(getByTestId('customServerSelectBtn'))
await waitFor(
() => expect(getByTestId('loginUsernameInput')).toBeTruthy(),
WAIT_OPTS,
)
// enter username & pass
fireEvent.changeText(getByTestId('loginUsernameInput'), 'bob')
fireEvent.changeText(getByTestId('loginPasswordInput'), 'hunter2')
await waitFor(
() => expect(getByTestId('loginNextButton')).toBeTruthy(),
WAIT_OPTS,
)
fireEvent.press(getByTestId('loginNextButton'))
// signed in
await waitFor(() => {
expect(getByTestId('homeFeed')).toBeTruthy() // we go back to settings in this situation
expect(rootStore?.me?.displayName).toBe('Bob')
expect(rootStore?.me?.handle).toBe('bob.test')
expect(rootStore?.session.accounts.length).toBe(2)
}, WAIT_OPTS)
expect(rootStore?.me?.displayName).toBe('Bob')
expect(rootStore?.me?.handle).toBe('bob.test')
expect(rootStore?.session.accounts.length).toBe(2)
})
it('can instantly switch between accounts', async () => {
const {getAllByTestId, getByTestId} = render(<MobileShell />, rootStore)
await waitFor(() => expect(getByTestId('homeFeed')).toBeTruthy(), WAIT_OPTS)
// open side menu
fireEvent.press(getAllByTestId('viewHeaderBackOrMenuBtn')[0])
await waitFor(() => expect(getByTestId('menuView')).toBeTruthy(), WAIT_OPTS)
// nav to settings
fireEvent.press(getByTestId('menuItemButton-Settings'))
await waitFor(
() => expect(getByTestId('settingsScreen')).toBeTruthy(),
WAIT_OPTS,
)
// select the alice account
fireEvent.press(getByTestId('switchToAccountBtn-alice.test'))
// swapped account
await waitFor(() => {
expect(rootStore?.me?.displayName).toBe('Alice')
expect(rootStore?.me?.handle).toBe('alice.test')
expect(rootStore?.session.accounts.length).toBe(2)
}, WAIT_OPTS)
expect(rootStore?.me?.displayName).toBe('Alice')
expect(rootStore?.me?.handle).toBe('alice.test')
expect(rootStore?.session.accounts.length).toBe(2)
})
it('will prompt for a password if you sign out', async () => {
const {getAllByTestId, getByTestId} = render(<MobileShell />, rootStore)
await waitFor(() => expect(getByTestId('homeFeed')).toBeTruthy(), WAIT_OPTS)
// open side menu
fireEvent.press(getAllByTestId('viewHeaderBackOrMenuBtn')[0])
await waitFor(() => expect(getByTestId('menuView')).toBeTruthy(), WAIT_OPTS)
// nav to settings
fireEvent.press(getByTestId('menuItemButton-Settings'))
await waitFor(
() => expect(getByTestId('settingsScreen')).toBeTruthy(),
WAIT_OPTS,
)
// press the sign out button
fireEvent.press(getByTestId('signOutBtn'))
// in the logged out state
await waitFor(
() => expect(getByTestId('signinOrCreateAccount')).toBeTruthy(),
WAIT_OPTS,
)
// move to signin view
fireEvent.press(getByTestId('signInButton'))
expect(getByTestId('signIn')).toBeTruthy()
expect(getByTestId('chooseAccountForm')).toBeTruthy()
// select an existing account
fireEvent.press(getByTestId('chooseAccountBtn-alice.test'))
// goes to login screen instead of straight back to settings
expect(getByTestId('loginForm')).toBeTruthy()
})
})
-47
View File
@@ -1,47 +0,0 @@
export const exampleComHtml = `<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta name="description" content="An example website">
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 2em;
background-color: #fdfdff;
border-radius: 0.5em;
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
div {
margin: 0 auto;
width: auto;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>`
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-19
View File
@@ -1,19 +0,0 @@
import {isNetworkError} from '../../src/lib/errors'
describe('isNetworkError', () => {
const inputs = [
'TypeError: Network request failed',
'Uncaught TypeError: Cannot read property x of undefined',
'Uncaught RangeError',
'Error: Aborted',
]
const outputs = [true, false, false, true]
it('correctly distinguishes network errors', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const result = isNetworkError(input)
expect(result).toEqual(outputs[i])
}
})
})
-116
View File
@@ -1,116 +0,0 @@
import {extractHtmlMeta} from '../../src/lib/extractHtmlMeta'
import {exampleComHtml} from './__mocks__/exampleComHtml'
import {youtubeHTML} from './__mocks__/youtubeHtml'
import {tiktokHtml} from './__mocks__/tiktokHtml'
describe('extractHtmlMeta', () => {
const cases = [
['', {}],
['nothing', {}],
['<title>title</title>', {title: 'title'}],
['<title> aSd!@#AC </title>', {title: 'aSd!@#AC'}],
['<title>\n title\n </title>', {title: 'title'}],
['<meta name="title" content="meta title">', {title: 'meta title'}],
[
'<meta name="description" content="meta description">',
{description: 'meta description'},
],
['<meta property="og:title" content="og title">', {title: 'og title'}],
[
'<meta property="og:description" content="og description">',
{description: 'og description'},
],
[
'<meta property="og:image" content="https://ogimage.com/foo.png">',
{image: 'https://ogimage.com/foo.png'},
],
[
'<meta property="twitter:title" content="twitter title">',
{title: 'twitter title'},
],
[
'<meta property="twitter:description" content="twitter description">',
{description: 'twitter description'},
],
[
'<meta property="twitter:image" content="https://twitterimage.com/foo.png">',
{image: 'https://twitterimage.com/foo.png'},
],
['<meta\n name="title"\n content="meta title"\n>', {title: 'meta title'}],
]
it.each(cases)(
'given the html tag %p, returns %p',
(input, expectedResult) => {
const output = extractHtmlMeta({html: input as string, hostname: ''})
expect(output).toEqual(expectedResult)
},
)
it('extracts title and description from a generic HTML page', () => {
const input = exampleComHtml
const expectedOutput = {
title: 'Example Domain',
description: 'An example website',
}
const output = extractHtmlMeta({html: input, hostname: 'example.com'})
expect(output).toEqual(expectedOutput)
})
it('extracts title and description from a Tiktok HTML page', () => {
const input = tiktokHtml
const expectedOutput = {
title:
'Coca-Cola and Mentos! Super Reaction! #cocacola #mentos #reaction #bal... | TikTok',
description:
'5.5M Likes, 20.8K Comments. TikTok video from Power Vision Tests (@_powervision_): &quot;Coca-Cola and Mentos! Super Reaction! #cocacola #mentos #reaction #balloon #sciencemoment #scienceexperiment #experiment #test #amazing #pvexp&quot;. оригинальный звук - Power Vision Tests.',
}
const output = extractHtmlMeta({html: input, hostname: 'tiktok.com'})
expect(output).toEqual(expectedOutput)
})
it('extracts title and description from a generic youtube page', () => {
const input = youtubeHTML
const expectedOutput = {
title: 'HD Video (1080p) with Relaxing Music of Native American Shamans',
description:
'Stunning HD Video ( 1080p ) of Patagonian Nature with Relaxing Native American Shamanic Music. HD footage used from ',
image: 'https://i.ytimg.com/vi/x6UITRjhijI/sddefault.jpg',
}
const output = extractHtmlMeta({html: input, hostname: 'youtube.com'})
expect(output).toEqual(expectedOutput)
})
it('extracts username from the url a twitter profile page', () => {
const expectedOutput = {
title: '@bluesky on Twitter',
}
const output = extractHtmlMeta({
hostname: 'twitter.com',
pathname: '/bluesky',
})
expect(output).toEqual(expectedOutput)
})
it('extracts username from the url a tweet', () => {
const expectedOutput = {
title: 'Tweet by @bluesky',
}
const output = extractHtmlMeta({
hostname: 'twitter.com',
pathname: '/bluesky/status/1582437529969917953',
})
expect(output).toEqual(expectedOutput)
})
it("does not extract username from the url when it's not a tweet or profile page", () => {
const expectedOutput = {
title: 'Twitter',
}
const output = extractHtmlMeta({
hostname: 'twitter.com',
pathname: '/i/articles/follows/-1675653703?time_window=24',
})
expect(output).toEqual(expectedOutput)
})
})
-93
View File
@@ -1,93 +0,0 @@
import {downloadAndResize, DownloadAndResizeOpts} from '../../src/lib/images'
import ImageResizer from '@bam.tech/react-native-image-resizer'
import RNFetchBlob from 'rn-fetch-blob'
describe('downloadAndResize', () => {
const errorSpy = jest.spyOn(global.console, 'error')
const mockResizedImage = {
path: jest.fn().mockReturnValue('file://resized-image.jpg'),
size: 100,
width: 50,
height: 50,
mime: 'image/jpeg',
}
beforeEach(() => {
const mockedCreateResizedImage =
ImageResizer.createResizedImage as jest.Mock
mockedCreateResizedImage.mockResolvedValue(mockResizedImage)
})
afterEach(() => {
jest.clearAllMocks()
})
it('should return resized image for valid URI and options', async () => {
const mockedFetch = RNFetchBlob.fetch as jest.Mock
mockedFetch.mockResolvedValueOnce({
path: jest.fn().mockReturnValue('file://downloaded-image.jpg'),
flush: jest.fn(),
})
const opts: DownloadAndResizeOpts = {
uri: 'https://example.com/image.jpg',
width: 100,
height: 100,
maxSize: 500000,
mode: 'cover',
timeout: 10000,
}
const result = await downloadAndResize(opts)
expect(result).toEqual(mockResizedImage)
expect(RNFetchBlob.config).toHaveBeenCalledWith({
fileCache: true,
appendExt: 'jpeg',
})
expect(RNFetchBlob.fetch).toHaveBeenCalledWith(
'GET',
'https://example.com/image.jpg',
)
expect(ImageResizer.createResizedImage).toHaveBeenCalledWith(
'file://downloaded-image.jpg',
100,
100,
'JPEG',
100,
undefined,
undefined,
undefined,
{mode: 'cover'},
)
})
it('should return undefined for invalid URI', async () => {
const opts: DownloadAndResizeOpts = {
uri: 'invalid-uri',
width: 100,
height: 100,
maxSize: 500000,
mode: 'cover',
timeout: 10000,
}
const result = await downloadAndResize(opts)
expect(errorSpy).toHaveBeenCalled()
expect(result).toBeUndefined()
})
it('should return undefined for unsupported file type', async () => {
const opts: DownloadAndResizeOpts = {
uri: 'https://example.com/image.bmp',
width: 100,
height: 100,
maxSize: 500000,
mode: 'cover',
timeout: 10000,
}
const result = await downloadAndResize(opts)
expect(result).toBeUndefined()
})
})
-24
View File
@@ -1,24 +0,0 @@
import {clamp} from '../../src/lib/numbers'
describe('clamp', () => {
const inputs: [number, number, number][] = [
[100, 0, 200],
[100, 0, 100],
[0, 0, 100],
[100, 0, -1],
[4, 1, 1],
[100, -100, 0],
[400, 100, -100],
[70, -1, 1],
[Infinity, Infinity, Infinity],
]
const outputs = [100, 100, 0, -1, 1, 0, -100, 1, Infinity]
it('correctly clamps any given number and range', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const result = clamp(...input)
expect(result).toEqual(outputs[i])
}
})
})
@@ -1,6 +1,52 @@
import {LikelyType, getLinkMeta, getLikelyType} from '../../src/lib/link-meta'
import {exampleComHtml} from './__mocks__/exampleComHtml'
import {mockedRootStore} from '../../__mocks__/state-mock'
import {LikelyType, getLinkMeta} from '../src/lib/link-meta'
const exampleComHtml = `<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta name="description" content="An example website">
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 2em;
background-color: #fdfdff;
border-radius: 0.5em;
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
div {
margin: 0 auto;
width: auto;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>`
describe('getLinkMeta', () => {
const inputs = [
@@ -11,10 +57,7 @@ describe('getLinkMeta', () => {
'https://example.com/image.png',
'https://example.com/video.avi',
'https://example.com/audio.ogg',
'https://example.com/text.txt',
'https://example.com/javascript.js',
'https://bsky.app/',
'https://bsky.app/index.html',
]
const outputs = [
{
@@ -51,35 +94,15 @@ describe('getLinkMeta', () => {
likelyType: LikelyType.Audio,
url: 'https://example.com/audio.ogg',
},
{
likelyType: LikelyType.Text,
url: 'https://example.com/text.txt',
},
{
likelyType: LikelyType.Other,
url: 'https://example.com/javascript.js',
},
{
likelyType: LikelyType.AtpData,
url: '/',
title: 'Bluesky',
description: 'A new kind of social network',
},
{
likelyType: LikelyType.AtpData,
url: '/index.html',
title: 'Not found',
},
{
likelyType: LikelyType.Other,
url: '',
title: '',
},
]
it('correctly handles a set of text inputs', async () => {
for (let i = 0; i < inputs.length; i++) {
global.fetch = jest.fn().mockImplementationOnce(() => {
return new Promise((resolve, _reject) => {
return new Promise((resolve, reject) => {
resolve({
ok: true,
status: 200,
@@ -88,20 +111,8 @@ describe('getLinkMeta', () => {
})
})
const input = inputs[i]
const output = await getLinkMeta(mockedRootStore, input)
const output = await getLinkMeta(input)
expect(output).toEqual(outputs[i])
}
})
})
describe('getLikelyType', () => {
it('correctly handles non-parsed url', async () => {
const output = await getLikelyType('https://example.com')
expect(output).toEqual(LikelyType.HTML)
})
it('handles non-string urls without crashing', async () => {
const output = await getLikelyType('123')
expect(output).toEqual(LikelyType.Other)
})
})
@@ -1,72 +0,0 @@
import {RootStoreModel} from '../../../src/state/models/root-store'
import {LinkMetasViewModel} from '../../../src/state/models/link-metas-view'
import * as LinkMetaLib from '../../../src/lib/link-meta'
import {LikelyType} from './../../../src/lib/link-meta'
import {sessionClient, SessionServiceClient} from '@atproto/api'
import {DEFAULT_SERVICE} from '../../../src/state'
describe('LinkMetasViewModel', () => {
let viewModel: LinkMetasViewModel
let rootStore: RootStoreModel
const getLinkMetaMockSpy = jest.spyOn(LinkMetaLib, 'getLinkMeta')
const mockedMeta = {
title: 'Test Title',
url: 'testurl',
likelyType: LikelyType.Other,
}
beforeEach(() => {
const api = sessionClient.service(DEFAULT_SERVICE) as SessionServiceClient
rootStore = new RootStoreModel(api)
viewModel = new LinkMetasViewModel(rootStore)
})
afterAll(() => {
jest.clearAllMocks()
})
describe('getLinkMeta', () => {
it('should return link meta if it is cached', async () => {
const url = 'http://example.com'
viewModel.cache.set(url, mockedMeta)
const result = await viewModel.getLinkMeta(url)
expect(getLinkMetaMockSpy).not.toHaveBeenCalled()
expect(result).toEqual(mockedMeta)
})
it('should return link meta if it is not cached', async () => {
getLinkMetaMockSpy.mockResolvedValueOnce(mockedMeta)
const result = await viewModel.getLinkMeta(mockedMeta.url)
expect(getLinkMetaMockSpy).toHaveBeenCalledWith(rootStore, mockedMeta.url)
expect(result).toEqual(mockedMeta)
})
it('should cache the link meta if it is successfully returned', async () => {
getLinkMetaMockSpy.mockResolvedValueOnce(mockedMeta)
await viewModel.getLinkMeta(mockedMeta.url)
expect(viewModel.cache.get(mockedMeta.url)).toEqual(mockedMeta)
})
it('should not cache the link meta if it fails to return', async () => {
const url = 'http://example.com'
const error = new Error('Failed to fetch link meta')
getLinkMetaMockSpy.mockRejectedValueOnce(error)
try {
await viewModel.getLinkMeta(url)
fail('Error was not thrown')
} catch (e) {
expect(e).toEqual(error)
expect(viewModel.cache.get(url)).toBeUndefined()
}
})
})
})
-153
View File
@@ -1,153 +0,0 @@
import {LogModel} from '../../../src/state/models/log'
describe('LogModel', () => {
let logModel: LogModel
beforeEach(() => {
logModel = new LogModel()
jest.spyOn(console, 'debug')
})
afterAll(() => {
jest.clearAllMocks()
})
it('should call a log method and add a log entry to the entries array', () => {
logModel.debug('Test log')
expect(logModel.entries.length).toEqual(1)
expect(logModel.entries[0]).toEqual({
id: logModel.entries[0].id,
type: 'debug',
summary: 'Test log',
details: undefined,
ts: logModel.entries[0].ts,
})
logModel.warn('Test log')
expect(logModel.entries.length).toEqual(2)
expect(logModel.entries[1]).toEqual({
id: logModel.entries[1].id,
type: 'warn',
summary: 'Test log',
details: undefined,
ts: logModel.entries[1].ts,
})
logModel.error('Test log')
expect(logModel.entries.length).toEqual(3)
expect(logModel.entries[2]).toEqual({
id: logModel.entries[2].id,
type: 'error',
summary: 'Test log',
details: undefined,
ts: logModel.entries[2].ts,
})
})
it('should call the console.debug after calling the debug method', () => {
logModel.debug('Test log')
expect(console.debug).toHaveBeenCalledWith('Test log', '')
})
it('should call the serialize method', () => {
logModel.debug('Test log')
expect(logModel.serialize()).toEqual({
entries: [
{
id: logModel.entries[0].id,
type: 'debug',
summary: 'Test log',
details: undefined,
ts: logModel.entries[0].ts,
},
],
})
})
it('should call the hydrate method with valid properties', () => {
logModel.hydrate({
entries: [
{
id: '123',
type: 'debug',
summary: 'Test log',
details: undefined,
ts: 123,
},
],
})
expect(logModel.entries).toEqual([
{
id: '123',
type: 'debug',
summary: 'Test log',
details: undefined,
ts: 123,
},
])
})
it('should call the hydrate method with invalid properties', () => {
logModel.hydrate({
entries: [
{
id: '123',
type: 'debug',
summary: 'Test log',
details: undefined,
ts: 123,
},
{
summary: 'Invalid entry',
},
],
})
expect(logModel.entries).toEqual([
{
id: '123',
type: 'debug',
summary: 'Test log',
details: undefined,
ts: 123,
},
])
})
it('should stringify the details if it is not a string', () => {
logModel.debug('Test log', {details: 'test'})
expect(logModel.entries[0].details).toEqual('{\n "details": "test"\n}')
})
it('should stringify the details object if it is of a specific error', () => {
class TestError extends Error {
constructor() {
super()
this.name = 'TestError'
}
}
const error = new TestError()
logModel.error('Test error log', error)
expect(logModel.entries[0].details).toEqual('TestError')
class XRPCInvalidResponseErrorMock {
validationError = {toString: () => 'validationError'}
lexiconNsid = 'test'
}
const xrpcInvalidResponseError = new XRPCInvalidResponseErrorMock()
logModel.error('Test error log', xrpcInvalidResponseError)
expect(logModel.entries[1].details).toEqual(
'{\n "validationError": {},\n "lexiconNsid": "test"\n}',
)
class XRPCErrorMock {
status = 'status'
error = 'error'
message = 'message'
}
const xrpcError = new XRPCErrorMock()
logModel.error('Test error log', xrpcError)
expect(logModel.entries[2].details).toEqual(
'{\n "status": "status",\n "error": "error",\n "message": "message"\n}',
)
})
})
-180
View File
@@ -1,180 +0,0 @@
import {RootStoreModel} from '../../../src/state/models/root-store'
import {MeModel} from '../../../src/state/models/me'
import {NotificationsViewModel} from './../../../src/state/models/notifications-view'
import {sessionClient, SessionServiceClient} from '@atproto/api'
import {DEFAULT_SERVICE} from './../../../src/state/index'
describe('MeModel', () => {
let rootStore: RootStoreModel
let meModel: MeModel
beforeEach(() => {
const api = sessionClient.service(DEFAULT_SERVICE) as SessionServiceClient
rootStore = new RootStoreModel(api)
meModel = new MeModel(rootStore)
})
afterAll(() => {
jest.clearAllMocks()
})
it('should clear() correctly', () => {
meModel.did = '123'
meModel.handle = 'handle'
meModel.displayName = 'John Doe'
meModel.description = 'description'
meModel.avatar = 'avatar'
meModel.notificationCount = 1
meModel.clear()
expect(meModel.did).toEqual('')
expect(meModel.handle).toEqual('')
expect(meModel.displayName).toEqual('')
expect(meModel.description).toEqual('')
expect(meModel.avatar).toEqual('')
expect(meModel.notificationCount).toEqual(0)
})
it('should hydrate() successfully with valid properties', () => {
meModel.hydrate({
did: '123',
handle: 'handle',
displayName: 'John Doe',
description: 'description',
avatar: 'avatar',
})
expect(meModel.did).toEqual('123')
expect(meModel.handle).toEqual('handle')
expect(meModel.displayName).toEqual('John Doe')
expect(meModel.description).toEqual('description')
expect(meModel.avatar).toEqual('avatar')
})
it('should not hydrate() with invalid properties', () => {
meModel.hydrate({
did: '',
handle: 'handle',
displayName: 'John Doe',
description: 'description',
avatar: 'avatar',
})
expect(meModel.did).toEqual('')
expect(meModel.handle).toEqual('')
expect(meModel.displayName).toEqual('')
expect(meModel.description).toEqual('')
expect(meModel.avatar).toEqual('')
meModel.hydrate({
did: '123',
displayName: 'John Doe',
description: 'description',
avatar: 'avatar',
})
expect(meModel.did).toEqual('')
expect(meModel.handle).toEqual('')
expect(meModel.displayName).toEqual('')
expect(meModel.description).toEqual('')
expect(meModel.avatar).toEqual('')
})
it('should load() successfully', async () => {
jest
.spyOn(rootStore.api.app.bsky.actor, 'getProfile')
.mockImplementationOnce((): Promise<any> => {
return Promise.resolve({
data: {
displayName: 'John Doe',
description: 'description',
avatar: 'avatar',
},
})
})
rootStore.session.data = {
did: '123',
handle: 'handle',
service: 'test service',
accessJwt: 'test token',
refreshJwt: 'test token',
}
await meModel.load()
expect(meModel.did).toEqual('123')
expect(meModel.handle).toEqual('handle')
expect(meModel.displayName).toEqual('John Doe')
expect(meModel.description).toEqual('description')
expect(meModel.avatar).toEqual('avatar')
})
it('should load() successfully without profile data', async () => {
jest
.spyOn(rootStore.api.app.bsky.actor, 'getProfile')
.mockImplementationOnce((): Promise<any> => {
return Promise.resolve({
data: null,
})
})
rootStore.session.data = {
did: '123',
handle: 'handle',
service: 'test service',
accessJwt: 'test token',
refreshJwt: 'test token',
}
await meModel.load()
expect(meModel.did).toEqual('123')
expect(meModel.handle).toEqual('handle')
expect(meModel.displayName).toEqual('')
expect(meModel.description).toEqual('')
expect(meModel.avatar).toEqual('')
})
it('should load() to nothing when no session', async () => {
rootStore.session.data = null
await meModel.load()
expect(meModel.did).toEqual('')
expect(meModel.handle).toEqual('')
expect(meModel.displayName).toEqual('')
expect(meModel.description).toEqual('')
expect(meModel.avatar).toEqual('')
expect(meModel.notificationCount).toEqual(0)
})
it('should serialize() key information', () => {
meModel.did = '123'
meModel.handle = 'handle'
meModel.displayName = 'John Doe'
meModel.description = 'description'
meModel.avatar = 'avatar'
expect(meModel.serialize()).toEqual({
did: '123',
handle: 'handle',
displayName: 'John Doe',
description: 'description',
avatar: 'avatar',
})
})
it('should clearNotificationCount() successfully', () => {
meModel.clearNotificationCount()
expect(meModel.notificationCount).toBe(0)
})
it('should update notifs count with fetchStateUpdate()', async () => {
meModel.notifications = {
refresh: jest.fn().mockResolvedValue({}),
} as unknown as NotificationsViewModel
jest
.spyOn(rootStore.api.app.bsky.notification, 'getCount')
.mockImplementationOnce((): Promise<any> => {
return Promise.resolve({
data: {
count: 1,
},
})
})
await meModel.fetchNotifications()
expect(meModel.notificationCount).toBe(1)
expect(meModel.notifications.refresh).toHaveBeenCalled()
})
})
-151
View File
@@ -1,151 +0,0 @@
import {NavigationModel} from './../../../src/state/models/navigation'
import * as flags from '../../../src/build-flags'
describe('NavigationModel', () => {
let model: NavigationModel
beforeEach(() => {
model = new NavigationModel()
model.setTitle('0-0', 'title')
})
afterAll(() => {
jest.clearAllMocks()
})
it('should clear() to the correct base state', async () => {
await model.clear()
expect(model.tabCount).toBe(2)
expect(model.tab).toEqual({
fixedTabPurpose: 0,
history: [
{
id: expect.anything(),
ts: expect.anything(),
url: '/',
},
],
id: expect.anything(),
index: 0,
isNewTab: false,
})
})
it('should call the navigate method', async () => {
const navigateSpy = jest.spyOn(model.tab, 'navigate')
await model.navigate('testurl', 'teststring')
expect(navigateSpy).toHaveBeenCalledWith('testurl', 'teststring')
})
it('should call the refresh method', async () => {
const refreshSpy = jest.spyOn(model.tab, 'refresh')
await model.refresh()
expect(refreshSpy).toHaveBeenCalled()
})
it('should call the isCurrentScreen method', () => {
expect(model.isCurrentScreen('11', 0)).toEqual(false)
})
it('should call the tab getter', () => {
expect(model.tab).toEqual({
fixedTabPurpose: 0,
history: [
{
id: expect.anything(),
ts: expect.anything(),
url: '/',
},
],
id: expect.anything(),
index: 0,
isNewTab: false,
})
})
it('should call the tabCount getter', () => {
expect(model.tabCount).toBe(2)
})
describe('tabs not enabled', () => {
jest.mock('../../../src/build-flags', () => ({
TABS_ENABLED: false,
}))
afterAll(() => {
jest.clearAllMocks()
})
it('should not create new tabs', () => {
// @ts-expect-error
flags.TABS_ENABLED = false
model.newTab('testurl')
expect(model.tab.isNewTab).toBe(false)
expect(model.tabIndex).toBe(0)
})
it('should not change the active tab', () => {
// @ts-expect-error
flags.TABS_ENABLED = false
model.setActiveTab(2)
expect(model.tabIndex).toBe(0)
})
it('should note close tabs', () => {
// @ts-expect-error
flags.TABS_ENABLED = false
model.closeTab(0)
expect(model.tabCount).toBe(2)
})
})
describe('tabs enabled', () => {
jest.mock('../../../src/build-flags', () => ({
TABS_ENABLED: true,
}))
afterAll(() => {
jest.clearAllMocks()
})
it('should create new tabs', () => {
// @ts-expect-error
flags.TABS_ENABLED = true
model.newTab('testurl', 'title')
expect(model.tab.isNewTab).toBe(true)
expect(model.tabIndex).toBe(2)
})
it('should change the current tab', () => {
// @ts-expect-error
flags.TABS_ENABLED = true
model.setActiveTab(0)
expect(model.tabIndex).toBe(0)
})
it('should close tabs', () => {
// @ts-expect-error
flags.TABS_ENABLED = true
model.closeTab(0)
expect(model.tabs).toEqual([
{
fixedTabPurpose: 1,
history: [
{
id: expect.anything(),
ts: expect.anything(),
url: '/notifications',
},
],
id: expect.anything(),
index: 0,
isNewTab: false,
},
])
expect(model.tabIndex).toBe(0)
})
})
})
-46
View File
@@ -1,46 +0,0 @@
import {
OnboardModel,
OnboardStageOrder,
} from '../../../src/state/models/onboard'
describe('OnboardModel', () => {
let onboardModel: OnboardModel
beforeEach(() => {
onboardModel = new OnboardModel()
})
afterAll(() => {
jest.clearAllMocks()
})
it('should start/stop correctly', () => {
onboardModel.start()
expect(onboardModel.isOnboarding).toBe(true)
onboardModel.stop()
expect(onboardModel.isOnboarding).toBe(false)
})
it('should call the next method until it has no more stages', () => {
onboardModel.start()
onboardModel.next()
expect(onboardModel.stage).toBe(OnboardStageOrder[1])
onboardModel.next()
expect(onboardModel.isOnboarding).toBe(false)
expect(onboardModel.stage).toBe(OnboardStageOrder[0])
})
it('serialize and hydrate', () => {
const serialized = onboardModel.serialize()
const newModel = new OnboardModel()
newModel.hydrate(serialized)
expect(newModel).toEqual(onboardModel)
onboardModel.start()
onboardModel.next()
const serialized2 = onboardModel.serialize()
newModel.hydrate(serialized2)
expect(newModel).toEqual(onboardModel)
})
})
-59
View File
@@ -1,59 +0,0 @@
import {RootStoreModel} from '../../../src/state/models/root-store'
import {setupState} from '../../../src/state'
describe('rootStore', () => {
let rootStore: RootStoreModel
beforeAll(() => {
jest.useFakeTimers()
})
beforeEach(async () => {
rootStore = await setupState()
})
afterAll(() => {
jest.clearAllMocks()
})
it('should call the clearAll() resets state correctly', () => {
rootStore.clearAll()
expect(rootStore.session.data).toEqual(null)
expect(rootStore.nav.tabs).toEqual([
{
fixedTabPurpose: 0,
history: [
{
id: expect.anything(),
ts: expect.anything(),
url: '/',
},
],
id: expect.anything(),
index: 0,
isNewTab: false,
},
{
fixedTabPurpose: 1,
history: [
{
id: expect.anything(),
ts: expect.anything(),
url: '/notifications',
},
],
id: expect.anything(),
index: 0,
isNewTab: false,
},
])
expect(rootStore.nav.tabIndex).toEqual(0)
expect(rootStore.me.did).toEqual('')
expect(rootStore.me.handle).toEqual('')
expect(rootStore.me.displayName).toEqual('')
expect(rootStore.me.description).toEqual('')
expect(rootStore.me.avatar).toEqual('')
expect(rootStore.me.notificationCount).toEqual(0)
})
})
-61
View File
@@ -1,61 +0,0 @@
import {
ConfirmModal,
ImagesLightbox,
ShellUiModel,
} from './../../../src/state/models/shell-ui'
describe('ShellUiModel', () => {
let model: ShellUiModel
beforeEach(() => {
model = new ShellUiModel()
})
afterAll(() => {
jest.clearAllMocks()
})
it('should call the openModal & closeModal method', () => {
const m = new ConfirmModal('Test Modal', 'Look good?', () => {})
model.openModal(m)
expect(model.isModalActive).toEqual(true)
expect(model.activeModal).toEqual(m)
model.closeModal()
expect(model.isModalActive).toEqual(false)
expect(model.activeModal).toBeUndefined()
})
it('should call the openLightbox & closeLightbox method', () => {
const lt = new ImagesLightbox(['uri'], 0)
model.openLightbox(lt)
expect(model.isLightboxActive).toEqual(true)
expect(model.activeLightbox).toEqual(lt)
model.closeLightbox()
expect(model.isLightboxActive).toEqual(false)
expect(model.activeLightbox).toBeUndefined()
})
it('should call the openComposer & closeComposer method', () => {
const composer = {
replyTo: {
uri: 'uri',
cid: 'cid',
text: 'text',
author: {
handle: 'handle',
displayName: 'name',
},
},
onPost: jest.fn(),
}
model.openComposer(composer)
expect(model.isComposerActive).toEqual(true)
expect(model.composerOpts).toEqual(composer)
model.closeComposer()
expect(model.isComposerActive).toEqual(false)
expect(model.composerOpts).toBeUndefined()
})
})
@@ -1,17 +1,8 @@
import {
extractEntities,
detectLinkables,
pluralize,
makeRecordUri,
ago,
makeValidHandle,
createFullHandle,
enforceLen,
cleanError,
toNiceDomain,
toShortUrl,
toShareUrl,
} from '../../src/lib/strings'
extractHtmlMeta,
} from '../src/lib/strings'
describe('extractEntities', () => {
const knownHandles = new Set(['handle.com', 'full123.test-of-chars'])
@@ -285,205 +276,44 @@ describe('detectLinkables', () => {
})
})
describe('pluralize', () => {
const inputs: [number, string, string?][] = [
[1, 'follower'],
[1, 'member'],
[100, 'post'],
[1000, 'repost'],
[10000, 'upvote'],
[100000, 'other'],
[2, 'man', 'men'],
describe('extractHtmlMeta', () => {
const inputs = [
'',
'nothing',
'<title>title</title>',
'<title> aSd!@#AC </title>',
'<title>\n title\n </title>',
'<meta name="title" content="meta title">',
'<meta name="description" content="meta description">',
'<meta property="og:title" content="og title">',
'<meta property="og:description" content="og description">',
'<meta property="og:image" content="https://ogimage.com/foo.png">',
'<meta property="twitter:title" content="twitter title">',
'<meta property="twitter:description" content="twitter description">',
'<meta property="twitter:image" content="https://twitterimage.com/foo.png">',
'<meta\n name="title"\n content="meta title"\n>',
]
const outputs = [
'follower',
'member',
'posts',
'reposts',
'upvotes',
'others',
'men',
{},
{},
{title: 'title'},
{title: 'aSd!@#AC'},
{title: 'title'},
{title: 'meta title'},
{description: 'meta description'},
{title: 'og title'},
{description: 'og description'},
{image: 'https://ogimage.com/foo.png'},
{title: 'twitter title'},
{description: 'twitter description'},
{image: 'https://twitterimage.com/foo.png'},
{title: 'meta title'},
]
it('correctly pluralizes a set of words', () => {
it('correctly handles a set of text inputs', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const output = pluralize(...input)
const output = extractHtmlMeta(input)
expect(output).toEqual(outputs[i])
}
})
})
describe('makeRecordUri', () => {
const inputs: [string, string, string][] = [
['alice.test', 'app.bsky.feed.post', '3jk7x4irgv52r'],
]
const outputs = ['at://alice.test/app.bsky.feed.post/3jk7x4irgv52r']
it('correctly builds a record URI', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const result = makeRecordUri(...input)
expect(result).toEqual(outputs[i])
}
})
})
describe('ago', () => {
const inputs = [
1671461038,
'04 Dec 1995 00:12:00 GMT',
new Date(),
new Date().setMinutes(new Date().getMinutes() - 10),
new Date().setHours(new Date().getHours() - 1),
new Date().setDate(new Date().getDate() - 1),
new Date().setMonth(new Date().getMonth() - 1),
]
const outputs = [
new Date(1671461038).toLocaleDateString(),
new Date('04 Dec 1995 00:12:00 GMT').toLocaleDateString(),
'0s',
'10m',
'1h',
'1d',
'1mo',
]
it('correctly calculates how much time passed, in a string', () => {
for (let i = 0; i < inputs.length; i++) {
const result = ago(inputs[i])
expect(result).toEqual(outputs[i])
}
})
})
describe('makeValidHandle', () => {
const inputs = [
'test-handle-123',
'test!"#$%&/()=?_',
'this-handle-should-be-too-big',
]
const outputs = ['test-handle-123', 'test', 'this-handle-should-b']
it('correctly parses and corrects handles', () => {
for (let i = 0; i < inputs.length; i++) {
const result = makeValidHandle(inputs[i])
expect(result).toEqual(outputs[i])
}
})
})
describe('createFullHandle', () => {
const inputs: [string, string][] = [
['test-handle-123', 'test'],
['.test.handle', 'test.test.'],
['test.handle.', '.test.test'],
]
const outputs = [
'test-handle-123.test',
'.test.handle.test.test.',
'test.handle.test.test',
]
it('correctly parses and corrects handles', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const result = createFullHandle(...input)
expect(result).toEqual(outputs[i])
}
})
})
describe('enforceLen', () => {
const inputs: [string, number][] = [
['Hello World!', 5],
['Hello World!', 20],
['', 5],
]
const outputs = ['Hello', 'Hello World!', '']
it('correctly enforces defined length on a given string', () => {
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i]
const result = enforceLen(...input)
expect(result).toEqual(outputs[i])
}
})
})
describe('cleanError', () => {
const inputs = [
'TypeError: Network request failed',
'Error: Aborted',
'Error: TypeError "x" is not a function',
'Error: SyntaxError unexpected token "export"',
'Some other error',
]
const outputs = [
'Unable to connect. Please check your internet connection and try again.',
'Unable to connect. Please check your internet connection and try again.',
'TypeError "x" is not a function',
'SyntaxError unexpected token "export"',
'Some other error',
]
it('removes extra content from error message', () => {
for (let i = 0; i < inputs.length; i++) {
const result = cleanError(inputs[i])
expect(result).toEqual(outputs[i])
}
})
})
describe('toNiceDomain', () => {
const inputs = [
'https://example.com/index.html',
'https://bsky.app',
'https://bsky.social',
'#123123123',
]
const outputs = ['example.com', 'bsky.app', 'Bluesky Social', '#123123123']
it("displays the url's host in a easily readable manner", () => {
for (let i = 0; i < inputs.length; i++) {
const result = toNiceDomain(inputs[i])
expect(result).toEqual(outputs[i])
}
})
})
describe('toShortUrl', () => {
const inputs = [
'https://bsky.app',
'https://bsky.app/3jk7x4irgv52r',
'https://bsky.app/3jk7x4irgv52r2313y182h9',
]
const outputs = [
'bsky.app',
'bsky.app/3jk7x4irgv52r',
'bsky.app/3jk7x4irgv52r2313y...',
]
it('shortens the url', () => {
for (let i = 0; i < inputs.length; i++) {
const result = toShortUrl(inputs[i])
expect(result).toEqual(outputs[i])
}
})
})
describe('toShareUrl', () => {
const inputs = ['https://bsky.app', '/3jk7x4irgv52r', 'item/test/123']
const outputs = [
'https://bsky.app',
'https://bsky.app/3jk7x4irgv52r',
'https://bsky.app/item/test/123',
]
it('appends https, when not present', () => {
for (let i = 0; i < inputs.length; i++) {
const result = toShareUrl(inputs[i])
expect(result).toEqual(outputs[i])
}
})
})
@@ -1,43 +0,0 @@
import React from 'react'
import {Autocomplete} from '../../../../src/view/com/composer/Autocomplete'
import {cleanup, fireEvent, render} from '../../../../jest/test-utils'
describe('Autocomplete', () => {
const onSelectMock = jest.fn()
const mockedProps = {
active: true,
items: [
{
handle: 'handle.test',
displayName: 'Test Display',
},
{
handle: 'handle2.test',
displayName: 'Test Display 2',
},
],
onSelect: onSelectMock,
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders a button for each user', async () => {
const {findAllByTestId} = render(<Autocomplete {...mockedProps} />)
const autocompleteButton = await findAllByTestId('autocompleteButton')
expect(autocompleteButton.length).toBe(2)
})
it('triggers onSelect by pressing the button', async () => {
const {findAllByTestId} = render(<Autocomplete {...mockedProps} />)
const autocompleteButton = await findAllByTestId('autocompleteButton')
fireEvent.press(autocompleteButton[0])
expect(onSelectMock).toHaveBeenCalledWith('handle.test')
fireEvent.press(autocompleteButton[1])
expect(onSelectMock).toHaveBeenCalledWith('handle2.test')
})
})
@@ -1,118 +0,0 @@
import React from 'react'
import {ComposePost} from '../../../../src/view/com/composer/ComposePost'
import {cleanup, fireEvent, render, waitFor} from '../../../../jest/test-utils'
import * as apilib from '../../../../src/state/lib/api'
import {
mockedAutocompleteViewStore,
mockedRootStore,
} from '../../../../__mocks__/state-mock'
import Toast from 'react-native-root-toast'
describe('ComposePost', () => {
const mockedProps = {
replyTo: {
uri: 'testUri',
cid: 'testCid',
text: 'testText',
author: {
handle: 'test.handle',
displayName: 'test name',
avatar: '',
},
},
onPost: jest.fn(),
onClose: jest.fn(),
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders post composer', async () => {
const {findByTestId} = render(<ComposePost {...mockedProps} />)
const composePostView = await findByTestId('composePostView')
expect(composePostView).toBeTruthy()
})
it('closes composer', async () => {
const {findByTestId} = render(<ComposePost {...mockedProps} />)
const composerCancelButton = await findByTestId('composerCancelButton')
fireEvent.press(composerCancelButton)
expect(mockedProps.onClose).toHaveBeenCalled()
})
it('changes text and publishes post', async () => {
const postSpy = jest.spyOn(apilib, 'post').mockResolvedValue({
uri: '',
cid: '',
})
const toastSpy = jest.spyOn(Toast, 'show')
const wrapper = render(<ComposePost {...mockedProps} />)
const composerTextInput = await wrapper.findByTestId('composerTextInput')
fireEvent.changeText(composerTextInput, 'testing publish')
const composerPublishButton = await wrapper.findByTestId(
'composerPublishButton',
)
fireEvent.press(composerPublishButton)
expect(postSpy).toHaveBeenCalledWith(
mockedRootStore,
'testing publish',
'testUri',
undefined,
[],
new Set<string>(),
expect.anything(),
)
// Waits for request to be resolved
await waitFor(() => {
expect(mockedProps.onPost).toHaveBeenCalled()
expect(mockedProps.onClose).toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith('Your reply has been published', {
animation: true,
duration: 3500,
hideOnPress: true,
position: 50,
shadow: true,
})
})
})
it('selects autocomplete item', async () => {
jest
.spyOn(React, 'useMemo')
.mockReturnValueOnce(mockedAutocompleteViewStore)
const {findAllByTestId} = render(<ComposePost {...mockedProps} />)
const autocompleteButton = await findAllByTestId('autocompleteButton')
fireEvent.press(autocompleteButton[0])
expect(mockedAutocompleteViewStore.setActive).toHaveBeenCalledWith(false)
})
it('selects photos', async () => {
const {findByTestId, queryByTestId} = render(
<ComposePost {...mockedProps} />,
)
let photoCarouselPickerView = queryByTestId('photoCarouselPickerView')
expect(photoCarouselPickerView).toBeFalsy()
const composerSelectPhotosButton = await findByTestId(
'composerSelectPhotosButton',
)
fireEvent.press(composerSelectPhotosButton)
photoCarouselPickerView = await findByTestId('photoCarouselPickerView')
expect(photoCarouselPickerView).toBeTruthy()
fireEvent.press(composerSelectPhotosButton)
photoCarouselPickerView = queryByTestId('photoCarouselPickerView')
expect(photoCarouselPickerView).toBeFalsy()
})
})
@@ -1,70 +0,0 @@
import React from 'react'
import {SelectedPhoto} from '../../../../src/view/com/composer/SelectedPhoto'
import {cleanup, fireEvent, render} from '../../../../jest/test-utils'
describe('SelectedPhoto', () => {
const mockedProps = {
selectedPhotos: ['mock-uri', 'mock-uri-2'],
onSelectPhotos: jest.fn(),
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('has no photos to render', () => {
const {queryByTestId} = render(
<SelectedPhoto selectedPhotos={[]} onSelectPhotos={jest.fn()} />,
)
const selectedPhotosView = queryByTestId('selectedPhotosView')
expect(selectedPhotosView).toBeNull()
const selectedPhotoImage = queryByTestId('selectedPhotoImage')
expect(selectedPhotoImage).toBeNull()
})
it('has 1 photos to render', async () => {
const {findByTestId} = render(
<SelectedPhoto
selectedPhotos={['mock-uri']}
onSelectPhotos={jest.fn()}
/>,
)
const selectedPhotosView = await findByTestId('selectedPhotosView')
expect(selectedPhotosView).toBeTruthy()
const selectedPhotoImage = await findByTestId('selectedPhotoImage')
expect(selectedPhotoImage).toBeTruthy()
// @ts-expect-error
expect(selectedPhotoImage).toHaveStyle({width: 250})
})
it('has 2 photos to render', async () => {
const {findAllByTestId} = render(<SelectedPhoto {...mockedProps} />)
const selectedPhotoImage = await findAllByTestId('selectedPhotoImage')
expect(selectedPhotoImage[0]).toBeTruthy()
// @ts-expect-error
expect(selectedPhotoImage[0]).toHaveStyle({width: 175})
})
it('has 3 photos to render', async () => {
const {findAllByTestId} = render(
<SelectedPhoto
selectedPhotos={['mock-uri', 'mock-uri-2', 'mock-uri-3']}
onSelectPhotos={jest.fn()}
/>,
)
const selectedPhotoImage = await findAllByTestId('selectedPhotoImage')
expect(selectedPhotoImage[0]).toBeTruthy()
// @ts-expect-error
expect(selectedPhotoImage[0]).toHaveStyle({width: 85})
})
it('removes a photo', async () => {
const {findAllByTestId} = render(<SelectedPhoto {...mockedProps} />)
const removePhotoButton = await findAllByTestId('removePhotoButton')
fireEvent.press(removePhotoButton[0])
expect(mockedProps.onSelectPhotos).toHaveBeenCalledWith(['mock-uri-2'])
})
})
@@ -1,58 +0,0 @@
import React from 'react'
import {Keyboard} from 'react-native'
import {CreateAccount} from '../../../../src/view/com/login/CreateAccount'
import {cleanup, fireEvent, render} from '../../../../jest/test-utils'
import {
mockedSessionStore,
mockedShellStore,
} from '../../../../__mocks__/state-mock'
describe('CreateAccount', () => {
const mockedProps = {
onPressBack: jest.fn(),
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders form and creates new account', async () => {
const {findByTestId} = render(<CreateAccount {...mockedProps} />)
const registerEmailInput = await findByTestId('registerEmailInput')
expect(registerEmailInput).toBeTruthy()
fireEvent.changeText(registerEmailInput, 'test@email.com')
const registerHandleInput = await findByTestId('registerHandleInput')
expect(registerHandleInput).toBeTruthy()
fireEvent.changeText(registerHandleInput, 'test.handle')
const registerPasswordInput = await findByTestId('registerPasswordInput')
expect(registerPasswordInput).toBeTruthy()
fireEvent.changeText(registerPasswordInput, 'testpass')
const registerIs13Input = await findByTestId('registerIs13Input')
expect(registerIs13Input).toBeTruthy()
fireEvent.press(registerIs13Input)
const createAccountButton = await findByTestId('createAccountButton')
expect(createAccountButton).toBeTruthy()
fireEvent.press(createAccountButton)
expect(mockedSessionStore.createAccount).toHaveBeenCalled()
})
it('renders and selects service', async () => {
const keyboardSpy = jest.spyOn(Keyboard, 'dismiss')
const {findByTestId} = render(<CreateAccount {...mockedProps} />)
const registerSelectServiceButton = await findByTestId(
'registerSelectServiceButton',
)
expect(registerSelectServiceButton).toBeTruthy()
fireEvent.press(registerSelectServiceButton)
expect(mockedShellStore.openModal).toHaveBeenCalled()
expect(keyboardSpy).toHaveBeenCalled()
})
})
@@ -1,110 +0,0 @@
import React from 'react'
import {cleanup, fireEvent, render} from '../../../../jest/test-utils'
import {ProfileViewModel} from '../../../../src/state/models/profile-view'
import {ProfileHeader} from '../../../../src/view/com/profile/ProfileHeader'
import {
mockedNavigationStore,
mockedProfileStore,
mockedShellStore,
} from '../../../../__mocks__/state-mock'
describe('ProfileHeader', () => {
const mockedProps = {
view: mockedProfileStore,
onRefreshAll: jest.fn(),
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders ErrorMessage on error', async () => {
const {findByTestId} = render(
<ProfileHeader
{...{
view: {
...mockedProfileStore,
hasError: true,
} as ProfileViewModel,
onRefreshAll: jest.fn(),
}}
/>,
)
const profileHeaderHasError = await findByTestId('profileHeaderHasError')
expect(profileHeaderHasError).toBeTruthy()
})
it('presses and opens edit profile', async () => {
const {findByTestId} = render(<ProfileHeader {...mockedProps} />)
const profileHeaderEditProfileButton = await findByTestId(
'profileHeaderEditProfileButton',
)
expect(profileHeaderEditProfileButton).toBeTruthy()
fireEvent.press(profileHeaderEditProfileButton)
expect(mockedShellStore.openModal).toHaveBeenCalled()
})
it('presses and opens followers page', async () => {
const {findByTestId} = render(<ProfileHeader {...mockedProps} />)
const profileHeaderFollowersButton = await findByTestId(
'profileHeaderFollowersButton',
)
expect(profileHeaderFollowersButton).toBeTruthy()
fireEvent.press(profileHeaderFollowersButton)
expect(mockedNavigationStore.navigate).toHaveBeenCalledWith(
'/profile/testhandle/followers',
)
})
// TODO - this will only pass if the profile has an avatar image set
// it('presses and opens avatar modal', async () => {
// const {findByTestId} = render(<ProfileHeader {...mockedProps} />)
// const profileHeaderAviButton = await findByTestId('profileHeaderAviButton')
// expect(profileHeaderAviButton).toBeTruthy()
// fireEvent.press(profileHeaderAviButton)
// expect(mockedShellStore.openLightbox).toHaveBeenCalled()
// })
it('presses and opens follows page', async () => {
const {findByTestId} = render(<ProfileHeader {...mockedProps} />)
const profileHeaderFollowsButton = await findByTestId(
'profileHeaderFollowsButton',
)
expect(profileHeaderFollowsButton).toBeTruthy()
fireEvent.press(profileHeaderFollowsButton)
expect(mockedNavigationStore.navigate).toHaveBeenCalledWith(
'/profile/testhandle/follows',
)
})
it('toggles following', async () => {
const {findByTestId} = render(
<ProfileHeader
{...{
view: {
...mockedProfileStore,
did: 'test did 2',
} as ProfileViewModel,
onRefreshAll: jest.fn(),
}}
/>,
)
const profileHeaderToggleFollowButton = await findByTestId(
'profileHeaderToggleFollowButton',
)
expect(profileHeaderToggleFollowButton).toBeTruthy()
fireEvent.press(profileHeaderToggleFollowButton)
expect(mockedProps.view.toggleFollowing).toHaveBeenCalled()
})
})
@@ -1,17 +0,0 @@
import {renderHook} from '../../../jest/test-utils'
import {useAnimatedValue} from '../../../src/view/lib/hooks/useAnimatedValue'
describe('useAnimatedValue', () => {
it('creates an Animated.Value with the initial value passed to the hook', () => {
const {result} = renderHook(() => useAnimatedValue(10))
// @ts-expect-error
expect(result.current.__getValue()).toEqual(10)
})
it('returns the same Animated.Value instance on subsequent renders', () => {
const {result, rerender} = renderHook(() => useAnimatedValue(10))
const firstValue = result.current
rerender({})
expect(result.current).toBe(firstValue)
})
})
@@ -1,49 +0,0 @@
import React from 'react'
import {fireEvent, render} from '../../../jest/test-utils'
import {Home} from '../../../src/view/screens/Home'
import {mockedRootStore, mockedShellStore} from '../../../__mocks__/state-mock'
describe('useOnMainScroll', () => {
const mockedProps = {
navIdx: [0, 0] as [number, number],
params: {},
visible: true,
}
it('toggles minimalShellMode to true', () => {
jest.useFakeTimers()
const {getByTestId} = render(<Home {...mockedProps} />)
fireEvent.scroll(getByTestId('homeFeed'), {
nativeEvent: {
contentOffset: {y: 20},
contentSize: {height: 100},
layoutMeasurement: {height: 50},
},
})
expect(mockedRootStore.shell.setMinimalShellMode).toHaveBeenCalledWith(true)
})
it('toggles minimalShellMode to false', () => {
jest.useFakeTimers()
const {getByTestId} = render(<Home {...mockedProps} />, {
...mockedRootStore,
shell: {
...mockedShellStore,
minimalShellMode: true,
},
})
fireEvent.scroll(getByTestId('homeFeed'), {
nativeEvent: {
contentOffset: {y: 0},
contentSize: {height: 100},
layoutMeasurement: {height: 50},
},
})
expect(mockedRootStore.shell.setMinimalShellMode).toHaveBeenCalledWith(
false,
)
})
})
-37
View File
@@ -1,37 +0,0 @@
import React from 'react'
import {Login} from '../../../src/view/screens/Login'
import {cleanup, fireEvent, render} from '../../../jest/test-utils'
describe('Login', () => {
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders initial screen', () => {
const {getByTestId} = render(<Login />)
const signUpScreen = getByTestId('signinOrCreateAccount')
expect(signUpScreen).toBeTruthy()
})
it('renders Signin screen', () => {
const {getByTestId} = render(<Login />)
const signInButton = getByTestId('signInButton')
fireEvent.press(signInButton)
const signInScreen = getByTestId('signIn')
expect(signInScreen).toBeTruthy()
})
it('renders CreateAccount screen', () => {
const {getByTestId} = render(<Login />)
const createAccountButton = getByTestId('createAccountButton')
fireEvent.press(createAccountButton)
const createAccountScreen = getByTestId('createAccount')
expect(createAccountScreen).toBeTruthy()
})
})
-21
View File
@@ -1,21 +0,0 @@
import React from 'react'
import {NotFound} from '../../../src/view/screens/NotFound'
import {cleanup, fireEvent, render} from '../../../jest/test-utils'
import {mockedNavigationStore} from '../../../__mocks__/state-mock'
describe('NotFound', () => {
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('navigates home', async () => {
const navigationSpy = jest.spyOn(mockedNavigationStore, 'navigate')
const {getByTestId} = render(<NotFound />)
const navigateHomeButton = getByTestId('navigateHomeButton')
fireEvent.press(navigateHomeButton)
expect(navigationSpy).toHaveBeenCalledWith('/')
})
})
-30
View File
@@ -1,30 +0,0 @@
import React from 'react'
import {Search} from '../../../src/view/screens/Search'
import {cleanup, fireEvent, render} from '../../../jest/test-utils'
describe('Search', () => {
jest.useFakeTimers()
const mockedProps = {
navIdx: [0, 0] as [number, number],
params: {
name: 'test name',
},
visible: true,
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders with query', async () => {
const {findByTestId} = render(<Search {...mockedProps} />)
const searchTextInput = await findByTestId('searchTextInput')
expect(searchTextInput).toBeTruthy()
fireEvent.changeText(searchTextInput, 'test')
const searchScrollView = await findByTestId('searchScrollView')
expect(searchScrollView).toBeTruthy()
})
})
-57
View File
@@ -1,57 +0,0 @@
import React from 'react'
import {Menu} from '../../../../src/view/shell/mobile/Menu'
import {cleanup, fireEvent, render} from '../../../../jest/test-utils'
import {mockedNavigationStore} from '../../../../__mocks__/state-mock'
describe('Menu', () => {
const onCloseMock = jest.fn()
const mockedProps = {
visible: true,
onClose: onCloseMock,
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders menu', () => {
const {getByTestId} = render(<Menu {...mockedProps} />)
const menuView = getByTestId('menuView')
expect(menuView).toBeTruthy()
})
it('presses profile card button', () => {
const {getByTestId} = render(<Menu {...mockedProps} />)
const profileCardButton = getByTestId('profileCardButton')
fireEvent.press(profileCardButton)
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.switchTo).toHaveBeenCalledWith(0, true)
})
it('presses search button', () => {
const {getByTestId} = render(<Menu {...mockedProps} />)
const searchBtn = getByTestId('searchBtn')
fireEvent.press(searchBtn)
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.switchTo).toHaveBeenCalledWith(0, true)
expect(mockedNavigationStore.navigate).toHaveBeenCalledWith('/search')
})
it("presses notifications menu item' button", () => {
const {getByTestId} = render(<Menu {...mockedProps} />)
const menuItemButton = getByTestId('menuItemButton-Notifications')
fireEvent.press(menuItemButton)
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.switchTo).toHaveBeenCalledWith(1, true)
})
})
@@ -1,100 +0,0 @@
import React from 'react'
import {Animated} from 'react-native'
import {TabsSelector} from '../../../../src/view/shell/mobile/TabsSelector'
import {cleanup, fireEvent, render} from '../../../../jest/test-utils'
import {mockedNavigationStore} from '../../../../__mocks__/state-mock'
describe('TabsSelector', () => {
const onCloseMock = jest.fn()
const mockedProps = {
active: true,
tabMenuInterp: new Animated.Value(0),
onClose: onCloseMock,
}
afterAll(() => {
jest.clearAllMocks()
cleanup()
})
it('renders tabs selector', () => {
const {getByTestId} = render(<TabsSelector {...mockedProps} />)
const tabsSelectorView = getByTestId('tabsSelectorView')
expect(tabsSelectorView).toBeTruthy()
})
it('renders nothing if inactive', () => {
const {getByTestId} = render(
<TabsSelector {...{...mockedProps, active: false}} />,
)
const emptyView = getByTestId('emptyView')
expect(emptyView).toBeTruthy()
})
// TODO - this throws currently, but the tabs selector isnt being used atm so I just disabled -prf
// it('presses share button', () => {
// const shareSpy = jest.spyOn(Share, 'share')
// const {getByTestId} = render(<TabsSelector {...mockedProps} />)
// const shareButton = getByTestId('shareButton')
// fireEvent.press(shareButton)
// expect(onCloseMock).toHaveBeenCalled()
// expect(shareSpy).toHaveBeenCalledWith({url: 'https://bsky.app/'})
// })
it('presses clone button', () => {
const {getByTestId} = render(<TabsSelector {...mockedProps} />)
const cloneButton = getByTestId('cloneButton')
fireEvent.press(cloneButton)
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.newTab).toHaveBeenCalled()
})
it('presses new tab button', () => {
const {getByTestId} = render(<TabsSelector {...mockedProps} />)
const newTabButton = getByTestId('newTabButton')
fireEvent.press(newTabButton)
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.newTab).toHaveBeenCalledWith('/')
})
it('presses change tab button', () => {
const {getAllByTestId} = render(<TabsSelector {...mockedProps} />)
const changeTabButton = getAllByTestId('changeTabButton')
fireEvent.press(changeTabButton[0])
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.newTab).toHaveBeenCalledWith('/')
})
it('presses close tab button', () => {
const {getAllByTestId} = render(<TabsSelector {...mockedProps} />)
const closeTabButton = getAllByTestId('closeTabButton')
fireEvent.press(closeTabButton[0])
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.setActiveTab).toHaveBeenCalledWith(0)
})
it('presses swipes to close the tab', () => {
const {getByTestId} = render(<TabsSelector {...mockedProps} />)
const tabsSwipable = getByTestId('tabsSwipable')
fireEvent(tabsSwipable, 'swipeableRightOpen')
expect(onCloseMock).toHaveBeenCalled()
expect(mockedNavigationStore.setActiveTab).toHaveBeenCalledWith(0)
})
})
+55
View File
@@ -0,0 +1,55 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "xyz.blueskyweb.app",
)
android_resource(
name = "res",
package = "xyz.blueskyweb.app",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)
+213 -64
View File
@@ -1,86 +1,128 @@
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
// codegenDir = file("../node_modules/react-native-codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
/* Bundling */
// A list containing the node command and its flags. Default is just 'node'.
// nodeExecutableAndArgs = ["node"]
//
// The command to run when bundling. By default is 'bundle'
// bundleCommand = "ram-bundle"
//
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/MyApplication.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */
// The hermes compiler command to run. By default it is 'hermesc'
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
}
project.ext.react = [
enableHermes: false, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create four separate APKs instead of one,
* one for each native architecture. This is useful if you don't
* use App Bundles (https://developer.android.com/guide/app-bundle/)
* and want to have separate APKs to upload to the Play Store.
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore (JSC)
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Private function to get the list of Native Architectures you want to build.
* This reads the value from reactNativeArchitectures in your gradle.properties
* file and works together with the --active-arch-only flag of react-native run-android.
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
@@ -92,13 +134,84 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
namespace "xyz.blueskyweb.app"
defaultConfig {
applicationId "xyz.blueskyweb.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-21",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=$buildDir/generated/source",
"PROJECT_BUILD_DIR=$buildDir",
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
// Make sure this target name is the same you specify inside the
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
targets "app_appmodules"
// Fix for windows limit on number of character in file paths and in command lines
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
arguments "NDK_APP_SHORT_COMMANDS=true"
}
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
ndkBuild {
path "$projectDir/src/main/jni/Android.mk"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureNdkBuild* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureNdkBuildRelease.dependsOn(preReleaseBuild)
configureNdkBuildDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}
splits {
@@ -148,24 +261,60 @@ android {
}
dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
implementation project(':rn-fetch-blob')
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source")
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
+19
View File
@@ -0,0 +1,19 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
@@ -25,16 +25,13 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
/**
* Class responsible of loading Flipper inside your React Native application. This is the debug
* flavor of it. Here you can add your own plugins and customize the Flipper setup.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
+1 -4
View File
@@ -2,9 +2,6 @@
package="xyz.blueskyweb.app">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".MainApplication"
@@ -18,7 +15,7 @@
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -2,8 +2,7 @@ package xyz.blueskyweb.app;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
import com.facebook.react.ReactRootView;
import android.os.Bundle;
public class MainActivity extends ReactActivity {
@@ -18,19 +17,30 @@ public class MainActivity extends ReactActivity {
}
/**
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
* (aka React 18) with two boolean flags.
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
* you can specify the rendered you wish to use (Fabric or the older renderer).
*/
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new DefaultReactActivityDelegate(
this,
getMainComponentName(),
return new MainActivityDelegate(this, getMainComponentName());
}
public static class MainActivityDelegate extends ReactActivityDelegate {
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
super(activity, mainComponentName);
}
@Override
protected ReactRootView createRootView() {
ReactRootView reactRootView = new ReactRootView(getContext());
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
);
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
return reactRootView;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
}
}
@@ -1,12 +1,13 @@
package xyz.blueskyweb.app;
import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.soloader.SoLoader;
import xyz.blueskyweb.app.newarchitecture.MainApplicationReactNativeHost;
import java.lang.reflect.InvocationTargetException;
@@ -15,7 +16,7 @@ import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new DefaultReactNativeHost(this) {
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
@@ -34,20 +35,18 @@ public class MainApplication extends Application implements ReactApplication {
protected String getJSMainModuleName() {
return "index";
}
@Override
protected boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
};
private final ReactNativeHost mNewArchitectureNativeHost =
new MainApplicationReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
}
@Override
@@ -56,10 +55,37 @@ public class MainApplication extends Application implements ReactApplication {
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.app.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
}
@@ -0,0 +1,116 @@
package xyz.blueskyweb.app.newarchitecture;
import android.app.Application;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.JSIModulePackage;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JSIModuleSpec;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.EmptyReactNativeConfig;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.uimanager.ViewManagerRegistry;
import xyz.blueskyweb.app.BuildConfig;
import xyz.blueskyweb.app.newarchitecture.components.MainComponentsRegistry;
import xyz.blueskyweb.app.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
* TurboModule delegates and the Fabric Renderer.
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationReactNativeHost extends ReactNativeHost {
public MainApplicationReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
// packages.add(new TurboReactPackage() { ... });
// If you have custom Fabric Components, their ViewManagers should also be loaded here
// inside a ReactPackage.
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@NonNull
@Override
protected ReactPackageTurboModuleManagerDelegate.Builder
getReactPackageTurboModuleManagerDelegateBuilder() {
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
// for the new architecture and to use TurboModules correctly.
return new MainApplicationTurboModuleManagerDelegate.Builder();
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();
// Here we provide a new JSIModuleSpec that will be responsible of providing the
// custom Fabric Components.
specs.add(
new JSIModuleSpec() {
@Override
public JSIModuleType getJSIModuleType() {
return JSIModuleType.UIManager;
}
@Override
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
final ComponentFactory componentFactory = new ComponentFactory();
CoreComponentsRegistry.register(componentFactory);
// Here we register a Components Registry.
// The one that is generated with the template contains no components
// and just provides you the one from React Native core.
MainComponentsRegistry.register(componentFactory);
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
ViewManagerRegistry viewManagerRegistry =
new ViewManagerRegistry(
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
return new FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
new EmptyReactNativeConfig(),
viewManagerRegistry);
}
});
return specs;
}
};
}
}
@@ -0,0 +1,36 @@
package xyz.blueskyweb.app.newarchitecture.components;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.soloader.SoLoader;
/**
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
@DoNotStrip
public class MainComponentsRegistry {
static {
SoLoader.loadLibrary("fabricjni");
}
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private native HybridData initHybrid(ComponentFactory componentFactory);
@DoNotStrip
private MainComponentsRegistry(ComponentFactory componentFactory) {
mHybridData = initHybrid(componentFactory);
}
@DoNotStrip
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
return new MainComponentsRegistry(componentFactory);
}
}
@@ -0,0 +1,48 @@
package xyz.blueskyweb.app.newarchitecture.modules;
import com.facebook.jni.HybridData;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.soloader.SoLoader;
import java.util.List;
/**
* Class responsible to load the TurboModules. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationTurboModuleManagerDelegate
extends ReactPackageTurboModuleManagerDelegate {
private static volatile boolean sIsSoLibraryLoaded;
protected MainApplicationTurboModuleManagerDelegate(
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
super(reactApplicationContext, packages);
}
protected native HybridData initHybrid();
native boolean canCreateTurboModule(String moduleName);
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
protected MainApplicationTurboModuleManagerDelegate build(
ReactApplicationContext context, List<ReactPackage> packages) {
return new MainApplicationTurboModuleManagerDelegate(context, packages);
}
}
@Override
protected synchronized void maybeLoadOtherSoLibraries() {
if (!sIsSoLibraryLoaded) {
// If you change the name of your application .so file in the Android.mk file,
// make sure you update the name here as well.
SoLoader.loadLibrary("app_appmodules");
sIsSoLibraryLoaded = true;
}
}
}
@@ -0,0 +1,24 @@
#include "MainApplicationModuleProvider.h"
#include <rncore.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string moduleName,
const JavaTurboModule::InitParams &params) {
// Here you can provide your own module provider for TurboModules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a library called `samplelibrary`:
//
// auto module = samplelibrary_ModuleProvider(moduleName, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(moduleName, params);
return rncore_ModuleProvider(moduleName, params);
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,16 @@
#pragma once
#include <memory>
#include <string>
#include <ReactCommon/JavaTurboModule.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string moduleName,
const JavaTurboModule::InitParams &params);
} // namespace react
} // namespace facebook
@@ -0,0 +1,45 @@
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainApplicationModuleProvider.h"
namespace facebook {
namespace react {
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
MainApplicationTurboModuleManagerDelegate::initHybrid(
jni::alias_ref<jhybridobject>) {
return makeCxxInstance();
}
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
registerHybrid({
makeNativeMethod(
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
makeNativeMethod(
"canCreateTurboModule",
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
});
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string name,
const std::shared_ptr<CallInvoker> jsInvoker) {
// Not implemented yet: provide pure-C++ NativeModules here.
return nullptr;
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string name,
const JavaTurboModule::InitParams &params) {
return MainApplicationModuleProvider(name, params);
}
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
std::string name) {
return getTurboModule(name, nullptr) != nullptr ||
getTurboModule(name, {.moduleName = name}) != nullptr;
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,38 @@
#include <memory>
#include <string>
#include <ReactCommon/TurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
namespace facebook {
namespace react {
class MainApplicationTurboModuleManagerDelegate
: public jni::HybridClass<
MainApplicationTurboModuleManagerDelegate,
TurboModuleManagerDelegate> {
public:
// Adapt it to the package you used for your Java class.
static constexpr auto kJavaDescriptor =
"Lcom/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
static void registerNatives();
std::shared_ptr<TurboModule> getTurboModule(
const std::string name,
const std::shared_ptr<CallInvoker> jsInvoker) override;
std::shared_ptr<TurboModule> getTurboModule(
const std::string name,
const JavaTurboModule::InitParams &params) override;
/**
* Test-only method. Allows user to verify whether a TurboModule can be
* created by instances of this class.
*/
bool canCreateTurboModule(std::string name);
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,61 @@
#include "MainComponentsRegistry.h"
#include <CoreComponentsRegistry.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/rncore/ComponentDescriptors.h>
namespace facebook {
namespace react {
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
std::shared_ptr<ComponentDescriptorProviderRegistry const>
MainComponentsRegistry::sharedProviderRegistry() {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
// Custom Fabric Components go here. You can register custom
// components coming from your App or from 3rd party libraries here.
//
// providerRegistry->add(concreteComponentDescriptorProvider<
// AocViewerComponentDescriptor>());
return providerRegistry;
}
jni::local_ref<MainComponentsRegistry::jhybriddata>
MainComponentsRegistry::initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate) {
auto instance = makeCxxInstance(delegate);
auto buildRegistryFunction =
[](EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
-> ComponentDescriptorRegistry::Shared {
auto registry = MainComponentsRegistry::sharedProviderRegistry()
->createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});
auto mutableRegistry =
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
mutableRegistry->setFallbackComponentDescriptor(
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
ComponentDescriptorParameters{
eventDispatcher, contextContainer, nullptr}));
return registry;
};
delegate->buildRegistryFunction = buildRegistryFunction;
return instance;
}
void MainComponentsRegistry::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
});
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,32 @@
#pragma once
#include <ComponentFactory.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
class MainComponentsRegistry
: public facebook::jni::HybridClass<MainComponentsRegistry> {
public:
// Adapt it to the package you used for your Java class.
constexpr static auto kJavaDescriptor =
"Lcom/app/newarchitecture/components/MainComponentsRegistry;";
static void registerNatives();
MainComponentsRegistry(ComponentFactory *delegate);
private:
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
sharedProviderRegistry();
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate);
};
} // namespace react
} // namespace facebook
+11
View File
@@ -0,0 +1,11 @@
#include <fbjni/fbjni.h>
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainComponentsRegistry.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::MainApplicationTurboModuleManagerDelegate::
registerNatives();
facebook::react::MainComponentsRegistry::registerNatives();
});
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -1,18 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package xyz.blueskyweb.app;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
/**
* Class responsible of loading Flipper inside your React Native application. This is the release
* flavor of it so it's empty as we don't want to load Flipper.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
// Do nothing as we don't want to initialize Flipper on Release.
}
}
+36 -6
View File
@@ -4,20 +4,50 @@ import org.apache.tools.ant.taskdefs.condition.Os
buildscript {
ext {
buildToolsVersion = "33.0.0"
buildToolsVersion = "31.0.0"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
compileSdkVersion = 31
targetSdkVersion = 31
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
classpath('com.android.tools.build:gradle:7.2.1')
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:4.1.2")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
google()
maven { url 'https://www.jitpack.io' }
}
}
-4
View File
@@ -38,7 +38,3 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+5 -2
View File
@@ -2,5 +2,8 @@ rootProject.name = 'app'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/react-native-gradle-plugin')
include ':rn-fetch-blob'
project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
}
-10
View File
@@ -1,10 +0,0 @@
# This `.xcode.env` file is versioned and is used to source the environment
# used when running script phases inside Xcode.
# To customize your local environment, you can create an `.xcode.env.local`
# file that is not versioned.
# NODE_BINARY variable contains the PATH to the node executable.
#
# Customize the NODE_BINARY variable here.
# For example, to use nvm with brew, add the following line
# . "$(brew --prefix nvm)/nvm.sh" --no-use
export NODE_BINARY=$(command -v node)
+10 -44
View File
@@ -1,25 +1,8 @@
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, min_ios_version_supported
prepare_react_native_project!
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
#
# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
# ```js
# module.exports = {
# dependencies: {
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
# ```
flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
use_frameworks! :linkage => linkage.to_sym
end
platform :ios, '11.0'
install! 'cocoapods', :deterministic_uuids => false
target 'app' do
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
@@ -30,16 +13,9 @@ target 'app' do
use_react_native!(
:path => config[:reactNativePath],
# Hermes is now enabled by default. Disable by setting this flag to false.
# Upcoming versions of React Native may rely on get_default_flags(), but
# we make it explicit here to aid in the React Native upgrade process.
# to enable hermes on iOS, change `false` to `true` and then install pods
:hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled],
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable the next line.
#:flipper_configuration => flipper_config,
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
@@ -49,24 +25,14 @@ target 'app' do
# Pods for testing
end
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable the next line.
#use_flipper!()
post_install do |installer|
# Temporary fix until CocoaPods 1.12.0 is released.
# https://github.com/CocoaPods/CocoaPods/issues/11402#issuecomment-1201464693
installer.pods_project.targets.each do |target|
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
react_native_post_install(
installer,
# Set `mac_catalyst_enabled` to `true` in order to apply patches
# necessary for Mac Catalyst builds
:mac_catalyst_enabled => false
)
react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
# iOS fix
+267 -381
View File
@@ -3,250 +3,227 @@ PODS:
- BVLinearGradient (2.6.2):
- React-Core
- DoubleConversion (1.1.6)
- FBLazyVector (0.71.1)
- FBReactNativeSpec (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired (= 0.71.1)
- RCTTypeSafety (= 0.71.1)
- React-Core (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- FBLazyVector (0.68.2)
- FBReactNativeSpec (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.68.2)
- RCTTypeSafety (= 0.68.2)
- React-Core (= 0.68.2)
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- fmt (6.2.1)
- glog (0.3.5)
- hermes-engine (0.71.0):
- hermes-engine/Pre-built (= 0.71.0)
- hermes-engine/Pre-built (0.71.0)
- libevent (2.1.12)
- RCT-Folly (2021.07.22.00):
- RCT-Folly (2021.06.28.00-v2):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCT-Folly/Default (= 2021.07.22.00)
- RCT-Folly/Default (2021.07.22.00):
- RCT-Folly/Default (= 2021.06.28.00-v2)
- RCT-Folly/Default (2021.06.28.00-v2):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- RCT-Folly/Futures (2021.07.22.00):
- boost
- DoubleConversion
- fmt (~> 6.2.1)
- RCTRequired (0.68.2)
- RCTTypeSafety (0.68.2):
- FBLazyVector (= 0.68.2)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.68.2)
- React-Core (= 0.68.2)
- React (0.68.2):
- React-Core (= 0.68.2)
- React-Core/DevSupport (= 0.68.2)
- React-Core/RCTWebSocket (= 0.68.2)
- React-RCTActionSheet (= 0.68.2)
- React-RCTAnimation (= 0.68.2)
- React-RCTBlob (= 0.68.2)
- React-RCTImage (= 0.68.2)
- React-RCTLinking (= 0.68.2)
- React-RCTNetwork (= 0.68.2)
- React-RCTSettings (= 0.68.2)
- React-RCTText (= 0.68.2)
- React-RCTVibration (= 0.68.2)
- React-callinvoker (0.68.2)
- React-Codegen (0.68.2):
- FBReactNativeSpec (= 0.68.2)
- RCT-Folly (= 2021.06.28.00-v2)
- RCTRequired (= 0.68.2)
- RCTTypeSafety (= 0.68.2)
- React-Core (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-Core (0.68.2):
- glog
- libevent
- RCTRequired (0.71.1)
- RCTTypeSafety (0.71.1):
- FBLazyVector (= 0.71.1)
- RCTRequired (= 0.71.1)
- React-Core (= 0.71.1)
- React (0.71.1):
- React-Core (= 0.71.1)
- React-Core/DevSupport (= 0.71.1)
- React-Core/RCTWebSocket (= 0.71.1)
- React-RCTActionSheet (= 0.71.1)
- React-RCTAnimation (= 0.71.1)
- React-RCTBlob (= 0.71.1)
- React-RCTImage (= 0.71.1)
- React-RCTLinking (= 0.71.1)
- React-RCTNetwork (= 0.71.1)
- React-RCTSettings (= 0.71.1)
- React-RCTText (= 0.71.1)
- React-RCTVibration (= 0.71.1)
- React-callinvoker (0.71.1)
- React-Codegen (0.71.1):
- FBReactNativeSpec
- hermes-engine
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- React-jsi
- React-jsiexecutor
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- React-Core (0.71.1):
- glog
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.71.1)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.68.2)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/CoreModulesHeaders (0.71.1):
- React-Core/CoreModulesHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/Default (0.71.1):
- React-Core/Default (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- RCT-Folly (= 2021.06.28.00-v2)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/DevSupport (0.71.1):
- React-Core/DevSupport (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.71.1)
- React-Core/RCTWebSocket (= 0.71.1)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-jsinspector (= 0.71.1)
- React-perflogger (= 0.71.1)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.68.2)
- React-Core/RCTWebSocket (= 0.68.2)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-jsinspector (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTActionSheetHeaders (0.71.1):
- React-Core/RCTActionSheetHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTAnimationHeaders (0.71.1):
- React-Core/RCTAnimationHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTBlobHeaders (0.71.1):
- React-Core/RCTBlobHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTImageHeaders (0.71.1):
- React-Core/RCTImageHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTLinkingHeaders (0.71.1):
- React-Core/RCTLinkingHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTNetworkHeaders (0.71.1):
- React-Core/RCTNetworkHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTSettingsHeaders (0.71.1):
- React-Core/RCTSettingsHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTTextHeaders (0.71.1):
- React-Core/RCTTextHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTVibrationHeaders (0.71.1):
- React-Core/RCTVibrationHeaders (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-Core/RCTWebSocket (0.71.1):
- React-Core/RCTWebSocket (0.68.2):
- glog
- RCT-Folly (= 2021.07.22.00)
- React-Core/Default (= 0.71.1)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-perflogger (= 0.71.1)
- RCT-Folly (= 2021.06.28.00-v2)
- React-Core/Default (= 0.68.2)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsiexecutor (= 0.68.2)
- React-perflogger (= 0.68.2)
- Yoga
- React-CoreModules (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.1)
- React-Codegen (= 0.71.1)
- React-Core/CoreModulesHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- React-RCTImage (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-cxxreact (0.71.1):
- React-CoreModules (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.2)
- React-Codegen (= 0.68.2)
- React-Core/CoreModulesHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- React-RCTImage (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-cxxreact (0.68.2):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.1)
- React-jsi (= 0.71.1)
- React-jsinspector (= 0.71.1)
- React-logger (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-runtimeexecutor (= 0.71.1)
- React-hermes (0.71.1):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.07.22.00)
- React-cxxreact (= 0.71.1)
- React-jsiexecutor (= 0.71.1)
- React-jsinspector (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-jsi (0.71.1):
- RCT-Folly (= 2021.06.28.00-v2)
- React-callinvoker (= 0.68.2)
- React-jsi (= 0.68.2)
- React-jsinspector (= 0.68.2)
- React-logger (= 0.68.2)
- React-perflogger (= 0.68.2)
- React-runtimeexecutor (= 0.68.2)
- React-jsi (0.68.2):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- React-jsiexecutor (0.71.1):
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsi/Default (= 0.68.2)
- React-jsi/Default (0.68.2):
- boost (= 1.76.0)
- DoubleConversion
- glog
- RCT-Folly (= 2021.07.22.00)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-perflogger (= 0.71.1)
- React-jsinspector (0.71.1)
- React-logger (0.71.1):
- RCT-Folly (= 2021.06.28.00-v2)
- React-jsiexecutor (0.68.2):
- DoubleConversion
- glog
- react-native-blur (4.3.0):
- RCT-Folly (= 2021.06.28.00-v2)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-perflogger (= 0.68.2)
- React-jsinspector (0.68.2)
- React-logger (0.68.2):
- glog
- react-native-cameraroll (5.1.0):
- React-Core
- react-native-cameraroll (5.2.2):
- react-native-pager-view (6.0.2):
- React-Core
- react-native-image-resizer (3.0.4):
- React-Core
- react-native-pager-view (6.1.2):
- React-Core
- react-native-paste-input (0.6.0):
- React-Core
- Swime (= 3.0.6)
- react-native-safe-area-context (4.4.1):
- RCT-Folly
- RCTRequired
@@ -257,98 +234,76 @@ PODS:
- React-Core
- react-native-version-number (0.3.6):
- React
- React-perflogger (0.71.1)
- React-RCTActionSheet (0.71.1):
- React-Core/RCTActionSheetHeaders (= 0.71.1)
- React-RCTAnimation (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.1)
- React-Codegen (= 0.71.1)
- React-Core/RCTAnimationHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTAppDelegate (0.71.1):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- React-RCTBlob (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.71.1)
- React-Core/RCTBlobHeaders (= 0.71.1)
- React-Core/RCTWebSocket (= 0.71.1)
- React-jsi (= 0.71.1)
- React-RCTNetwork (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTImage (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.1)
- React-Codegen (= 0.71.1)
- React-Core/RCTImageHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- React-RCTNetwork (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTLinking (0.71.1):
- React-Codegen (= 0.71.1)
- React-Core/RCTLinkingHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTNetwork (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.1)
- React-Codegen (= 0.71.1)
- React-Core/RCTNetworkHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTSettings (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.1)
- React-Codegen (= 0.71.1)
- React-Core/RCTSettingsHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-RCTText (0.71.1):
- React-Core/RCTTextHeaders (= 0.71.1)
- React-RCTVibration (0.71.1):
- RCT-Folly (= 2021.07.22.00)
- React-Codegen (= 0.71.1)
- React-Core/RCTVibrationHeaders (= 0.71.1)
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/core (= 0.71.1)
- React-runtimeexecutor (0.71.1):
- React-jsi (= 0.71.1)
- ReactCommon/turbomodule/bridging (0.71.1):
- React-perflogger (0.68.2)
- React-RCTActionSheet (0.68.2):
- React-Core/RCTActionSheetHeaders (= 0.68.2)
- React-RCTAnimation (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.2)
- React-Codegen (= 0.68.2)
- React-Core/RCTAnimationHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-RCTBlob (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- React-Codegen (= 0.68.2)
- React-Core/RCTBlobHeaders (= 0.68.2)
- React-Core/RCTWebSocket (= 0.68.2)
- React-jsi (= 0.68.2)
- React-RCTNetwork (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-RCTImage (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.2)
- React-Codegen (= 0.68.2)
- React-Core/RCTImageHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- React-RCTNetwork (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-RCTLinking (0.68.2):
- React-Codegen (= 0.68.2)
- React-Core/RCTLinkingHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-RCTNetwork (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.2)
- React-Codegen (= 0.68.2)
- React-Core/RCTNetworkHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-RCTSettings (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- RCTTypeSafety (= 0.68.2)
- React-Codegen (= 0.68.2)
- React-Core/RCTSettingsHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-RCTText (0.68.2):
- React-Core/RCTTextHeaders (= 0.68.2)
- React-RCTVibration (0.68.2):
- RCT-Folly (= 2021.06.28.00-v2)
- React-Codegen (= 0.68.2)
- React-Core/RCTVibrationHeaders (= 0.68.2)
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (= 0.68.2)
- React-runtimeexecutor (0.68.2):
- React-jsi (= 0.68.2)
- ReactCommon/turbomodule/core (0.68.2):
- DoubleConversion
- glog
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.1)
- React-Core (= 0.71.1)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-logger (= 0.71.1)
- React-perflogger (= 0.71.1)
- ReactCommon/turbomodule/core (0.71.1):
- DoubleConversion
- glog
- RCT-Folly (= 2021.07.22.00)
- React-callinvoker (= 0.71.1)
- React-Core (= 0.71.1)
- React-cxxreact (= 0.71.1)
- React-jsi (= 0.71.1)
- React-logger (= 0.71.1)
- React-perflogger (= 0.71.1)
- rn-fetch-blob (0.12.0):
- React-Core
- RNBackgroundFetch (4.1.8):
- React-Core
- RNCAsyncStorage (1.17.11):
- RCT-Folly (= 2021.06.28.00-v2)
- React-callinvoker (= 0.68.2)
- React-Core (= 0.68.2)
- React-cxxreact (= 0.68.2)
- React-jsi (= 0.68.2)
- React-logger (= 0.68.2)
- React-perflogger (= 0.68.2)
- RNCAsyncStorage (1.17.10):
- React-Core
- RNCClipboard (1.11.1):
- React-Core
- RNFS (2.20.0):
- React-Core
- RNGestureHandler (2.8.0):
- RNGestureHandler (2.6.1):
- React-Core
- RNImageCropPicker (0.38.1):
- React-Core
@@ -361,14 +316,7 @@ PODS:
- TOCropViewController
- RNInAppBrowser (3.7.0):
- React-Core
- RNNotifee (7.4.0):
- React-Core
- RNNotifee/NotifeeCore (= 7.4.0)
- RNNotifee/NotifeeCore (7.4.0):
- React-Core
- RNReactNativeHapticFeedback (1.14.0):
- React-Core
- RNReanimated (2.13.0):
- RNReanimated (2.10.0):
- DoubleConversion
- FBLazyVector
- FBReactNativeSpec
@@ -395,17 +343,11 @@ PODS:
- React-RCTText
- ReactCommon/turbomodule/core
- Yoga
- RNScreens (3.18.2):
- RNScreens (3.17.0):
- React-Core
- React-RCTImage
- RNSVG (12.5.0):
- RNSVG (12.4.4):
- React-Core
- segment-analytics-react-native (2.10.1):
- React-Core
- sovran-react-native
- sovran-react-native (0.4.5):
- React-Core
- Swime (3.0.6)
- TOCropViewController (2.6.1)
- Yoga (1.14.0)
@@ -416,8 +358,6 @@ DEPENDENCIES:
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- libevent (~> 2.1.12)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
@@ -425,26 +365,22 @@ DEPENDENCIES:
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Codegen (from `build/generated/ios`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
- React-hermes (from `../node_modules/react-native/ReactCommon/hermes`)
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
- "react-native-image-resizer (from `../node_modules/@bam.tech/react-native-image-resizer`)"
- react-native-pager-view (from `../node_modules/react-native-pager-view`)
- "react-native-paste-input (from `../node_modules/@mattermost/react-native-paste-input`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-version-number (from `../node_modules/react-native-version-number`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
- React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
@@ -454,28 +390,19 @@ DEPENDENCIES:
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
- RNBackgroundFetch (from `../node_modules/react-native-background-fetch`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`)
- "RNNotifee (from `../node_modules/@notifee/react-native`)"
- RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
- "segment-analytics-react-native (from `../node_modules/@segment/analytics-react-native`)"
- "sovran-react-native (from `../node_modules/@segment/sovran-react-native`)"
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
trunk:
- fmt
- libevent
- Swime
- TOCropViewController
EXTERNAL SOURCES:
@@ -491,8 +418,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/FBReactNativeSpec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
hermes-engine:
:podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
RCT-Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTRequired:
@@ -511,8 +436,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../node_modules/react-native/ReactCommon/cxxreact"
React-hermes:
:path: "../node_modules/react-native/ReactCommon/hermes"
React-jsi:
:path: "../node_modules/react-native/ReactCommon/jsi"
React-jsiexecutor:
@@ -521,16 +444,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
react-native-blur:
:path: "../node_modules/@react-native-community/blur"
react-native-cameraroll:
:path: "../node_modules/@react-native-camera-roll/camera-roll"
react-native-image-resizer:
:path: "../node_modules/@bam.tech/react-native-image-resizer"
react-native-pager-view:
:path: "../node_modules/react-native-pager-view"
react-native-paste-input:
:path: "../node_modules/@mattermost/react-native-paste-input"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-splash-screen:
@@ -543,8 +460,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
React-RCTAnimation:
:path: "../node_modules/react-native/Libraries/NativeAnimation"
React-RCTAppDelegate:
:path: "../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../node_modules/react-native/Libraries/Blob"
React-RCTImage:
@@ -563,36 +478,22 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
rn-fetch-blob:
:path: "../node_modules/rn-fetch-blob"
RNBackgroundFetch:
:path: "../node_modules/react-native-background-fetch"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNCClipboard:
:path: "../node_modules/@react-native-clipboard/clipboard"
RNFS:
:path: "../node_modules/react-native-fs"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNImageCropPicker:
:path: "../node_modules/react-native-image-crop-picker"
RNInAppBrowser:
:path: "../node_modules/react-native-inappbrowser-reborn"
RNNotifee:
:path: "../node_modules/@notifee/react-native"
RNReactNativeHapticFeedback:
:path: "../node_modules/react-native-haptic-feedback"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSVG:
:path: "../node_modules/react-native-svg"
segment-analytics-react-native:
:path: "../node_modules/@segment/analytics-react-native"
sovran-react-native:
:path: "../node_modules/@segment/sovran-react-native"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
@@ -600,66 +501,51 @@ SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: ad72713385db5289b19f1ead07e8e4aa26dcb01d
FBReactNativeSpec: df2602c11e33d310433496e28a48b4b2be652a61
FBLazyVector: a7a655862f6b09625d11c772296b01cd5164b648
FBReactNativeSpec: 81ce99032d5b586fddd6a38d450f8595f7e04be4
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 476ee3e89abb49e07f822b48323c51c57124b572
hermes-engine: f6e715aa6c8bd38de6c13bc85e07b0a337edaa89
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: fd4d923b964658aa0c4091a32c8b2004c6d9e3a6
RCTTypeSafety: c276d85975bde3d8448907235c70bf0da257adfd
React: e481a67971af1ce9639c9f746b753dd0e84ca108
React-callinvoker: 1051c04a94fa9d243786b86380606bad701a3b31
React-Codegen: 14b1e716d361d5ad95e0ce1a338f3fa0733a98b5
React-Core: 698fc3baecb80d511d987475a16d036cec6d287f
React-CoreModules: 59245305f41ff0adfeac334acc0594dea4585a7c
React-cxxreact: 49accd2954b0f532805dbcd1918fa6962f32f247
React-hermes: d068733294581a085e95b6024e8d951b005e26d3
React-jsi: 122b9bce14f4c6c7cb58f28f87912cfe091885fa
React-jsiexecutor: 60cf272aababc5212410e4249d17cea14fc36caa
React-jsinspector: ff56004b0c974b688a6548c156d5830ad751ae07
React-logger: 60a0b5f8bed667ecf9e24fecca1f30d125de6d75
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
react-native-cameraroll: 71d68167beb6fc7216aa564abb6d86f1d666a2c6
react-native-image-resizer: 794abf75ec13ed1f0dbb1f134e27504ea65e9e66
react-native-pager-view: 54bed894cecebe28cede54c01038d9d1e122de43
react-native-paste-input: 5182843692fd2ec72be50f241a38a49796e225d7
RCT-Folly: 4d8508a426467c48885f1151029bc15fa5d7b3b8
RCTRequired: 3e917ea5377751094f38145fdece525aa90545a0
RCTTypeSafety: c43c072a4bd60feb49a9570b0517892b4305c45e
React: 176dd882de001854ced260fad41bb68a31aa4bd0
React-callinvoker: c2864d1818d6e64928d2faf774a3800dfc38fe1f
React-Codegen: 98b6f97f0a7abf7d67e4ce435c77c05b7a95cf05
React-Core: fdaa2916b1c893f39f02cff0476d1fb0cab1e352
React-CoreModules: fd8705b80699ec36c2cdd635c2ce9d874b9cfdfc
React-cxxreact: 1832d971f7b0cb2c7b943dc0ec962762c90c906e
React-jsi: 72af715135abe8c3f0dcf3b2548b71d048b69a7e
React-jsiexecutor: b7b553412f2ec768fe6c8f27cd6bafdb9d8719e6
React-jsinspector: c5989c77cb89ae6a69561095a61cce56a44ae8e8
React-logger: a0833912d93b36b791b7a521672d8ee89107aff1
react-native-cameraroll: a40b082318eb1ecd0336a2f29d9f74b7f2c8cae8
react-native-pager-view: 592421df0259bf7a7a4fe85b74c24f3f39905605
react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
react-native-version-number: b415bbec6a13f2df62bf978e85bc0d699462f37f
React-perflogger: ec8eef2a8f03ecfa6361c2c5fb9197ef4a29cc85
React-RCTActionSheet: a0c023b86cf4c862fa9c4eb0f6f91fbe878fb2de
React-RCTAnimation: 168d53718c74153947c0109f55900faa64d79439
React-RCTAppDelegate: a8efbab128b34aa07a9491c85a41401210b1bec5
React-RCTBlob: 9bcbfc893bfda9f6b2eb016329d38c0f6366d31a
React-RCTImage: 3fcd4570b4b0f1ac2f4b4b6308dba33ce66c5b50
React-RCTLinking: 1edb8e1bb3fc39bf9e13c63d6aaaa3f0c3d18683
React-RCTNetwork: 500a79e0e0f67678077df727fabba87a55c043e1
React-RCTSettings: cc4414eb84ad756d619076c3999fecbf12896d6f
React-RCTText: 2a34261f3da6e34f47a62154def657546ebfa5e1
React-RCTVibration: 49d531ec8498e0afa2c9b22c2205784372e3d4f3
React-runtimeexecutor: 311feb67600774723fe10eb8801d3138cae9ad67
ReactCommon: 03be76588338a27a88d103b35c3c44a3fd43d136
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBackgroundFetch: 8e16176ff415daac743a6eb57afc8e9e14dbe623
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
React-perflogger: a18b4f0bd933b8b24ecf9f3c54f9bf65180f3fe6
React-RCTActionSheet: 547fe42fdb4b6089598d79f8e1d855d7c23e2162
React-RCTAnimation: bc9440a1c37b06ae9ebbb532d244f607805c6034
React-RCTBlob: a1295c8e183756d7ef30ba6e8f8144dfe8a19215
React-RCTImage: a30d1ee09b1334067fbb6f30789aae2d7ac150c9
React-RCTLinking: ffc6d5b88d1cb9aca13c54c2ec6507fbf07f2ac4
React-RCTNetwork: f807a2facab6cf5cf36d592e634611de9cf12d81
React-RCTSettings: 861806819226ed8332e6a8f90df2951a34bb3e7f
React-RCTText: f3fb464cc41a50fc7a1aba4deeb76a9ad8282cb9
React-RCTVibration: 79040b92bfa9c3c2d2cb4f57e981164ec7ab9374
React-runtimeexecutor: b960b687d2dfef0d3761fbb187e01812ebab8b23
ReactCommon: 095366164a276d91ea704ce53cb03825c487a3f2
RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3
RNGestureHandler: 28ad20bf02257791f7f137b31beef34b9549f54b
RNImageCropPicker: 648356d68fbf9911a1016b3e3723885d28373eda
RNInAppBrowser: e36d6935517101ccba0e875bac8ad7b0cb655364
RNNotifee: da8dcf09f079ea22f46e239d7c406e10d4525a5f
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
RNReanimated: d8d9d3d3801bda5e35e85cdffc871577d044dc2e
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
RNSVG: 6adc5c52d2488a476248413064b7f2832e639057
segment-analytics-react-native: cb097e393c3560a0d4cfd877044293e37b0050d9
sovran-react-native: fd3dc8f1a4b14acdc4ad25fc6b4ac4f52a2a2a15
Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b
RNReanimated: 5bdcbcc3a72aedeee7bb099604262403aa75a1e5
RNScreens: 0df01424e9e0ed7827200d6ed1087ddd06c493f9
RNSVG: ecd661f380a07ba690c9c5929c475a44f432d674
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
Yoga: 921eb014669cf9c718ada68b08d362517d564e0c
Yoga: 99652481fcd320aefa4a7ef90095b95acd181952
PODFILE CHECKSUM: 32d3f4ec61d2c786d46fd58000f53917922dbb8a
PODFILE CHECKSUM: cf94853ebcb0d8e0d027dca9ab7a4ede886a8f20
COCOAPODS: 1.11.3
+76 -117
View File
@@ -11,9 +11,9 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
5698CA584FD738B2091BD18F /* libPods-app-appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0C7BDE7769B84011D3747DC /* libPods-app-appTests.a */; };
67BF1AE6AABFC881715B2D6A /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8789F612EDA2C48C6064ADD6 /* libPods-app.a */; };
E46FE2C12981C0DB007C107C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E46FE2C02981C0DB007C107C /* LaunchScreen.storyboard */; };
379AA1F602EF704C84D7A02B /* libPods-app-appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 05B90550DA1387B5C44FB80A /* libPods-app-appTests.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
8896F504D7727F539A45F5E3 /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3B8692C2BCABAF5171340D /* libPods-app.a */; };
E4BBD590292C1F5200296224 /* app.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = E4437C9E28581FA7006DA9E7 /* app.entitlements */; };
/* End PBXBuildFile section */
@@ -31,21 +31,21 @@
00E356EE1AD99517003FC87E /* appTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = appTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* appTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = appTests.m; sourceTree = "<group>"; };
05B90550DA1387B5C44FB80A /* libPods-app-appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app-appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07F961A680F5B00A75B9A /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = app/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = app/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = app/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = app/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = app/main.m; sourceTree = "<group>"; };
53DBA218C184B95B107AC33E /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
8789F612EDA2C48C6064ADD6 /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
8BB4EDB104E125B8A1913E74 /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
A8E093A0B5DA947150924A68 /* Pods-app-appTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app-appTests.release.xcconfig"; path = "Target Support Files/Pods-app-appTests/Pods-app-appTests.release.xcconfig"; sourceTree = "<group>"; };
C01FB6762BC17DADC0319338 /* Pods-app-appTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app-appTests.debug.xcconfig"; path = "Target Support Files/Pods-app-appTests/Pods-app-appTests.debug.xcconfig"; sourceTree = "<group>"; };
C0C7BDE7769B84011D3747DC /* libPods-app-appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app-appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = app/LaunchScreen.storyboard; sourceTree = "<group>"; };
98E4A0BAD72B0C24AF5986D9 /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
9FA2BF4F6574C986E6B102F0 /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
BC19D4073A1A93D07CD74999 /* Pods-app-appTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app-appTests.release.xcconfig"; path = "Target Support Files/Pods-app-appTests/Pods-app-appTests.release.xcconfig"; sourceTree = "<group>"; };
E1EFCDF82E4E346591FBB028 /* Pods-app-appTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app-appTests.debug.xcconfig"; path = "Target Support Files/Pods-app-appTests/Pods-app-appTests.debug.xcconfig"; sourceTree = "<group>"; };
E4437C9E28581FA7006DA9E7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = app.entitlements; path = app/app.entitlements; sourceTree = "<group>"; };
E46FE2C02981C0DB007C107C /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
FC3B8692C2BCABAF5171340D /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -53,7 +53,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5698CA584FD738B2091BD18F /* libPods-app-appTests.a in Frameworks */,
379AA1F602EF704C84D7A02B /* libPods-app-appTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -61,7 +61,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
67BF1AE6AABFC881715B2D6A /* libPods-app.a in Frameworks */,
8896F504D7727F539A45F5E3 /* libPods-app.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -93,8 +93,8 @@
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB71A68108700A75B9A /* main.m */,
E46FE2C02981C0DB007C107C /* LaunchScreen.storyboard */,
);
name = app;
sourceTree = "<group>";
@@ -103,8 +103,8 @@
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
8789F612EDA2C48C6064ADD6 /* libPods-app.a */,
C0C7BDE7769B84011D3747DC /* libPods-app-appTests.a */,
FC3B8692C2BCABAF5171340D /* libPods-app.a */,
05B90550DA1387B5C44FB80A /* libPods-app-appTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -143,10 +143,10 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup;
children = (
8BB4EDB104E125B8A1913E74 /* Pods-app.debug.xcconfig */,
53DBA218C184B95B107AC33E /* Pods-app.release.xcconfig */,
C01FB6762BC17DADC0319338 /* Pods-app-appTests.debug.xcconfig */,
A8E093A0B5DA947150924A68 /* Pods-app-appTests.release.xcconfig */,
98E4A0BAD72B0C24AF5986D9 /* Pods-app.debug.xcconfig */,
9FA2BF4F6574C986E6B102F0 /* Pods-app.release.xcconfig */,
E1EFCDF82E4E346591FBB028 /* Pods-app-appTests.debug.xcconfig */,
BC19D4073A1A93D07CD74999 /* Pods-app-appTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -158,12 +158,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "appTests" */;
buildPhases = (
A6849EFFDCB214D4F4EAE972 /* [CP] Check Pods Manifest.lock */,
420689A074E4E7426A392B09 /* [CP] Check Pods Manifest.lock */,
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
DFA0C2B14E2F369C4B2337CE /* [CP] Copy Pods Resources */,
B73FFC16945F7B215A06B698 /* [CP] Embed Pods Frameworks */,
C5618360E9363F58ED30C68E /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -179,14 +178,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */;
buildPhases = (
DDF15D430A078CE70E577FDA /* [CP] Check Pods Manifest.lock */,
1C451C95EB728E95B9CDD2FF /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
45E56D79C207C80AF89FBAA2 /* [CP] Copy Pods Resources */,
B0229D18A3C8908C642F9131 /* [CP] Embed Pods Frameworks */,
BC7FC72051CB585D346FAE10 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -245,8 +243,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E46FE2C12981C0DB007C107C /* LaunchScreen.storyboard in Resources */,
E4BBD590292C1F5200296224 /* app.entitlements in Resources */,
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -260,90 +258,15 @@
files = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env.local",
"$(SRCROOT)/.xcode.env",
);
name = "Bundle React Native code and images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
shellScript = "set -e\n\nexport BUNDLE_COMMAND=\"ram-bundle\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
45E56D79C207C80AF89FBAA2 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources.sh\"\n";
showEnvVarsInLog = 0;
};
A6849EFFDCB214D4F4EAE972 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-app-appTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
B0229D18A3C8908C642F9131 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
B73FFC16945F7B215A06B698 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app-appTests/Pods-app-appTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
DDF15D430A078CE70E577FDA /* [CP] Check Pods Manifest.lock */ = {
1C451C95EB728E95B9CDD2FF /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -365,7 +288,46 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DFA0C2B14E2F369C4B2337CE /* [CP] Copy Pods Resources */ = {
420689A074E4E7426A392B09 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-app-appTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
BC7FC72051CB585D346FAE10 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C5618360E9363F58ED30C68E /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -434,7 +396,7 @@
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C01FB6762BC17DADC0319338 /* Pods-app-appTests.debug.xcconfig */;
baseConfigurationReference = E1EFCDF82E4E346591FBB028 /* Pods-app-appTests.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
@@ -462,7 +424,7 @@
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A8E093A0B5DA947150924A68 /* Pods-app-appTests.release.xcconfig */;
baseConfigurationReference = BC19D4073A1A93D07CD74999 /* Pods-app-appTests.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
@@ -487,7 +449,7 @@
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BB4EDB104E125B8A1913E74 /* Pods-app.debug.xcconfig */;
baseConfigurationReference = 98E4A0BAD72B0C24AF5986D9 /* Pods-app.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@@ -497,7 +459,7 @@
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
INFOPLIST_FILE = app/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -517,7 +479,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 53DBA218C184B95B107AC33E /* Pods-app.release.xcconfig */;
baseConfigurationReference = 9FA2BF4F6574C986E6B102F0 /* Pods-app.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
@@ -526,12 +488,11 @@
DEVELOPMENT_TEAM = B3LX46C5HS;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
INFOPLIST_FILE = app/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -577,7 +538,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -593,7 +554,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
@@ -611,7 +572,6 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
name = Debug;
@@ -649,7 +609,7 @@
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -658,7 +618,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
@@ -675,7 +635,6 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
+4 -2
View File
@@ -1,6 +1,8 @@
#import <RCTAppDelegate.h>
#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : RCTAppDelegate
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
@property (nonatomic, strong) UIWindow *window;
@end
+87 -16
View File
@@ -1,6 +1,10 @@
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
// universal links
#import <React/RCTLinkingManager.h>
@@ -8,21 +12,59 @@
// splash screen
#import "RNSplashScreen.h"
#import <TSBackgroundFetch/TSBackgroundFetch.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Show the splash screen
// [RNSplashScreen show];
// Register BackgroundFetch
[[TSBackgroundFetch sharedInstance] didFinishLaunching];
RCTAppSetupPrepareApp(application);
self.moduleName = @"xyz.blueskyweb.app";
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"xyz.blueskyweb.app", nil);
if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
// Show the splash screen
[RNSplashScreen show];
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
@@ -34,16 +76,45 @@
#endif
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
return true;
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
// universal links
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Some files were not shown because too many files have changed in this diff Show More