react-native-safe-area-context#SafeAreaView JavaScript Examples
The following examples show how to use
react-native-safe-area-context#SafeAreaView.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Subscribe.js From actual with MIT License | 6 votes |
export function Subscribe({ route, navigation, getUserData, createBudget }) {
let { email, userId, key } = route.params || {};
let textStyle = [
styles.text,
{ fontSize: 17, lineHeight: 25, color: 'white' }
];
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAvoidingView>
{/* <StatusBar barStyle="light-content" /> */}
<Header navigation={navigation} buttons={['back']} />
<Stack justify="center" style={{ flex: 1, padding: 20 }} spacing={8}>
<View>
<Text style={textStyle}>
You{"'"}re almost there. You need to subscribe to gain access to
Actual. No charges will be made for 1 month.
</Text>
</View>
<View style={{ alignItems: 'center' }}>
<Text style={[textStyle, { fontWeight: '700', marginBottom: 5 }]}>
Start with a 1 month free trial.
</Text>
<AccountButton
navigation={navigation}
userData={{ id: userId, key, email }}
darkMode={true}
useDummyPurchaser={true}
/>
</View>
</Stack>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Example #2
Source File: ProfileScreen.js From hero with MIT License | 6 votes |
ProfileScreen = () => {
return (
<SafeAreaView style={styles.appContainer}>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="dark-content"
/>
<View style={styles.header}>
<Text style={styles.appTitle}>profile</Text>
</View>
<View style={styles.footer}>
<Text style={styles.p}>Designed by Gino Lee Swanepoel</Text>
<Text style={styles.p}>in React Native</Text>
</View>
</SafeAreaView>
);
}
Example #3
Source File: index.js From musicont with MIT License | 6 votes |
Index = ({ songs }) => {
const [assets] = useAssets([require('../../assets/icons/hamburger.png'), require('../../assets/icons/search.png')]);
const [drawer, setDrawer] = useState(false);
return (
<Drawer active={drawer} current="favourite" onItemPressed={() => setDrawer(false)}>
<SafeAreaView style={styles.container}>
<Header
options={{
left: {
children: drawer ? <Icon name="x" color="#C4C4C4" /> : <Image source={require('../../assets/icons/hamburger.png')} resizeMode="contain" />,
onPress: () => setDrawer(!drawer),
},
middle: {
show: true,
text: 'My Favourites',
},
right: {
show: false,
},
}}
/>
<View style={styles.sections}>
{songs && songs.length > 0 ? (
<Section.MusicList audios={songs} indicator={false} useIndex={true} />
) : (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', color: 'rgba(0, 0, 0, .3)' }}>No favourite yet!</Text>
</View>
)}
</View>
</SafeAreaView>
</Drawer>
);
}
Example #4
Source File: index.js From musicont with MIT License | 6 votes |
Index = () => {
const [assets] = useAssets([require('../../assets/icons/hamburger.png'), require('../../assets/icons/search.png')]);
const [drawer, setDrawer] = useState(false);
return (
<Drawer active={drawer} current="home" onItemPressed={() => setDrawer(false)}>
<SafeAreaView style={styles.container}>
<Header
options={{
left: {
children: drawer ? <Icon name="x" color="#C4C4C4" /> : <Image source={require('../../assets/icons/hamburger.png')} resizeMode="contain" />,
onPress: () => setDrawer(!drawer),
},
}}
/>
<View style={styles.sections}>
<Section.Explore />
<Section.Recent style={{ marginTop: 30 }} />
<Section.Playlist style={{ marginTop: 30 }} />
</View>
<Footer />
</SafeAreaView>
</Drawer>
);
}
Example #5
Source File: index.js From musicont with MIT License | 6 votes |
Index = ({ songs }) => {
const [assets] = useAssets([require('../../assets/icons/hamburger.png'), require('../../assets/icons/search.png')]);
const [drawer, setDrawer] = useState(false);
return (
<Drawer active={drawer} current="recent" onItemPressed={() => setDrawer(false)}>
<SafeAreaView style={styles.container}>
<Header
options={{
left: {
children: drawer ? <Icon name="x" color="#C4C4C4" /> : <Image source={require('../../assets/icons/hamburger.png')} resizeMode="contain" />,
onPress: () => setDrawer(!drawer),
},
middle: {
show: true,
text: 'Recently Played',
},
right: {
show: false,
},
}}
/>
<View style={styles.sections}>
{songs && songs.length > 0 ? (
<Section.MusicList audios={songs} indicator={false} useIndex={true} />
) : (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', color: 'rgba(0, 0, 0, .3)' }}>No recent yet!</Text>
</View>
)}
</View>
</SafeAreaView>
</Drawer>
);
}
Example #6
Source File: index.js From musicont with MIT License | 6 votes |
Index = ({ songs }) => {
const [assets] = useAssets([require('../../assets/icons/hamburger.png'), require('../../assets/icons/search.png')]);
const [drawer, setDrawer] = useState(false);
return (
<Drawer active={drawer} current="songs" onItemPressed={() => setDrawer(false)}>
<SafeAreaView style={styles.container}>
<Header
options={{
left: {
children: drawer ? <Icon name="x" color="#C4C4C4" /> : <Image source={require('../../assets/icons/hamburger.png')} resizeMode="contain" />,
onPress: () => setDrawer(!drawer),
},
middle: {
show: true,
text: 'All Songs',
},
right: {
show: false,
},
}}
/>
<View style={styles.sections}>
<Section.MusicList audios={songs} indicator={false} />
</View>
</SafeAreaView>
</Drawer>
);
}
Example #7
Source File: SignModal.js From reddit-clone with MIT License | 6 votes |
SignModal = ({ navigation }) => {
const { colors } = useTheme()
return (
<TouchableWithoutFeedback onPress={() => navigation.goBack()}>
<View as={SafeAreaView} style={styles.container}>
<StatusBar hidden />
<View
style={[styles.modal, { backgroundColor: colors.background }]}
onStartShouldSetResponder={() => true}
>
<View style={styles.buttonContainer}>
<Button
bgColor={colors.signUpButton}
title="Sign Up"
onPress={() => navigation.navigate('SignUp')}
>
<PlusCircle color={colors.white} />
</Button>
<Button
bgColor={colors.signInButton}
title="Sign In"
onPress={() => navigation.navigate('SignIn')}
>
<LogIn color={colors.white} />
</Button>
</View>
</View>
</View>
</TouchableWithoutFeedback>
)
}
Example #8
Source File: App.js From redis-examples with MIT License | 6 votes |
render(){
this.height = Math.round(Dimensions.get('screen').height);
this.width = Math.round(Dimensions.get('screen').width);
return (
<SafeAreaView style={{
width: this.width,
height: this.height,
flex: 1,
alignItems: 'center'}}>
<StatusBar
backgroundColor="#f4511e"/>
</SafeAreaView>
);
}
Example #9
Source File: Leaderboard.js From redis-examples with MIT License | 6 votes |
render() {
const {navigate} = this.props.navigation;
this.height = Math.round(Dimensions.get('screen').height);
this.width = Math.round(Dimensions.get('screen').width);
return (
<SafeAreaView
style={{
width: this.width,
height: this.height,
flex: 1,
alignItems: 'center'
}}>
<StatusBar
backgroundColor="#f4511e"/>
<View
style={{
height:this.height,
width: this.width
}}>
<FlatList
style={{
flex: 1,
flexDirection: 'column',
}}
renderItem={() => this.renderPlayerItems()}
data={[{bos: 'boş', key: 'key'}]}
refreshing={true}></FlatList>
</View>
</SafeAreaView>
);
}
Example #10
Source File: index.js From MediBuddy with MIT License | 6 votes |
Footer = () => (
<SafeAreaView>
<Divider />
<View style={styles.footer}>
<View style={{ flex: 1 }}>
<Button
style={styles.btn}
labelStyle={styles.cancel}
mode="text"
onPress={() => console.log('Pressed')}>
Cancel
</Button>
</View>
<View style={{ flex: 1 }}>
<Button
style={styles.btn}
labelStyle={styles.ok}
mode="text"
onPress={() => console.log('Pressed')}>
Reschedule
</Button>
</View>
</View>
</SafeAreaView>
)
Example #11
Source File: HomeScreen.js From WhatsApp-Clone with MIT License | 6 votes |
HomeScreen = ({children, style, navigation, ...rest}) => {
useEffect(() => {
registerStateChangeListener();
sendPageLoadStatus()
return () => {
// Clean up the subscription
unRgisterStateChangeListener()
};
}, []);
function registerStateChangeListener() {
AppState.addEventListener('change', handleAppStateChange);
}
function unRgisterStateChangeListener() {
AppState.removeEventListener('change', handleAppStateChange);
}
return (
<SafeAreaView style={DEFAULT_STYLES.container}>
<View style={DEFAULT_STYLES.container}>
{/* <Header hasTabs style={styles.headerStyle}> */}
<HomeHeader navigation={navigation} />
{/* </Header> */}
<TabView navigation={navigation} />
</View>
</SafeAreaView>
);
}
Example #12
Source File: index.js From actual with MIT License | 5 votes |
render() {
const {
currentMonth,
bounds,
editMode,
initialized,
showBudgetDetails
} = this.state;
const {
categories,
categoryGroups,
prefs,
budgetType,
navigation,
applyBudgetAction
} = this.props;
let numberFormat = prefs.numberFormat || 'comma-dot';
if (!categoryGroups || !initialized) {
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
}}
>
<AnimatedLoading width={25} height={25} />
</View>
);
}
return (
<SafeAreaView
edges={['top']}
style={{ flex: 1, backgroundColor: colors.p5 }}
>
<FocusAwareStatusBar barStyle="light-content" />
<SyncRefresh onSync={this.sync}>
{({ refreshing, onRefresh }) => (
<BudgetTable
// This key forces the whole table rerender when the number
// format changes
key={numberFormat}
categories={categories}
categoryGroups={categoryGroups}
type={budgetType}
month={currentMonth}
monthBounds={bounds}
editMode={editMode}
navigation={navigation}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
onEditMode={flag => this.setState({ editMode: flag })}
onShowBudgetDetails={this.onShowBudgetDetails}
onPrevMonth={this.onPrevMonth}
onNextMonth={this.onNextMonth}
onAddCategory={this.onAddCategory}
onReorderCategory={this.onReorderCategory}
onReorderGroup={this.onReorderGroup}
onOpenActionSheet={this.onOpenActionSheet}
onBudgetAction={applyBudgetAction}
/>
)}
</SyncRefresh>
{showBudgetDetails && (
<BudgetSummary
month={currentMonth}
onClose={() => this.setState({ showBudgetDetails: false })}
/>
)}
</SafeAreaView>
);
}
Example #13
Source File: BudgetList.js From actual with MIT License | 5 votes |
render() {
let {
navigation,
files,
loadAllFiles,
getUserData,
showActionSheetWithOptions,
keyId
} = this.props;
return (
<SafeAreaView edges={['bottom']} style={{ flex: 1 }}>
<Modal
title="Select a file"
backgroundColor="white"
allowScrolling={false}
showOverlay={false}
edges={['top']}
rightButton={
<RefreshButton
onRefresh={() => {
getUserData();
loadAllFiles();
}}
/>
}
>
{/* <StatusBar barStyle="light-content" /> */}
<FlatList
data={files}
ListEmptyComponent={EmptyMessage}
renderItem={({ item: file }) => (
<File
file={file}
showActionSheetWithOptions={showActionSheetWithOptions}
onSelect={() => {
if (file.state === 'broken') {
showBrokenMessage(file, showActionSheetWithOptions, () =>
this.onDelete(file)
);
} else if (file.state === 'remote') {
this.props.downloadBudget(file.cloudFileId);
} else {
this.props.loadBudget(file.id);
}
}}
onDelete={this.onDelete}
/>
)}
keyExtractor={item => item.id}
style={{ flex: 1 }}
/>
<View
style={{
alignItems: 'center',
marginHorizontal: 10,
marginVertical: 15,
flexDirection: 'row'
}}
>
<Button primary style={{ flex: 1 }} onPress={() => this.onCreate()}>
New file
</Button>
</View>
</Modal>
<UserButton
navigation={navigation}
keyId={keyId}
onLogOut={() => {
iap.resetUser();
this.props.signOut();
}}
/>
</SafeAreaView>
);
}
Example #14
Source File: Login.js From actual with MIT License | 5 votes |
function Login({ navigation, createBudget }) {
let [email, setEmail] = useState('');
let [loading, setLoading] = useState(false);
let [error, setError] = useState(null);
async function sendCode() {
setLoading(true);
setError(null);
let { error } = await send('subscribe-send-email-code', { email });
setLoading(false);
if (error) {
setError(getErrorMessage(error));
} else {
navigation.navigate('Confirm', { email });
}
}
let textStyle = [
styles.text,
{ fontSize: 17, lineHeight: 25, color: 'white' }
];
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAvoidingView>
<TransitionView navigation={navigation}>
{/* <StatusBar barStyle="light-content" /> */}
<Header
navigation={navigation}
buttons={['back', 'demo']}
loadDemoBudget={() => {
send('track', { name: 'app:create-demo' });
createBudget({ demoMode: true });
}}
/>
<Stack justify="center" style={{ flex: 1, padding: 20 }} spacing={5}>
<View>
<Text style={textStyle}>
<Text style={{ fontWeight: '700' }}>Sign in.</Text> We
{"'"}
ll email you a code that you can use to log in. You only need to
do this once. Right now, the mobile app works best as a
companion to the desktop app.
</Text>
</View>
<SingleInput
title="Email"
value={email}
loading={loading}
error={error}
inputProps={{
keyboardType: 'email-address',
placeholder: '[email protected]'
}}
onChange={setEmail}
onSubmit={sendCode}
/>
</Stack>
</TransitionView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Example #15
Source File: index.js From musicont with MIT License | 5 votes |
Index = ({ songs, playlists, navigation }) => {
const [assets] = useAssets([require('../../assets/icons/hamburger.png'), require('../../assets/icons/search.png')]);
const [drawer, setDrawer] = useState(false);
return (
<Drawer active={drawer} current="playlist" onItemPressed={() => setDrawer(false)}>
<SafeAreaView style={styles.container}>
<Header
options={{
left: {
children: drawer ? <Icon name="x" color="#C4C4C4" /> : <Image source={require('../../assets/icons/hamburger.png')} resizeMode="contain" />,
onPress: () => setDrawer(!drawer),
},
middle: {
show: true,
text: 'Playlists',
},
right: {
show: false,
},
}}
/>
{playlists && playlists?.length > 0 ? (
<ScrollView style={{ flex: 1 }} contentContainerStyle={styles.sections} showsVerticalScrollIndicator={false}>
{playlists.map((playlist, key) => (
<Card.Playlist
key={key}
style={styles.item}
overlayStyle={{ height: 200 }}
imageURL={songs[playlist?.songs[0]]?.img}
title={playlist?.name}
subtitle={`${playlist?.songs.length} Songs`}
onPress={() => {
const playlistIndex = playlists.findIndex((i) => i?.name.toLowerCase() === playlist?.name.toLowerCase());
navigation.push(SCREENS.PLAYLIST, { playlistIndex });
}}
/>
))}
</ScrollView>
) : (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', color: 'rgba(0, 0, 0, .3)' }}>No playlists yet!</Text>
</View>
)}
</SafeAreaView>
</Drawer>
);
}
Example #16
Source File: index.js From musicont with MIT License | 5 votes |
Index = ({ songs }) => {
const { goBack } = useNavigation();
const [audios, setAudios] = useState([]);
const [search, setSearch] = useState('');
const handleInput = (val) => {
const value = val.replace(' ', ' ');
setSearch(value);
if (value.length > 3) {
const results = songs.filter((song) => {
let regex = new RegExp(value, 'ig');
return regex.exec(song?.title) || regex.exec(song?.author);
});
setAudios(results);
} else {
setAudios([]);
}
};
return (
<>
<StatusBar style="dark" />
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<View style={styles.input}>
<Icon name="search" color="#FFF" />
<TextInput style={styles.textInput} onChangeText={handleInput} value={search} returnKeyType="search" placeholder="Search..." />
</View>
<TouchableOpacity style={styles.btn} onPress={() => goBack()}>
<Text style={styles.btnTxt}>Cancel</Text>
</TouchableOpacity>
</View>
<View style={styles.result}>
{audios.length > 0 ? (
<Section.MusicList audios={audios} />
) : (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', color: 'rgba(0, 0, 0, .3)' }}>Search something...</Text>
</View>
)}
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
</>
);
}
Example #17
Source File: Home.js From reddit-clone with MIT License | 5 votes |
Home = () => {
const { authState } = React.useContext(AuthContext)
const { theme } = React.useContext(ThemeContext)
const { colors } = useTheme()
const [postData, setPostData] = React.useState(null)
const [category, setCategory] = React.useState('all')
const [isLoading, setIsLoaading] = React.useState(false)
const getPostData = React.useCallback(async () => {
setIsLoaading(true)
const { data } = await axios.get(
!category || category === 'all' ? 'posts' : `posts/${category}`
)
setPostData(data)
setIsLoaading(false)
}, [category])
React.useEffect(() => {
getPostData()
}, [getPostData])
return (
<View as={SafeAreaView} style={styles.container}>
<StatusBar
barStyle={theme === 'light' ? 'dark-content' : 'light-content'}
backgroundColor={colors.background}
/>
{postData ? (
<FlatList
data={postData}
extraData={isLoading}
refreshing={isLoading}
onRefresh={() => getPostData()}
keyExtractor={item => item.id}
ListHeaderComponent={
<CategoryPicker selectedCategory={category} onClick={setCategory} addAll />
}
ListHeaderComponentStyle={[styles.categoryPicker, { backgroundColor: colors.bgColor }]}
ListEmptyComponent={
<Text style={[styles.empty, { color: colors.text }]}>Ups! Not found any post!</Text>
}
renderItem={({ item, index }) => (
<Post
index={index}
postId={item.id}
userId={authState.userInfo.id}
score={item.score}
type={item.type}
title={item.title}
author={item.author}
category={item.category}
text={item.text}
comments={item.comments}
created={item.created}
url={item.url}
votes={item.votes}
views={item.views}
setIsLoaading={setIsLoaading}
setData={setPostData}
deleteButton={false}
/>
)}
/>
) : (
<>
<CategoryLoader />
{[1, 2, 3, 4, 5].map(i => (
<PostLoader key={i} />
))}
</>
)}
</View>
)
}
Example #18
Source File: User.js From reddit-clone with MIT License | 5 votes |
User = ({ route }) => {
const { authState } = React.useContext(AuthContext)
const { colors } = useTheme()
const [isLoading, setIsLoaading] = React.useState(false)
const [userPosts, setuserPosts] = React.useState(null)
const username = route.params?.username
const getUserPostDetail = React.useCallback(async () => {
setIsLoaading(true)
const { data } = await axios.get(`user/${username || authState.userInfo.username}`)
setuserPosts(data)
setIsLoaading(false)
}, [authState.userInfo.username, username])
React.useEffect(() => {
getUserPostDetail()
}, [getUserPostDetail])
const deletePost = async (postId, index) => {
setIsLoaading(true)
const { status } = await axios.delete(`post/${postId}`)
if (status === 200) {
setuserPosts(prevData => {
prevData.splice(index, 1)
return prevData
})
}
setIsLoaading(false)
}
return (
<View as={SafeAreaView} style={styles.boxCenter}>
{userPosts ? (
<FlatList
data={userPosts.sort((a, b) => a.created < b.created)}
extraData={isLoading}
refreshing={isLoading}
onRefresh={() => getUserPostDetail()}
keyExtractor={item => item.id}
ListEmptyComponent={
<Text style={[styles.empty, { color: colors.text }]}>Ups! Not found any post!</Text>
}
ListHeaderComponent={<HeaderComponent username={username} postCount={userPosts.length} />}
stickyHeaderIndices={[0]}
ListHeaderComponentStyle={styles.headerComponentStyle}
renderItem={({ item, index }) => (
<Post
index={index}
postId={item.id}
userId={authState.userInfo.id}
score={item.score}
type={item.type}
title={item.title}
author={item.author}
category={item.category}
text={item.text}
comments={item.comments}
created={item.created}
url={item.url}
votes={item.votes}
views={item.views}
setIsLoaading={setIsLoaading}
setData={setuserPosts}
deleteButton={true}
deletePost={() => deletePost(item.id, index)}
/>
)}
/>
) : (
<>
{[1, 2, 3, 4, 5].map(i => (
<PostLoader key={i} />
))}
</>
)}
</View>
)
}
Example #19
Source File: App.js From Legacy with Mozilla Public License 2.0 | 4 votes |
function App() {
let [fontsLoaded] = useFonts(Platform.OS == "web" ? {
// Coiny_400Regular,
} : {
Coiny_400Regular,
Roboto_100Thin,
Roboto_300Light,
Roboto_400Regular,
Roboto_500Medium,
Roboto_700Bold,
Roboto_900Black,
});
const loadingLogin = useSelector(i => i.loadingLogin || i.version < 0);
const version = useSelector(i => i.version);
const ref = React.useRef();
const dispatch = useDispatch();
const { t } = useTranslation();
const { getInitialState } = useLinking(ref, {
prefixes: ['https://cuppazee.app', 'cuppazee://'],
config: {
__primary: {
screens: {
...pageScreens,
Auth: 'auth',
},
}
},
});
var [isReady, setIsReady] = React.useState(false);
var [initialState, setInitialState] = React.useState();
var theme = useSelector(i => i.themes[i.theme])
React.useEffect(() => {
Promise.race([
getInitialState(),
new Promise(resolve =>
setTimeout(resolve, 150)
)
])
.catch(e => {
console.error(e);
})
.then(state => {
if (state !== undefined) {
setTimeout(() => dispatch(setCurrentRoute(state?.routes?.[0]?.state?.routes?.slice?.()?.reverse?.()?.[0] ?? {})), 100);
setInitialState(state);
}
setIsReady(true);
});
}, [getInitialState]);
function handleStateChange(a) {
dispatch(setCurrentRoute(a?.routes?.[0]?.state?.routes?.slice?.()?.reverse?.()?.[0] ?? {}))
}
if (!theme || !theme.page || !theme.page.bg) {
return <View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: 'white' }}>
<ActivityIndicator size="large" color="orange" />
</View>;
}
if (!fontsLoaded) {
return <View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: theme.page.bg }}>
<ActivityIndicator size="large" color={theme.page.fg} />
</View>;
}
if (loadingLogin) {
return <View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: theme.page.bg }}>
<Text allowFontScaling={false} style={{ ...font("bold"), fontSize: 20, color: theme.page.fg, marginBottom: 20 }}>{t('common:logging_in')}</Text>
<ActivityIndicator size="large" color={theme.page.fg} />
</View>;
}
if (version != Math.max(...Object.keys(changelogs).map(Number))) {
var arr = Object.keys(changelogs).map(Number).filter(i => i > version).slice(-10).sort((a, b) => a - b);
var logs = arr.map(i => [i, changelogs[i]])
return <SafeAreaView style={{ backgroundColor: theme.navigation.bg, height: "100%" }}>
<ScrollView
style={{ flex: 1 }}
contentContainerStyle={{ padding: 8, justifyContent: "center", alignItems: "center", flexGrow: 1 }}>
{logs.map(([build, log]) => <View style={{ maxWidth: "100%" }}>
<View style={{ alignItems: "center" }}>
<Text allowFontScaling={false} style={{ color: theme.navigation.fg, fontSize: 32, ...font("bold") }}>{t('changelog:build_n', { n: build })}</Text>
</View>
{log?.map(i => <View style={{ flexDirection: "row", alignItems: "center", width: 400, maxWidth: "100%" }}>
{i.image && <Image source={getIcon(i.image)} style={{ height: 48, width: 48 }} />}
{i.icon && <View style={{ height: 48, width: 48, backgroundColor: theme.page_content.bg, borderRadius: 24, justifyContent: "center", alignItems: "center" }}>
<MaterialCommunityIcons size={32} color={theme.page_content.fg} name={i.icon} />
</View>}
<View style={{ padding: 8, flex: 1 }}>
<Text allowFontScaling={false} style={{ color: theme.navigation.fg, fontSize: 20, ...font("bold") }}>{i.title}</Text>
<Text allowFontScaling={false} style={{ color: theme.navigation.fg, fontSize: 16, ...font() }}>{i.description}</Text>
</View>
</View>) ?? <Text allowFontScaling={false} style={{ ...font("bold"), fontSize: 20, color: theme.page.fg, marginBottom: 20 }}>{t('changelog:no_changelog')}</Text>}
{build == Math.max(...Object.keys(changelogs).map(Number)) && <Button mode="contained" style={{ borderWidth: theme.page_content.border ? 2 : 0, borderColor: theme.page_content.border }} color={theme.page_content.bg} onPress={() => {
dispatch(cuppazeeVersion(Math.max(...Object.keys(changelogs).map(Number))))
}}>{logs.some(i=>i[1].some(x=>x.privacy))?t('changelog:continue_and_agree'):t('changelog:continue')}</Button>}
</View>)}
</ScrollView>
</SafeAreaView>;
}
if (!isReady) {
return null;
}
var navWidth = 400;
return (
<NavigationContainer independent={true} onStateChange={handleStateChange} initialState={initialState} ref={ref}>
<StatusBar translucent={true} backgroundColor={theme.navigation.bg + 'cc'} barStyle="light-content" />
<View style={{ flex: 1 }}>
<DrawerNav />
{/* <View style={{position:"absolute",bottom:-0.5*navWidth,right:-0.5*navWidth,width:navWidth,height:navWidth,borderRadius:navWidth/2,paddingBottom:navWidth/2,paddingRight:navWidth/2,backgroundColor:"white"}}><Text>Hello</Text></View> */}
</View>
</NavigationContainer>
);
}
Example #20
Source File: App.js From filen-mobile with GNU Affero General Public License v3.0 | 4 votes |
App = memo(() => {
const [isLoggedIn, setIsLoggedIn] = useMMKVBoolean("isLoggedIn", storage)
const setDimensions = useStore(useCallback(state => state.setDimensions))
const [darkMode, setDarkMode] = useMMKVBoolean("darkMode", storage)
const [setupDone, setSetupDone] = useState(false)
const [currentScreenName, setCurrentScreenName] = useState("MainScreen")
const setCurrentRoutes = useStore(useCallback(state => state.setCurrentRoutes))
const toastBottomOffset = useStore(useCallback(state => state.toastBottomOffset))
const toastTopOffset = useStore(useCallback(state => state.toastTopOffset))
const setNetInfo = useStore(useCallback(state => state.setNetInfo))
const showNavigationAnimation = useStore(useCallback(state => state.showNavigationAnimation))
const [userId, setUserId] = useMMKVNumber("userId", storage)
const [cameraUploadEnabled, setCameraUploadEnabled] = useMMKVBoolean("cameraUploadEnabled:" + userId, storage)
const setBiometricAuthScreenState = useStore(useCallback(state => state.setBiometricAuthScreenState))
const setCurrentShareItems = useStore(useCallback(state => state.setCurrentShareItems))
const setAppState = useStore(useCallback(state => state.setAppState))
const [lang, setLang] = useMMKVString("lang", storage)
const [nodeJSAlive, setNodeJSAlive] = useState(true)
const setContentHeight = useStore(useCallback(state => state.setContentHeight))
const isDeviceReady = useStore(useCallback(state => state.isDeviceReady))
const [startOnCloudScreen, setStartOnCloudScreen] = useMMKVBoolean("startOnCloudScreen:" + userId, storage)
const [userSelectedTheme, setUserSelectedTheme] = useMMKVString("userSelectedTheme", storage)
const [currentDimensions, setCurrentDimensions] = useState({ window: Dimensions.get("window"), screen: Dimensions.get("screen") })
const handleShare = useCallback(async (items) => {
if(!items){
return false
}
if(typeof items !== "undefined"){
if(typeof items.data !== "undefined"){
if(items.data !== null){
if(items.data.length > 0){
await new Promise((resolve) => {
const wait = BackgroundTimer.setInterval(() => {
if(typeof navigationRef !== "undefined"){
const navState = navigationRef.getState()
if(typeof navState !== "undefined"){
if(typeof navState.routes !== "undefined"){
if(navState.routes.filter(route => route.name == "SetupScreen" || route.name == "BiometricAuthScreen" || route.name == "LoginScreen").length == 0){
if(storage.getBoolean("isLoggedIn")){
BackgroundTimer.clearInterval(wait)
return resolve()
}
}
}
}
}
}, 250)
})
let containsValidItems = true
if(Platform.OS == "android"){
if(Array.isArray(items.data)){
for(let i = 0; i < items.data.length; i++){
if(items.data[i].indexOf("file://") == -1 && items.data[i].indexOf("content://") == -1){
containsValidItems = false
}
}
}
else{
if(items.data.indexOf("file://") == -1 && items.data.indexOf("content://") == -1){
containsValidItems = false
}
}
}
else{
for(let i = 0; i < items.data.length; i++){
if(items.data[i].data.indexOf("file://") == -1 && items.data[i].data.indexOf("content://") == -1){
containsValidItems = false
}
}
}
if(containsValidItems){
setCurrentShareItems(items)
showToast({ type: "upload" })
}
else{
showToast({ message: i18n(lang, "shareMenuInvalidType") })
}
}
}
}
}
})
const initBackgroundFetch = useCallback(() => {
BackgroundFetch.configure({
minimumFetchInterval: 15,
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY,
stopOnTerminate: false,
startOnBoot: true,
enableHeadless: false
}, (taskId) => {
console.log("[" + Platform.OS + "] BG fetch running:", taskId)
const waitForInit = (callback) => {
const timeout = (+new Date() + 15000)
const wait = BackgroundTimer.setInterval(() => {
if(timeout > (+new Date())){
if(isLoggedIn && cameraUploadEnabled && setupDone && isDeviceReady){
BackgroundTimer.clearInterval(wait)
return callback(false)
}
}
else{
BackgroundTimer.clearInterval(wait)
return callback(true)
}
}, 10)
}
waitForInit((timedOut) => {
if(timedOut){
console.log("[" + Platform.OS + "] BG fetch timed out:", taskId)
BackgroundFetch.finish(taskId)
}
else{
runCameraUpload({
runOnce: true,
maxQueue: 1,
callback: () => {
console.log("[" + Platform.OS + "] BG fetch done:", taskId)
BackgroundFetch.finish(taskId)
}
})
}
})
}, (taskId) => {
console.log("[" + Platform.OS + "] BG fetch timeout:", taskId)
BackgroundFetch.finish(taskId)
}).then((status) => {
console.log("[" + Platform.OS + "] BG fetch init status:", status)
}).catch((err) => {
console.log("[" + Platform.OS + "] BG fetch init error:", err)
})
})
const setAppearance = useCallback(() => {
BackgroundTimer.setTimeout(() => {
if(typeof userSelectedTheme == "string" && userSelectedTheme.length > 1){
if(userSelectedTheme == "dark"){
setDarkMode(true)
setStatusBarStyle(true)
}
else{
setDarkMode(false)
setStatusBarStyle(false)
}
}
else{
if(Appearance.getColorScheme() == "dark"){
setDarkMode(true)
setStatusBarStyle(true)
}
else{
setDarkMode(false)
setStatusBarStyle(false)
}
}
}, 1000) // We use a timeout due to the RN appearance event listener firing both "dark" and "light" on app resume which causes the screen to flash for a second
})
useEffect(() => {
if(isLoggedIn && cameraUploadEnabled && setupDone){
runCameraUpload({
maxQueue: 10,
runOnce: false,
callback: undefined
})
}
}, [isLoggedIn, cameraUploadEnabled, setupDone])
useEffect(() => {
initBackgroundFetch()
//global.nodeThread.pingPong(() => {
// setNodeJSAlive(false)
//})
NetInfo.fetch().then((state) => {
setNetInfo(state)
}).catch((err) => {
console.log(err)
})
//BackgroundTimer.start()
const appStateListener = AppState.addEventListener("change", (nextAppState) => {
setAppState(nextAppState)
if(nextAppState == "background"){
if(Math.floor(+new Date()) > storage.getNumber("biometricPinAuthTimeout:" + userId) && storage.getBoolean("biometricPinAuth:" + userId)){
setBiometricAuthScreenState("auth")
storage.set("biometricPinAuthTimeout:" + userId, (Math.floor(+new Date()) + 500000))
navigationRef.current.dispatch(StackActions.push("BiometricAuthScreen"))
}
}
if(nextAppState == "active"){
checkAppVersion({ navigation: navigationRef })
}
})
const netInfoListener = NetInfo.addEventListener((state) => {
setNetInfo(state)
})
const dimensionsListener = Dimensions.addEventListener("change", ({ window, screen }) => {
setDimensions({ window, screen })
setCurrentDimensions({ window, screen })
})
const navigationRefListener = navigationRef.addListener("state", (event) => {
if(typeof event.data !== "undefined"){
if(typeof event.data.state !== "undefined"){
if(typeof event.data.state.routes !== "undefined"){
//console.log("Current Screen:", event.data.state.routes[event.data.state.routes.length - 1].name, event.data.state.routes[event.data.state.routes.length - 1].params)
setCurrentScreenName(event.data.state.routes[event.data.state.routes.length - 1].name)
setCurrentRoutes(event.data.state.routes)
}
}
}
})
ShareMenu.getInitialShare(handleShare)
const shareMenuListener = ShareMenu.addNewShareListener(handleShare)
setAppearance()
const appearanceListener = Appearance.addChangeListener(() => {
setAppearance()
})
if(isLoggedIn && !setupDone){
setup({ navigation: navigationRef }).then(() => {
setSetupDone(true)
if(storage.getBoolean("biometricPinAuth:" + userId)){
setBiometricAuthScreenState("auth")
storage.set("biometricPinAuthTimeout:" + userId, (Math.floor(+new Date()) + 500000))
navigationRef.current.dispatch(StackActions.push("BiometricAuthScreen"))
}
else{
navigationRef.current.dispatch(CommonActions.reset({
index: 0,
routes: [
{
name: "MainScreen",
params: {
parent: startOnCloudScreen ? (storage.getBoolean("defaultDriveOnly:" + userId) ? storage.getString("defaultDriveUUID:" + userId) : "base") : "recents"
}
}
]
}))
}
}).catch((err) => {
console.log(err)
if(typeof storage.getString("masterKeys") == "string" && typeof storage.getString("apiKey") == "string" && typeof storage.getString("privateKey") == "string" && typeof storage.getString("publicKey") == "string" && typeof storage.getNumber("userId") == "number"){
if(storage.getString("masterKeys").length > 16 && storage.getString("apiKey").length > 16 && storage.getString("privateKey").length > 16 && storage.getString("publicKey").length > 16 && storage.getNumber("userId") !== 0){
setSetupDone(true)
if(storage.getBoolean("biometricPinAuth:" + userId)){
setBiometricAuthScreenState("auth")
storage.set("biometricPinAuthTimeout:" + userId, (Math.floor(+new Date()) + 500000))
navigationRef.current.dispatch(StackActions.push("BiometricAuthScreen"))
}
else{
navigationRef.current.dispatch(CommonActions.reset({
index: 0,
routes: [
{
name: "MainScreen",
params: {
parent: startOnCloudScreen ? (storage.getBoolean("defaultDriveOnly:" + userId) ? storage.getString("defaultDriveUUID:" + userId) : "base") : "recents"
}
}
]
}))
}
}
else{
setSetupDone(false)
showToast({ message: i18n(lang, "appSetupNotPossible") })
}
}
else{
setSetupDone(false)
showToast({ message: i18n(lang, "appSetupNotPossible") })
}
})
}
// Reset on app launch
storage.set("cameraUploadRunning", false)
return () => {
dimensionsListener.remove()
shareMenuListener.remove()
navigationRef.removeListener(navigationRefListener)
navigationRefListener()
appearanceListener.remove()
netInfoListener()
appStateListener.remove()
}
}, [])
return (
<>
<NavigationContainer ref={navigationRef}>
<Fragment>
<SafeAreaProvider style={{
backgroundColor: darkMode ? "black" : "white",
}}>
<SafeAreaView mode="padding" style={{
backgroundColor: currentScreenName == "ImageViewerScreen" ? "black" : (darkMode ? "black" : "white"),
paddingTop: Platform.OS == "android" ? 5 : 5,
height: "100%",
width: "100%"
}}>
<View style={{
width: currentScreenName == "ImageViewerScreen" ? currentDimensions.screen.width : "100%",
height: currentScreenName == "ImageViewerScreen" ? currentDimensions.screen.height : "100%",
backgroundColor: darkMode ? "black" : "white"
}} onLayout={(e) => setContentHeight(e.nativeEvent.layout.height)}>
{
nodeJSAlive ? (
<>
<Stack.Navigator initialRouteName={isLoggedIn ? (setupDone ? "MainScreen" : "SetupScreen") : "LoginScreen"} screenOptions={{
contentStyle: {
backgroundColor: darkMode ? "black" : "white"
},
headerStyle: {
backgroundColor: darkMode ? "black" : "white"
},
headerShown: false,
animation: showNavigationAnimation ? "default" : "none"
}}>
<Stack.Screen name="SetupScreen" component={SetupScreen} options={{
title: "SetupScreen"
}}></Stack.Screen>
<Stack.Screen name="LoginScreen" options={{
title: "LoginScreen"
}}>{(props) => <LoginScreen {...props} setSetupDone={setSetupDone} />}</Stack.Screen>
<Stack.Screen name="RegisterScreen" component={RegisterScreen} options={{
title: "RegisterScreen"
}}></Stack.Screen>
<Stack.Screen name="ForgotPasswordScreen" component={ForgotPasswordScreen} options={{
title: "ForgotPasswordScreen"
}}></Stack.Screen>
<Stack.Screen name="ResendConfirmationScreen" component={ResendConfirmationScreen} options={{
title: "ResendConfirmationScreen"
}}></Stack.Screen>
<Stack.Screen name="MainScreen" initialParams={{ parent: startOnCloudScreen ? (storage.getBoolean("defaultDriveOnly:" + userId) ? storage.getString("defaultDriveUUID:" + userId) : "base") : "recents" }} component={MainScreen} options={{
title: "MainScreen"
}}></Stack.Screen>
<Stack.Screen name="SettingsScreen" component={SettingsScreen} options={{
title: "SettingsScreen"
}}></Stack.Screen>
<Stack.Screen name="TransfersScreen" component={TransfersScreen} options={{
title: "TransfersScreen"
}}></Stack.Screen>
<Stack.Screen name="CameraUploadScreen" component={CameraUploadScreen} options={{
title: "CameraUploadScreen"
}}></Stack.Screen>
<Stack.Screen name="BiometricAuthScreen" component={BiometricAuthScreen} options={{
title: "BiometricAuthScreen"
}}></Stack.Screen>
<Stack.Screen name="LanguageScreen" component={LanguageScreen} options={{
title: "LanguageScreen"
}}></Stack.Screen>
<Stack.Screen name="SettingsAdvancedScreen" component={SettingsAdvancedScreen} options={{
title: "SettingsAdvancedScreen"
}}></Stack.Screen>
<Stack.Screen name="SettingsAccountScreen" component={SettingsAccountScreen} options={{
title: "SettingsAccountScreen"
}}></Stack.Screen>
<Stack.Screen name="EventsScreen" component={EventsScreen} options={{
title: "EventsScreen"
}}></Stack.Screen>
<Stack.Screen name="EventsInfoScreen" component={EventsInfoScreen} options={{
title: "EventsInfoScreen"
}}></Stack.Screen>
<Stack.Screen name="GDPRScreen" component={GDPRScreen} options={{
title: "GDPRScreen"
}}></Stack.Screen>
<Stack.Screen name="InviteScreen" component={InviteScreen} options={{
title: "InviteScreen"
}}></Stack.Screen>
<Stack.Screen name="TwoFactorScreen" component={TwoFactorScreen} options={{
title: "TwoFactorScreen"
}}></Stack.Screen>
<Stack.Screen name="ChangeEmailPasswordScreen" component={ChangeEmailPasswordScreen} options={{
title: "ChangeEmailPasswordScreen"
}}></Stack.Screen>
<Stack.Screen name="TextEditorScreen" component={TextEditorScreen} options={{
title: "TextEditorScreen"
}}></Stack.Screen>
<Stack.Screen name="UpdateScreen" component={UpdateScreen} options={{
title: "UpdateScreen"
}}></Stack.Screen>
<Stack.Screen name="ImageViewerScreen" component={ImageViewerScreen} options={{
title: "ImageViewerScreen",
presentation: "fullScreenModal"
}}></Stack.Screen>
</Stack.Navigator>
<>
{
setupDone && isLoggedIn && ["MainScreen", "SettingsScreen", "TransfersScreen", "CameraUploadScreen", "EventsScreen", "EventsInfoScreen", "SettingsAdvancedScreen", "SettingsAccountScreen", "LanguageScreen", "GDPRScreen", "InviteScreen", "TwoFactorScreen", "ChangeEmailPasswordScreen"].includes(currentScreenName) && (
<View style={{
position: "relative",
width: "100%",
bottom: 0,
height: 50
}}>
<BottomBar navigation={navigationRef} currentScreenName={currentScreenName} />
</View>
)
}
</>
</>
) : (
<View style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
}}>
<Ionicon name="information-circle-outline" size={70} color={darkMode ? "white" : "black"} />
<Text style={{
color: darkMode ? "white" : "black",
marginTop: 5,
width: "70%",
textAlign: "center"
}}>
{i18n(lang, "nodeJSProcessDied")}
</Text>
</View>
)
}
{
nodeJSAlive && (
<>
<TransfersIndicator navigation={navigationRef} />
<TopBarActionSheet navigation={navigationRef} />
<BottomBarAddActionSheet navigation={navigationRef} />
<ItemActionSheet navigation={navigationRef} />
<FolderColorActionSheet navigation={navigationRef} />
<PublicLinkActionSheet navigation={navigationRef} />
<ShareActionSheet navigation={navigationRef} />
<FileVersionsActionSheet navigation={navigationRef} />
<ProfilePictureActionSheet navigation={navigationRef} />
<SortByActionSheet navigation={navigationRef} />
</>
)
}
</View>
</SafeAreaView>
</SafeAreaProvider>
{
nodeJSAlive && (
<>
<Disable2FATwoFactorDialog navigation={navigationRef} />
<DeleteAccountTwoFactorDialog navigation={navigationRef} />
<RedeemCodeDialog navigation={navigationRef} />
<ConfirmStopSharingDialog navigation={navigationRef} />
<ConfirmRemoveFromSharedInDialog navigation={navigationRef} />
<ConfirmPermanentDeleteDialog navigation={navigationRef} />
<RenameDialog navigation={navigationRef} />
<CreateFolderDialog navigation={navigationRef} />
<CreateTextFileDialog navigation={navigationRef} />
<BulkShareDialog navigation={navigationRef} />
<FullscreenLoadingModal navigation={navigationRef} />
</>
)
}
</Fragment>
</NavigationContainer>
<Toast
ref={(ref) => global.toast = ref}
offsetBottom={toastBottomOffset}
offsetTop={toastTopOffset}
pointerEvents="box-none"
style={{
zIndex: 99999
}}
/>
</>
)
})
Example #21
Source File: Confirm.js From actual with MIT License | 4 votes |
function Confirm({ route, navigation, getUserData, loginUser, createBudget }) {
let [code, setCode] = useState('');
let [loading, setLoading] = useState(false);
let [error, setError] = useState(null);
let { signingUp } = route.params || {};
async function onConfirm() {
let { email } = route.params || {};
setLoading(true);
let { confirmed, error, userId, key, validSubscription } = await send(
'subscribe-confirm',
{ email, code }
);
if (error) {
setLoading(false);
setError(getErrorMessage(error));
} else if (!confirmed) {
setLoading(false);
setError(getErrorMessage('not-confirmed'));
} else if (!validSubscription) {
if (Platform.OS === 'ios') {
// Eagerly load in the offerings (otherwise the subscribe button
// shows a loading state which is weird)
await setupPurchases({ id: userId, email: email });
await getOfferings();
setLoading(false);
navigation.navigate('Subscribe', { email, userId, key });
} else {
// This is a "half-created" account, right now on Android we
// don't fix it here, we just tell the user. This is super
// rare - only happens if a user on iOS creates an account but
// doesn't subscribe, and then tries to log in on Android with
// that account
alert(
'An error occurred loading your account. Please contact [email protected] for support'
);
setLoading(false);
}
} else {
setLoading(false);
// This will load the user in the backend and rerender the app
// in the logged in state
loginUser(userId, key);
if (global.SentryClient) {
global.SentryClient.setUser({
id: userId,
ip_address: '{{auto}}'
});
}
}
}
let textStyle = [
styles.text,
{ fontSize: 17, lineHeight: 25, color: 'white' }
];
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAvoidingView>
<TransitionView navigation={navigation}>
<Header
navigation={navigation}
buttons={['back', 'demo']}
loadDemoBudget={() => createBudget({ demoMode: true })}
/>
<Stack justify="center" style={{ flex: 1, padding: 20 }} spacing={5}>
<View>
{signingUp ? (
<Text style={textStyle}>
Enter the code you got in your email to activate your account:
</Text>
) : (
<Text style={textStyle}>
Enter the code you got in your email:
</Text>
)}
</View>
<SingleInput
title="Code"
value={code}
loading={loading}
error={error}
inputProps={{ keyboardType: 'numeric' }}
onChange={setCode}
onSubmit={onConfirm}
/>
</Stack>
</TransitionView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Example #22
Source File: Intro.js From actual with MIT License | 4 votes |
render() {
let { navigation, createBudget } = this.props;
let { selectedFeature } = this.state;
//let textStyle = [styles.text, { color: 'white' }];
return (
<SafeAreaView style={{ flex: 1 }}>
<TransitionView navigation={navigation}>
<View
style={{ justifyContent: 'center', alignItems: 'center', flex: 1 }}
>
<ScalableImage
source={Icon}
width={35}
style={{ marginBottom: 20, marginTop: 30 }}
/>
<View style={{ height: 240 }}>
<ScrollView
ref={el => (this.scrollView = el)}
pagingEnabled={true}
horizontal={true}
showsHorizontalScrollIndicator={false}
onScrollBeginDrag={this.onScrollBegin}
onScrollEndDrag={this.onScrollEnd}
onMomentumScrollEnd={this.onMomentumScrollEnd}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
flex: 0
}}
>
<Feature
title="Welcome to Actual"
subtitle={
<Text>
Actual is a privacy-focused app that lets you track your
finances without all the fuss. Create your own budgeting
workflows quickly and discover your spending habits.{' '}
<ExternalLink href="https://actualbudget.com/">
Learn more
</ExternalLink>
</Text>
}
/>
<Feature
title="Powerful budgeting made simple"
subtitle="Based on tried and true methods, our budgeting system is based off of your real income instead of made up numbers."
/>
<Feature
title="The fastest way to manage transactions"
subtitle="Breeze through your transactions and update them easily with a streamlined, minimal interface."
/>
<Feature
title="A privacy-focused approach"
subtitle="All of your data exists locally and is always available. We only upload your data to our servers when syncing across devices, and we encrypt it so even we can't read it."
/>
</View>
</ScrollView>
<View style={{ alignItems: 'center' }}>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
width: 45
}}
>
<SlideshowIndicator selected={selectedFeature === 0} />
<SlideshowIndicator selected={selectedFeature === 1} />
<SlideshowIndicator selected={selectedFeature === 2} />
<SlideshowIndicator selected={selectedFeature === 3} />
</View>
</View>
</View>
</View>
<View
style={{
marginHorizontal: 50,
marginBottom: 15,
justifyContent: 'flex-end'
}}
>
<Button
primary
style={{ marginBottom: 10, backgroundColor: 'white' }}
contentStyle={{ borderWidth: 0 }}
textStyle={{ fontSize: 17, color: colors.n1 }}
onPress={() => {
navigation.navigate('SubscribeEmail');
}}
>
Get started
</Button>
<Button
style={{
marginBottom: 10,
backgroundColor: 'rgba(180, 180, 180, .15)'
}}
contentStyle={{ borderWidth: 0 }}
textStyle={{ fontSize: 17, color: 'white' }}
onPress={() => {
navigation.navigate('Login');
}}
>
Log in
</Button>
</View>
<Button
bare
textStyle={{ fontWeight: 'bold', fontSize: 15, color: 'white' }}
style={{ padding: 10, alignSelf: 'center' }}
onPress={() => createBudget({ demoMode: true })}
>
Try demo
</Button>
</TransitionView>
</SafeAreaView>
);
}
Example #23
Source File: SubscribeEmail.js From actual with MIT License | 4 votes |
export function SubscribeEmail({ navigation, createBudget }) {
let [email, setEmail] = useState('');
let [error, setError] = useState(null);
let [loading, setLoading] = useState(false);
async function eagerlyLoadOfferings(userId, email) {
await iap.setupPurchases({ id: userId, email });
iap.getOfferings();
}
async function onSignup() {
setLoading(true);
setError(null);
let { error, userId, key } = await send('subscribe-subscribe', {
email,
useStripe: Platform.OS !== 'ios'
});
if (error) {
setLoading(false);
setError(getSubscribeError(error));
} else {
if (Platform.OS === 'ios') {
// Don't block on this, but start loading the available offerings
// now so when they see the subscribe screen later they don't see
// a loading screen
eagerlyLoadOfferings(userId, email);
}
let { error } = await send('subscribe-send-email-code', { email });
if (error) {
setError('Something went wrong while activating your account');
return;
}
setLoading(false);
navigation.navigate('Confirm', { email, signingUp: true });
}
}
let textStyle = [
styles.text,
{ fontSize: 17, lineHeight: 25, color: 'white' }
];
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAvoidingView>
<TransitionView navigation={navigation}>
{/* <StatusBar barStyle="light-content" /> */}
<Header
navigation={navigation}
loadDemoBudget={() => createBudget({ demoMode: true })}
/>
<Stack justify="center" style={{ flex: 1, padding: 20 }} spacing={5}>
<View>
<Text style={[textStyle, { maxWidth: 500 }]}>
<Text style={{ fontWeight: '700' }}>Create an account.</Text>{' '}
Sign up to sync your data across all devices. By default all
your data is local. In the future we will also provide bank
syncing.
</Text>
</View>
<SingleInput
title="Email"
value={email}
loading={loading}
error={error}
inputProps={{
keyboardType: 'email-address',
placeholder: '[email protected]'
}}
onChange={setEmail}
onSubmit={onSignup}
/>
</Stack>
</TransitionView>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Example #24
Source File: Modal.js From actual with MIT License | 4 votes |
render() {
let {
title,
style,
children,
animate,
backgroundColor,
rightButton,
allowScrolling = true,
edges = ['top', 'bottom']
} = this.props;
animate = false;
return (
<SafeAreaView
edges={edges}
style={{ flex: 1, backgroundColor: 'transparent' }}
>
<KeyboardAvoidingView>
<FocusAwareStatusBar barStyle="light-content" />
<Animated.View
style={[
{ flex: 1 },
animate && {
opacity: this.opening,
transform: [
{
translateY: this.opening.interpolate({
inputRange: [0, 1],
outputRange: [10, 0]
})
}
]
}
]}
>
<View
style={{
flex: 1,
shadowColor: colors.n3,
shadowOffset: { width: 0, height: 0 },
shadowRadius: 4,
shadowOpacity: 1,
elevation: 2
}}
>
<View
style={[
{
margin: 7,
borderRadius: 4,
overflow: 'hidden',
backgroundColor: backgroundColor || colors.n11,
flex: 1
},
style
]}
onLayout={this.onLayout}
ref={el => (this.modal = el)}
>
<View
style={{
alignSelf: 'stretch',
alignItems: 'center',
paddingVertical: 15,
paddingLeft: 15,
backgroundColor: backgroundColor || colors.n11,
borderColor: colors.n10,
borderBottomWidth: 1
}}
>
<Text
style={{
color: colors.n1,
fontSize: 20,
fontWeight: '700'
}}
numberOfLines={1}
>
{title}
</Text>
{rightButton && (
<View
style={{
position: 'absolute',
right: 0,
top: 0,
bottom: 0,
justifyContent: 'center'
}}
>
{rightButton}
</View>
)}
</View>
{allowScrolling ? (
<ScrollView style={{ flex: 1 }}>{children}</ScrollView>
) : (
children
)}
</View>
</View>
</Animated.View>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Example #25
Source File: CharacterScreen.js From hero with MIT License | 4 votes |
CharacterScreen = ({ route, navigation }) => {
const {
hero,
image,
publisher,
comicPicture,
summary,
firstIssue,
firstIssueURL,
} = route.params;
let publisherLogo = null;
let logoShape = null;
// let firstIssueID = null;
if (publisher === "Marvel Comics" || publisher === "Marvel") {
publisherLogo = require(`../assets/images/Marvel-Logo.jpg`);
logoShape = styles.publisherLogoRectangle;
} else if (publisher === "DC Comics") {
publisherLogo = require(`../assets/images/DC-Logo.png`);
logoShape = styles.publisherLogoSquare;
}
const activeLightboxProps = {
resizeMode: "contain",
marginHorizontal: 20,
flex: 1,
width: null,
};
// if (comicPicture) {
// comicPictureURL = require(comicPicture);
// }
function searchComic(firstComic) {
fetch(
`https://comicvine.gamespot.com/api/characters/?api_key=${apiComic.key}&sort=deck:desc&filter=name:${hero.name}&format=json`
)
.then((res) => {
if (res.status === 404) {
throw new Error("I didn't find this hero. Please try again!");
} else {
return res.json();
}
})
.then((result) => {
console.log("====================================");
console.log("NEW SEARCH");
console.log("====================================");
console.log(result.results);
});
}
function searchFirstComic() {
fetch(
`https://comicvine.gamespot.com/api/issue/4000-${firstIssue.id}/?api_key=${apiComic.key}&format=json`
)
.then((res) => {
if (res.status === 404) {
throw new Error("I didn't find this hero. Please try again!");
} else {
return res.json();
}
})
.then((result) => {
console.log("FIRST ISSUE DETAILS");
console.log(result.results.image.original_url);
// comicPicture = require(result.results[0].image.original_url);
// summary = result.results[0].deck;
});
}
// useEffect(() => {
// // console.log(publisherVine);
// // searchComic(hero.biography["first-appearance"]);
// // searchFirstComic();
// }, []);
return (
<View style={styles.appContainer}>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="dark-content"
/>
<SafeAreaView
style={{ flex: 1, backgroundColor: "transparent" }}
forceInset={{ top: "always" }}
>
<Header title={""} backbutton={true} navigation={navigation} />
<Image source={image} style={styles.heroImage} />
<LinearGradient
colors={["#ffffff00", COLORS.beige]}
style={styles.bottomFade}
locations={[0.3, 1]}
/>
<View style={styles.heroInfoContainer}>
<View style={styles.heroTitleContainer}>
<Text style={styles.heroTitle}>{hero.name}</Text>
<View style={styles.heroHeader}>
<Text style={{ ...styles.h4, marginLeft: 3, fontSize: 16 }}>
{hero.biography["full-name"]}
</Text>
<Image source={publisherLogo} style={logoShape} />
</View>
</View>
<Divider
orientation="horizontal"
width={3}
style={{ ...styles.divider, marginBottom: 0 }}
color={COLORS.navy}
/>
<ScrollView
style={{ height: 340 }}
contentContainerStyle={{
width: "100%",
paddingBottom: 40,
marginTop: 10,
}}
showsVerticalScrollIndicator={false}
>
<Text
style={{
...styles.p,
fontSize: 12,
marginBottom: 20,
lineHeight: 18,
}}
>
{summary}
</Text>
{/********** STATS DETAILS ***************/}
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-around",
alignItems: "center",
// backgroundColor: COLORS.grey,
borderRadius: 20,
// padding: 10,
marginBottom: 10,
}}
>
<View
style={{
alignItems: "center",
justifyContent: "center",
padding: 5,
}}
>
<AnimatedCircularProgress
size={60}
width={10}
duration={2000}
backgroundWidth={8}
rotation={-124}
arcSweepAngle={250}
fill={Number(hero.powerstats.intelligence)}
tintColor={COLORS.red}
tintColorSecondary={COLORS.green}
// onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor={COLORS.navy}
padding={0}
lineCap={"round"}
// renderCap={({ center }) => (
// <Circle cx={center.x} cy={center.y} r="10" fill="blue" />
// )}
>
{(fill) => (
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
left: 1,
}}
>
{Math.floor(fill)}
</Text>
)}
</AnimatedCircularProgress>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
fontSize: 10,
marginTop: -10,
}}
>
Intelligence
</Text>
</View>
<View
style={{
alignItems: "center",
justifyContent: "center",
padding: 5,
}}
>
<AnimatedCircularProgress
size={60}
width={10}
duration={2000}
backgroundWidth={8}
rotation={-124}
arcSweepAngle={250}
fill={Number(hero.powerstats.strength)}
tintColor={COLORS.red}
tintColorSecondary={COLORS.green}
// onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor={COLORS.navy}
padding={0}
lineCap={"round"}
// renderCap={({ center }) => (
// <Circle cx={center.x} cy={center.y} r="10" fill="blue" />
// )}
>
{(fill) => (
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
left: 1,
}}
>
{Math.floor(fill)}
</Text>
)}
</AnimatedCircularProgress>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
fontSize: 10,
marginTop: -10,
}}
>
Strength
</Text>
</View>
<View
style={{
alignItems: "center",
justifyContent: "center",
padding: 5,
}}
>
<AnimatedCircularProgress
size={60}
width={10}
duration={2000}
backgroundWidth={8}
rotation={-124}
arcSweepAngle={250}
fill={Number(hero.powerstats.speed)}
tintColor={COLORS.red}
tintColorSecondary={COLORS.green}
// onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor={COLORS.navy}
padding={0}
lineCap={"round"}
// renderCap={({ center }) => (
// <Circle cx={center.x} cy={center.y} r="10" fill="blue" />
// )}
>
{(fill) => (
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
left: 1,
}}
>
{Math.floor(fill)}
</Text>
)}
</AnimatedCircularProgress>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
fontSize: 10,
marginTop: -10,
}}
>
Speed
</Text>
</View>
<View
style={{
alignItems: "center",
justifyContent: "center",
padding: 5,
}}
>
<AnimatedCircularProgress
size={60}
width={10}
duration={2000}
backgroundWidth={8}
rotation={-124}
arcSweepAngle={250}
fill={Number(hero.powerstats.durability)}
tintColor={COLORS.red}
tintColorSecondary={COLORS.green}
// onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor={COLORS.navy}
padding={0}
lineCap={"round"}
// renderCap={({ center }) => (
// <Circle cx={center.x} cy={center.y} r="10" fill="blue" />
// )}
>
{(fill) => (
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
left: 1,
}}
>
{Math.floor(fill)}
</Text>
)}
</AnimatedCircularProgress>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
fontSize: 10,
marginTop: -10,
}}
>
Durability
</Text>
</View>
<View
style={{
alignItems: "center",
justifyContent: "center",
padding: 5,
}}
>
<AnimatedCircularProgress
size={60}
width={10}
duration={2000}
backgroundWidth={8}
rotation={-124}
arcSweepAngle={250}
fill={Number(hero.powerstats.power)}
tintColor={COLORS.red}
tintColorSecondary={COLORS.green}
// onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor={COLORS.navy}
padding={0}
lineCap={"round"}
// renderCap={({ center }) => (
// <Circle cx={center.x} cy={center.y} r="10" fill="blue" />
// )}
>
{(fill) => (
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
left: 1,
}}
>
{Math.floor(fill)}
</Text>
)}
</AnimatedCircularProgress>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
fontSize: 10,
marginTop: -10,
}}
>
Power
</Text>
</View>
<View
style={{
alignItems: "center",
justifyContent: "center",
padding: 5,
}}
>
<AnimatedCircularProgress
size={60}
width={10}
duration={2000}
backgroundWidth={8}
rotation={-124}
arcSweepAngle={250}
fill={Number(hero.powerstats.combat)}
tintColor={COLORS.red}
tintColorSecondary={COLORS.green}
// onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor={COLORS.navy}
padding={0}
lineCap={"round"}
// renderCap={({ center }) => (
// <Circle cx={center.x} cy={center.y} r="10" fill="blue" />
// )}
>
{(fill) => (
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
left: 1,
}}
>
{Math.floor(fill)}
</Text>
)}
</AnimatedCircularProgress>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
fontSize: 10,
marginTop: -10,
}}
>
Combat
</Text>
</View>
</View>
{/********** BIOGRAPHY DETAILS ***************/}
<View style={styles.heroDetailsContainer}>
<Text style={{ ...styles.h2 }}>Biography</Text>
<Divider
orientation="horizontal"
width={2}
inset={true}
style={styles.divider}
color={COLORS.navy}
/>
{Object.entries(hero.biography).map(([key, value, index]) => {
// console.log(`${key}: ${value}`);
let str = value.toString();
if (
key != "full-name" &&
key != "place-of-birth" &&
key != "first-appearance" &&
key != "alter-egos" &&
"No alter egos found."
) {
str = str.replace(/,|;\s*(?![^()]*\))/g, "\n\u2022 ");
}
return (
<View
key={index}
style={{
flexDirection:
key == "aliases" || key == "alter-egos"
? "column"
: "row",
justifyContent: "space-between",
alignItems: "flex-start",
flexWrap: "wrap",
marginBottom: 5,
}}
>
<Text style={styles.h4}>{key}:</Text>
{str.split(`/,[s]*/g, ", "`).map((value) => (
<Text
style={{ ...styles.p, textTransform: "capitalize" }}
>
{key == "alter-egos" || key == "aliases"
? "\u2022 " + value
: value && value == "-"
? "unknown"
: value}
</Text>
))}
</View>
);
})}
</View>
{/********** COMIC PICTURE ***************/}
<View style={styles.comicPictureContainer}>
{firstIssueURL ? (
<Lightbox
// renderHeader={() => {
// return (
// <View
// style={{
// justifyContent: "center",
// alignItems: "flex-start",
// paddingHorizontal: 15,
// top: 70,
// }}
// >
// <Text
// style={{ ...styles.heroTitle, color: COLORS.beige }}
// >
// First Issue
// </Text>
// </View>
// );
// }}
activeProps={activeLightboxProps}
>
<Image
source={{
uri: firstIssueURL,
}}
style={styles.comicPicture}
/>
</Lightbox>
) : (
<Text style={styles.h4}>NO PICTURE</Text>
)}
</View>
{/********** APPEARANCE DETAILS ***************/}
<View style={styles.heroDetailsContainer}>
<Text style={{ ...styles.h2 }}>Appearence</Text>
<Divider
orientation="horizontal"
width={2}
inset={true}
style={styles.divider}
color={COLORS.navy}
/>
{Object.entries(hero.appearance).map(([key, value, index]) => {
// console.log(`${key}: ${value}`);
const str = value.toString();
return (
<View
key={index}
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "stretch",
flexWrap: "wrap",
}}
>
<Text style={styles.h4}>{key}:</Text>
<Text style={{ ...styles.p, marginTop: -2 }}>
{str
.split(`/,[s]*/g, ", "`)
.map((value) =>
str.includes(",") ? (
<Text>{value.replace(/,(?=[^\s])/g, ", ")}</Text>
) : (
<Text>{value}</Text>
)
)}
</Text>
</View>
);
})}
</View>
{/********** WORK DETAILS ***************/}
<View style={styles.heroDetailsContainer}>
<Text style={{ ...styles.h2 }}>Work</Text>
<Divider
orientation="horizontal"
width={2}
inset={true}
style={styles.divider}
color={COLORS.navy}
/>
{Object.entries(hero.work).map(([key, value, index]) => {
// console.log(`${key}: ${value}`);
let str = value;
if (key != "base") {
str = str.replace(/,|;\s*(?![^()]*\))/g, "\n\u2022 ");
}
return (
<View
key={index}
style={{
flexDirection: key == "base" ? "row" : "column",
justifyContent: "space-between",
alignItems: key == "base" ? "stretch" : "flex-start",
flexWrap: "wrap",
marginBottom: 5,
}}
>
<Text style={{ ...styles.h4, marginBottom: 4 }}>
{key}:
</Text>
{str.split(`/,[s]*/g, ", "`).map((value) => (
<Text
style={{
...styles.p,
textTransform: "capitalize",
lineHeight: key == "occupation" ? 20 : 0,
marginTop: -2,
}}
>
{key == "base"
? value
: "\u2022 " + value && value != "-"
? "\u2022 " + value
: "unknown"}
</Text>
))}
</View>
);
})}
</View>
{/********** CONNECTIONS DETAILS ***************/}
<View style={styles.heroDetailsContainer}>
<Text style={{ ...styles.h2 }}>Connections</Text>
<Divider
orientation="horizontal"
width={2}
inset={true}
style={styles.divider}
color={COLORS.navy}
/>
{Object.entries(hero.connections).map(([key, value, index]) => {
// console.log(`${key}: ${value}`);
let str = value.toString();
if (key == "group-affiliation") {
str = value.replace(/,|;\s*(?![^()]*\))/g, "\n\u2022 ");
} else {
str = value.replace(/,|;\s*(?![^()]*\))/g, "\n\u2022 ");
}
// const firstLetter = str.charAt(0).toUpperCase() + str.slice(1);
return (
<View
key={index}
style={{
flexDirection: "column",
justifyContent: "space-between",
alignItems: "flex-start",
flexWrap: "wrap",
marginBottom: 5,
}}
>
<Text style={styles.h4}>{key}:</Text>
{str.split(`/,[s]*/g, ", "`).map((value) => (
<Text
style={{
...styles.p,
textTransform: "capitalize",
lineHeight: 24,
}}
>
{value != "-" ? "\u2022 " + value : "unknown"}
</Text>
))}
</View>
);
})}
</View>
</ScrollView>
<LinearGradient
colors={["#ffffff00", COLORS.beige]}
style={styles.bottomFadeInfo}
locations={[0.8, 1]}
pointerEvents={"none"}
/>
</View>
</SafeAreaView>
</View>
);
}
Example #26
Source File: HomeScreen.js From hero with MIT License | 4 votes |
HomeScreen = ({ navigation }) => {
const [XMen, popularHeroes, villains] = useContext(HeroesContext);
const [loading, setLoading] = useState(false);
const insets = useSafeAreaInsets();
const scrollY = new Animated.Value(0);
const translateY = scrollY.interpolate({
inputRange: [40, 100 + insets.top],
outputRange: [40, insets.top - 100],
extrapolate: "clamp",
});
const search = async (item) => {
try {
setLoading(true);
const searchResponse = await fetch(
`https://superheroapi.com/api/${api.key}/${item.id}/`
);
const characterResponse = await fetch(
`https://comicvine.gamespot.com/api/characters/?api_key=${apiComicVine.key}&filter=name:${item.title},publisher${item.publisher}&field_list=deck,publisher,first_appeared_in_issue&format=json`
);
const hero = await searchResponse.json();
const characterInfo = await characterResponse.json();
summary = characterInfo.results[0].deck;
firstIssue = characterInfo.results[0].first_appeared_in_issue;
publisher = characterInfo.results[0].publisher.name;
const firstComicResponse = await fetch(
`https://comicvine.gamespot.com/api/issue/4000-${firstIssue.id}/?api_key=${apiComicVine.key}&format=json`
);
const firstComicInfo = await firstComicResponse.json();
firstIssueURL = firstComicInfo.results.image.original_url;
navigation.navigate("Character", {
hero: hero,
image: item.image,
// publisher: item.publisher,
comicPicture: comicPicture,
summary: summary,
firstIssue: firstIssue,
firstIssueURL: firstIssueURL,
publisher: publisher,
});
// setLoading(false);
} catch (error) {
console.error(error);
setLoading(false);
}
};
const _renderItem = ({ item, index }) => {
return (
<Pressable
key={index}
style={({ pressed }) => [
styles.heroCard,
{ opacity: pressed ? 0.8 : 1.0 },
]}
style={styles.heroCard}
>
<TouchableScale
delayPressIn={50}
activeScale={0.9}
tension={160}
friction={2}
onPress={() => {
search(item);
// console.log(item.id);
}}
>
<MaskedView
maskElement={
<SquircleView
style={StyleSheet.absoluteFill}
squircleParams={{
cornerRadius: 50,
cornerSmoothing: 1,
fillColor: "pink",
}}
/>
}
>
<Image
source={item.image}
resizeMode="cover"
PlaceholderContent={<ActivityIndicator />}
style={{
width: "100%",
height: "100%",
}}
/>
<View
style={{
flex: 1,
position: "absolute",
bottom: -5,
padding: 30,
width: "100%",
justifyContent: "center",
borderRadius: 20,
}}
>
<Text
style={{
...styles.h4,
fontSize: 20,
color: COLORS.beige,
textShadowColor: "rgba(0, 0, 0, 1)",
textShadowOffset: { width: -1, height: 1 },
textShadowRadius: 5,
}}
>
{item.title}
</Text>
</View>
</MaskedView>
</TouchableScale>
</Pressable>
// </Animated.View>
);
};
useEffect(() => {
setLoading(false);
}, []);
useEffect(() => {
const unsubscribe = navigation.addListener("blur", () => {
// Screen was blurred
// Do something
setLoading(false);
});
return unsubscribe;
}, [navigation]);
return (
<>
<View style={styles.appContainer}>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="dark-content"
/>
<SafeAreaView
style={{
flex: 1,
width: Dimensions.get("window").width,
}}
forceInset={{ top: "always" }}
>
<Animated.View
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
zIndex: 10,
height: 100,
transform: [{ translateY: translateY }],
}}
>
<View style={styles.header}>
<View style={{ justifyContent: "flex-end" }}>
<Text style={styles.appTitle}>hero</Text>
<Text
style={{ ...styles.p, fontSize: 7, marginTop: -2, left: -2 }}
>
the Superhero Encyclopedia
</Text>
</View>
</View>
</Animated.View>
<ScrollView
contentContainerStyle={{
paddingBottom: 80,
width: Dimensions.get("window").width,
}}
onScroll={(e) => {
scrollY.setValue(e.nativeEvent.contentOffset.y);
}}
scrollEventThrottle={6}
>
<View style={styles.popularContainer}>
<View
style={{
flexDirection: "row",
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
}}
>
<Text
style={{
...styles.h4,
marginBottom: 10,
paddingLeft: 15,
}}
>
Popular
</Text>
<Icon
name="trending-up"
type="feather"
color={COLORS.navy}
size={30}
iconStyle={{ bottom: 2, paddingLeft: 5 }}
/>
</View>
<Carousel
data={popularHeroes}
sliderWidth={380}
itemWidth={260}
renderItem={_renderItem}
loop={true}
inactiveSlideShift={0}
inactiveSlideOpacity={Platform.OS === "ios" ? 0.5 : 1}
/>
</View>
<View style={styles.heroContainer}>
<View
style={{
flexDirection: "row",
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
}}
>
<Text
style={{
...styles.h4,
marginBottom: 10,
paddingLeft: 15,
}}
>
Villians
</Text>
<Icon
name="emoticon-devil"
type="material-community"
color={COLORS.navy}
size={30}
iconStyle={{ bottom: 2, paddingLeft: 5, opacity: 0.9 }}
/>
</View>
<Carousel
data={villains}
sliderWidth={380}
itemWidth={260}
renderItem={_renderItem}
loop={true}
// inactiveSlideShift={-24}
inactiveSlideOpacity={Platform.OS === "ios" ? 0.5 : 1}
/>
</View>
<View style={styles.heroContainer}>
<Text
style={{
...styles.h4,
marginBottom: 10,
paddingHorizontal: 15,
}}
>
X-Men
</Text>
<Carousel
data={XMen}
sliderWidth={380}
itemWidth={260}
renderItem={_renderItem}
loop={true}
// inactiveSlideShift={-24}
inactiveSlideOpacity={Platform.OS === "ios" ? 0.5 : 1}
/>
</View>
</ScrollView>
{/* )} */}
<LinearGradient
colors={[COLORS.beige, "#ffffff00"]}
style={styles.scrollGradient}
locations={[0, 1]}
pointerEvents={"none"}
/>
</SafeAreaView>
</View>
{loading === true ? (
<Modal statusBarTranslucent={true}>
<View
style={{
backgroundColor: COLORS.beige,
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
}}
>
<Progress.CircleSnail
color={[COLORS.navy, COLORS.orange, COLORS.blue]}
size={80}
thickness={10}
style={styles.loading}
strokeCap={"round"}
/>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
marginTop: -15,
left: 3,
}}
>
loading...
</Text>
</View>
</Modal>
) : null}
</>
);
}
Example #27
Source File: SearchScreen.js From hero with MIT License | 4 votes |
SearchScreen = ({ navigation }) => {
const [query, setQuery] = useState("");
const [XMen, popularHeroes, villains] = useContext(HeroesContext);
const [heroNames, setHeroNames] = useState([]);
const [filteredHeroNames, setFilteredHeroNames] = useState([]);
const [loadingSearch, setLoadingSearch] = useState(false);
const [loading, setLoading] = useState(false);
const fetchHeroes = async () => {
try {
setLoadingSearch(true);
const response = await fetch(
"https://cdn.jsdelivr.net/gh/akabab/[email protected]/api/all.json"
);
const herojson = await response.json();
setHeroNames(herojson);
// heroNames.forEach((hero) => {
// console.log(hero.name);
// });
setLoadingSearch(false);
} catch (error) {
console.error(error);
setLoadingSearch(false);
}
};
const renderFooter = () => {
if (!loadingSearch) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE",
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
const renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: COLORS.navy,
borderRadius: 50,
marginLeft: "7%",
}}
/>
);
};
const handleSearch = (text) => {
const formattedQuery = text.toLowerCase();
const data = heroNames.filter((item) => {
// return contains(name, formattedQuery);
return item.name.toLowerCase().includes(formattedQuery);
});
setFilteredHeroNames(data);
setQuery(text);
// this.setState({ data, query: text })
};
const search = async (item) => {
try {
setLoading(true);
const searchResponse = await fetch(
`https://superheroapi.com/api/${api.key}/${item.id}/`,
{
method: "GET",
}
);
// console.log(item.biography.fullName);
// const realName = item.biography.fullName.text();
const characterResponse = await fetch(
`https://comicvine.gamespot.com/api/characters/?api_key=${apiComicVine.key}&filter=name:${item.name},publisher:${item.biography.publisher},real_name:${item.biography.fullName},origin:${item.appearance.race}&field_list=deck,publisher,first_appeared_in_issue&format=json`,
{
method: "GET",
}
);
const hero = await searchResponse.json();
const characterInfo = await characterResponse.json();
// console.log(characterInfo);
{
characterInfo.results[0].deck == undefined
? (summary = "no summary")
: (summary = characterInfo.results[0].deck);
}
// summary = characterInfo.results[0].deck;
firstIssue = characterInfo.results[0].first_appeared_in_issue;
publisher = characterInfo.results[0].publisher.name;
const firstComicResponse = await fetch(
`https://comicvine.gamespot.com/api/issue/4000-${firstIssue.id}/?api_key=${apiComicVine.key}&format=json`,
{
method: "GET",
}
);
const firstComicInfo = await firstComicResponse.json();
// console.log(firstComicInfo);
firstIssueURL = firstComicInfo.results.image.original_url;
navigation.navigate("Character", {
hero: hero,
image: { uri: item.images.lg },
// publisher: item.publisher,
comicPicture: comicPicture,
summary: summary,
firstIssue: firstIssue,
firstIssueURL: firstIssueURL,
publisher: publisher,
});
setLoading(false);
} catch (error) {
console.error(error);
setLoading(false);
Alert.alert("Sorry!", "Hero Not Found");
}
};
useEffect(() => {
fetchHeroes();
setLoading(false);
}, []);
return (
<View style={styles.appContainer}>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="dark-content"
/>
<SafeAreaView>
<View style={styles.header}>
<Text style={styles.appTitle}>search</Text>
</View>
{/* <KeyboardAvoidingView
style={{
paddingHorizontal: 15,
marginBottom: -20,
zIndex: 5,
backgroundColor: "transparent",
}}
> */}
<SearchBar
placeholder="Search..."
onChangeText={handleSearch}
value={query}
containerStyle={styles.inputContainer}
inputContainerStyle={styles.input}
inputStyle={styles.inputText}
searchIcon={{ size: 25 }}
round={true}
/>
{/* </KeyboardAvoidingView> */}
<BigList
itemHeight={ITEM_HEIGHT}
headerHeight={0}
footerHeight={0}
style={{
position: "absolute",
width: "100%",
marginTop: 20,
paddingTop: 10,
height: Platform.OS === "ios" ? 580 : 590,
}}
data={
filteredHeroNames && filteredHeroNames.length > 0
? filteredHeroNames
: heroNames
}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => search(item)}>
<View
style={{
flexDirection: "row",
padding: 10,
// paddingTop: 30,
paddingHorizontal: 15,
alignItems: "center",
borderBottomColor: COLORS.navy,
borderBottomWidth: 2,
// backgroundColor: COLORS.navy,
marginBottom: 15,
// marginHorizontal: 10,
borderRadius: 15,
}}
>
<Avatar
rounded
source={{ uri: item.images.md }}
size="medium"
containerStyle={{
marginRight: 13,
borderColor: COLORS.navy,
borderWidth: 2,
top: 0,
}}
/>
{/* <Image
source={{ uri: item.images.sm }}
style={{ width: "100%", height: 90, borderRadius: 10 }}
/> */}
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
color: COLORS.navy,
}}
>
{item.name}
</Text>
</View>
</TouchableOpacity>
)}
keyExtractor={(item) => item.id.toString()}
// ItemSeparatorComponent={renderSeparator}
renderFooter={renderFooter}
// ListHeaderComponent={}
/>
<LinearGradient
colors={[COLORS.beige, "#ffffff00"]}
style={styles.scrollGradient}
locations={[0, 1]}
pointerEvents={"none"}
/>
{loading === true ? (
<Modal statusBarTranslucent={true}>
<View
style={{
backgroundColor: COLORS.beige,
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
}}
>
<Progress.CircleSnail
color={[COLORS.navy, COLORS.orange, COLORS.blue]}
size={80}
thickness={10}
style={styles.loading}
strokeCap={"round"}
/>
<Text
style={{
...styles.p,
fontFamily: "Flame-Regular",
marginTop: -15,
left: 3,
}}
>
loading...
</Text>
</View>
</Modal>
) : null}
</SafeAreaView>
</View>
);
}
Example #28
Source File: Post.js From reddit-clone with MIT License | 4 votes |
Post = ({
index,
postId,
userId,
score,
type,
title,
author,
category,
text,
comments,
created,
url,
votes,
views,
setIsLoaading,
setData,
postType,
deleteButton,
deletePost
}) => {
const { colors } = useTheme()
const navigation = useNavigation()
const { authState } = React.useContext(AuthContext)
const route = useRoute()
const isUpVoted = () => {
return votes.find(v => v.user === userId)?.vote === 1
}
const isDownVoted = () => {
return votes.find(v => v.user === userId)?.vote === -1
}
const upVote = async () => {
setIsLoaading(true)
const { data } = await axios.get(`post/${postId}/upvote`)
if (postType !== 'item') {
setData(prevData => {
prevData[index] = data
return prevData
})
} else {
setData(data)
}
setIsLoaading(false)
}
const downVote = async () => {
setIsLoaading(true)
const { data } = await axios.get(`post/${postId}/downvote`)
if (postType !== 'item') {
setData(prevData => {
prevData[index] = data
return prevData
})
} else {
setData(data)
}
setIsLoaading(false)
}
const unVote = async () => {
setIsLoaading(true)
const { data } = await axios.get(`post/${postId}/unvote`)
if (postType !== 'item') {
setData(prevData => {
prevData[index] = data
return prevData
})
} else {
setData(data)
}
setIsLoaading(false)
}
return (
<View
as={SafeAreaView}
style={[
styles.container,
{ backgroundColor: colors.bgColor, borderColor: colors.postBorder }
]}
>
<View style={styles.headerContainer}>
<View style={styles.headerLeft}>
<Text style={[styles.regularFont, { color: colors.text }]}>{category} </Text>
<Text
style={[styles.italicFont, { color: colors.blue }]}
onPress={() => navigation.navigate('User', { username: author.username })}
>
@{author?.username} ·{' '}
</Text>
<Text style={[styles.dateText, { color: colors.text }]}>{moment(created).fromNow()}</Text>
</View>
<View style={styles.headerRight}>
{deleteButton && author?.id === authState.userInfo.id && (
<TouchableOpacity style={styles.trash} activeOpacity={0.5} onPress={deletePost}>
<Trash color={colors.red} width={20} height={20} />
</TouchableOpacity>
)}
</View>
</View>
<Text
style={[styles.title, { color: colors.text }]}
onPress={() => navigation.navigate('PostDetail', { postId, category, comments })}
>
{title}
</Text>
<Text
numberOfLines={route.name === 'PostDetail' ? 10000 : 10}
style={[
styles.regularFont,
{ color: colors.text },
type === 'link' && route.name === 'PostDetail' && styles.link
]}
onPress={() =>
route.name === 'PostDetail' && type === 'link'
? Linking.openURL(url)
: navigation.navigate('PostDetail', { postId, category, comments })
}
>
{type === 'link' ? url : text}
</Text>
<View style={styles.bottomContainer}>
<View style={styles.centerAlign}>
<TouchableOpacity onPress={() => (isUpVoted() ? unVote() : upVote())}>
<ArrowUp
width={22}
height={22}
strokeWidth={4}
color={isUpVoted() ? colors.green : colors.icon}
/>
</TouchableOpacity>
<Text style={[styles.score, { color: colors.text }]}>{score}</Text>
<TouchableOpacity onPress={() => (isDownVoted() ? unVote() : downVote())}>
<ArrowDown
width={22}
height={22}
strokeWidth={4}
color={isDownVoted() ? colors.red : colors.icon}
/>
</TouchableOpacity>
</View>
<TouchableOpacity
style={styles.centerAlign}
activeOpacity={0.7}
onPress={() => navigation.navigate('PostDetail', { postId, category, comments })}
>
<MessageSquare
color={colors.icon}
style={styles.commentIcon}
width={20}
height={20}
strokeWidth={3}
/>
<Text style={[styles.commentText, { color: colors.text }]}>{comments?.length}</Text>
</TouchableOpacity>
<Text style={[styles.italicFont, { color: colors.text }]}>{views} views</Text>
</View>
</View>
)
}
Example #29
Source File: CreatePost.js From reddit-clone with MIT License | 4 votes |
CreatePost = () => {
const { colors } = useTheme()
const [isLoading, setIsLoading] = React.useState(false)
const [message, setMessage] = React.useState(null)
const fadeAnim = React.useRef(new Animated.Value(0)).current
const fadeIn = () => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: true
}).start()
setTimeout(() => {
setMessage(null)
}, 6000)
}
return (
<ScrollView as={SafeAreaView} style={[styles.container, { backgroundColor: colors.bgColor }]}>
<Formik
initialValues={{
type: 'text',
category: '',
title: '',
url: '',
text: ''
}}
onSubmit={async (values, { setStatus, resetForm }) => {
setIsLoading(true)
try {
await axios.post('posts', values)
resetForm({ ...values, type: 'text' })
setMessage('Successfully Created!')
fadeIn()
} catch (error) {
setStatus(error.response.data.message)
}
setIsLoading(false)
}}
validationSchema={Yup.object({
type: Yup.mixed().oneOf(['text', 'link']),
category: Yup.string().required('Required'),
title: Yup.string()
.required('Required')
.max(100, 'Must be at most 100 characters long'),
text: Yup.string().when('type', {
is: 'text',
then: Yup.string()
.required('Required')
.min(4, 'Must be at least 4 characters long')
}),
url: Yup.string().when('type', {
is: 'link',
then: Yup.string()
.required('Required')
.url('Invalid Url')
})
})}
>
{({
handleChange,
handleBlur,
handleSubmit,
touched,
errors,
status,
values,
setFieldValue
}) => (
<View>
{message && (
<Animated.View
style={{
opacity: fadeAnim
}}
>
{!!message && <Text style={styles.message}>{message}</Text>}
</Animated.View>
)}
{!!status && <Text style={styles.status}>{status}</Text>}
<View style={styles.flexRow}>
<Text style={[styles.formLabel, { color: colors.text }]}>Type</Text>
{touched.type && errors.type && (
<Text style={styles.errorMessage}>{errors.type}</Text>
)}
</View>
<TypeSwichContainer>
<TypeSwichButton selected={values.type} onClick={setFieldValue} type="text" />
<TypeSwichButton selected={values.type} onClick={setFieldValue} type="link" />
</TypeSwichContainer>
<View style={styles.flexRow}>
<Text style={[styles.formLabel, { color: colors.text }]}>Category</Text>
{touched.category && errors.category && (
<Text style={styles.errorMessage}>{errors.category}</Text>
)}
</View>
<CategoryPicker selectedCategory={values.category} setFieldValue={setFieldValue} />
<View style={styles.flexRow}>
<Text style={[styles.formLabel, { color: colors.text }]}>Title</Text>
{touched.title && errors.title && (
<Text style={styles.errorMessage}>{errors.title}</Text>
)}
</View>
<TextInput
style={[
styles.textInput,
{ borderColor: colors.border, color: colors.text, height: 40 },
touched.title && errors.title && { borderColor: colors.red }
]}
value={values.title}
onChangeText={handleChange('title')}
onBlur={handleBlur('title')}
/>
{values.type === 'link' ? (
<>
<View style={styles.flexRow}>
<Text style={[styles.formLabel, { color: colors.text }]}>Url</Text>
{touched.url && errors.url && (
<Text style={styles.errorMessage}>{errors.url}</Text>
)}
</View>
<TextInput
style={[
styles.textInput,
{ borderColor: colors.border, color: colors.text },
touched.url && errors.url && { borderColor: colors.red }
]}
multiline
value={values.url}
onChangeText={handleChange('url')}
onBlur={handleBlur('url')}
/>
</>
) : (
<>
<View style={styles.flexRow}>
<Text style={[styles.formLabel, { color: colors.text }]}>Text</Text>
{touched.text && errors.text && (
<Text style={styles.errorMessage}>{errors.text}</Text>
)}
</View>
<TextInput
style={[
styles.textInput,
{ borderColor: colors.border, color: colors.text },
touched.text && errors.text && { borderColor: colors.red }
]}
multiline
value={values.text}
onChangeText={handleChange('text')}
onBlur={handleBlur('text')}
/>
</>
)}
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.submitButton, { backgroundColor: colors.blue }]}
onPress={handleSubmit}
>
{isLoading ? (
<ActivityIndicator size="small" color="white" />
) : (
<Plus color="white" />
)}
<Text style={styles.submitButtonText}>Create Post</Text>
</TouchableOpacity>
</View>
</View>
)}
</Formik>
</ScrollView>
)
}