Update package dependencies and enhance AddonsScreen UI; add expo-dev-client to package.json, improve header layout with logo, and refactor addon management features for better user experience. Update styles for consistency and clarity.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1 MiB |
BIN
assets/icon.png
|
Before Width: | Height: | Size: 1 MiB After Width: | Height: | Size: 1.1 MiB |
112
package-lock.json
generated
|
|
@ -29,6 +29,7 @@
|
|||
"expo": "~52.0.43",
|
||||
"expo-auth-session": "^6.0.3",
|
||||
"expo-blur": "^14.0.3",
|
||||
"expo-dev-client": "~5.0.20",
|
||||
"expo-file-system": "^18.0.12",
|
||||
"expo-haptics": "~14.0.1",
|
||||
"expo-image": "~2.0.7",
|
||||
|
|
@ -4671,6 +4672,22 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/anser": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz",
|
||||
|
|
@ -6720,6 +6737,58 @@
|
|||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-client": {
|
||||
"version": "5.0.20",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.0.20.tgz",
|
||||
"integrity": "sha512-bLNkHdU7V3I4UefgJbJnIDUBUL0LxIal/xYEx9BbgDd3B7wgQKY//+BpPIxBOKCQ22lkyiHY8y9tLhO903sAgg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expo-dev-launcher": "5.0.35",
|
||||
"expo-dev-menu": "6.0.25",
|
||||
"expo-dev-menu-interface": "1.9.3",
|
||||
"expo-manifests": "~0.15.8",
|
||||
"expo-updates-interface": "~1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-launcher": {
|
||||
"version": "5.0.35",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-5.0.35.tgz",
|
||||
"integrity": "sha512-hEQr0ZREnUMxZ6wtQgfK1lzYnbb0zar3HqYZhmANzXmE6UEPbQ4GByLzhpfz/d+xxdBVQZsrHdtiV28KPG2sog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "8.11.0",
|
||||
"expo-dev-menu": "6.0.25",
|
||||
"expo-manifests": "~0.15.8",
|
||||
"resolve-from": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-menu": {
|
||||
"version": "6.0.25",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-6.0.25.tgz",
|
||||
"integrity": "sha512-K2m4z/I+CPWbMtHlDzU68lHaQs52De0v5gbsjAmA5ig8FrYh4MKZvPxSVANaiKENzgmtglu8qaFh7ua9Gt2TfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"expo-dev-menu-interface": "1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-dev-menu-interface": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-1.9.3.tgz",
|
||||
"integrity": "sha512-KY/dWTBE1l47i9V366JN5rC6YIdOc9hz8yAmZzkl5DrPia5l3M2WIjtnpHC9zUkNjiSiG2urYoOAq4H/uLdmyg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-file-system": {
|
||||
"version": "18.0.12",
|
||||
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz",
|
||||
|
|
@ -6781,6 +6850,12 @@
|
|||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-json-utils": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.14.0.tgz",
|
||||
"integrity": "sha512-xjGfK9dL0B1wLnOqNkX0jM9p48Y0I5xEPzHude28LY67UmamUyAACkqhZGaPClyPNfdzczk7Ej6WaRMT3HfXvw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expo-keep-awake": {
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.0.3.tgz",
|
||||
|
|
@ -6816,6 +6891,19 @@
|
|||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-manifests": {
|
||||
"version": "0.15.8",
|
||||
"resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.8.tgz",
|
||||
"integrity": "sha512-VuIyaMfRfLZeETNsRohqhy1l7iZ7I+HKMPfZXVL2Yn17TT0WkOhZoq1DzYwPbOHPgp1Uk6phNa86EyaHrD2DLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@expo/config": "~10.0.11",
|
||||
"expo-json-utils": "~0.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-modules-autolinking": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.0.8.tgz",
|
||||
|
|
@ -6959,6 +7047,15 @@
|
|||
"integrity": "sha512-FRjRvs7RgsXjkbGSOjYSxhX5V70c0IzA/jy3HXeYpATMwD9fOR1DbveLW497QGsVdCa0vThbJUtR8rIzAfpHQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expo-updates-interface": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-1.0.0.tgz",
|
||||
"integrity": "sha512-93oWtvULJOj+Pp+N/lpTcFfuREX1wNeHtp7Lwn8EbzYYmdn37MvZU3TPW2tYYCZuhzmKEXnUblYcruYoDu7IrQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-web-browser": {
|
||||
"version": "14.0.2",
|
||||
"resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-14.0.2.tgz",
|
||||
|
|
@ -8383,6 +8480,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
|
|
@ -12811,6 +12914,15 @@
|
|||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-latest-callback": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz",
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@
|
|||
"react-native-tab-view": "^4.0.10",
|
||||
"react-native-video": "^6.12.0",
|
||||
"react-native-web": "~0.19.13",
|
||||
"subsrt": "^1.1.1"
|
||||
"subsrt": "^1.1.1",
|
||||
"expo-dev-client": "~5.0.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
|
|
|
|||
BIN
src/assets/IMG_0761.PNG
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/IMG_0762.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { View, Text, TouchableOpacity, Platform, StyleSheet } from 'react-native';
|
||||
import { View, TouchableOpacity, Platform, StyleSheet, Image } from 'react-native';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { colors } from '../styles/colors';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
|
@ -38,9 +38,12 @@ export const NuvioHeader = () => {
|
|||
)
|
||||
)}
|
||||
<View style={styles.contentContainer}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title}>NUVIO</Text>
|
||||
<View style={styles.titleAccent} />
|
||||
<View style={styles.logoContainer}>
|
||||
<Image
|
||||
source={require('../assets/IMG_0762.png')}
|
||||
style={styles.logo}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.searchButton}
|
||||
|
|
@ -70,7 +73,7 @@ const styles = StyleSheet.create({
|
|||
overflow: 'hidden',
|
||||
},
|
||||
headerContainer: {
|
||||
height: Platform.OS === 'ios' ? 85 : 75,
|
||||
height: Platform.OS === 'ios' ? 100 : 90,
|
||||
paddingTop: Platform.OS === 'ios' ? 35 : 20,
|
||||
backgroundColor: 'rgba(0,0,0,0.3)',
|
||||
},
|
||||
|
|
@ -97,35 +100,23 @@ const styles = StyleSheet.create({
|
|||
contentContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 24,
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 24,
|
||||
height: '100%',
|
||||
},
|
||||
titleContainer: {
|
||||
position: 'relative',
|
||||
logoContainer: {
|
||||
height: 70,
|
||||
width: 70,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
title: {
|
||||
fontSize: 32,
|
||||
fontWeight: '900',
|
||||
color: colors.white,
|
||||
letterSpacing: 2,
|
||||
fontFamily: Platform.OS === 'ios' ? 'System' : 'sans-serif-black',
|
||||
textTransform: 'uppercase',
|
||||
marginLeft: Platform.OS === 'ios' ? -4 : -8,
|
||||
textShadowColor: 'rgba(255, 255, 255, 0.2)',
|
||||
textShadowOffset: { width: 0, height: 1 },
|
||||
textShadowRadius: 4,
|
||||
},
|
||||
titleAccent: {
|
||||
position: 'absolute',
|
||||
bottom: -3,
|
||||
left: Platform.OS === 'ios' ? -2 : -6,
|
||||
width: 24,
|
||||
height: 2,
|
||||
backgroundColor: colors.primary || '#3D85C6',
|
||||
borderRadius: 1,
|
||||
logo: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
},
|
||||
searchButton: {
|
||||
padding: 8,
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
iconWrapper: {
|
||||
width: 40,
|
||||
|
|
|
|||
|
|
@ -63,9 +63,7 @@ const AddonsScreen = () => {
|
|||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||
const [installing, setInstalling] = useState(false);
|
||||
const [catalogCount, setCatalogCount] = useState(0);
|
||||
// Add state for reorder mode
|
||||
const [reorderMode, setReorderMode] = useState(false);
|
||||
// Force dark mode
|
||||
const isDarkMode = true;
|
||||
|
||||
// State for community addons
|
||||
|
|
@ -502,333 +500,124 @@ const AddonsScreen = () => {
|
|||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<SafeAreaView style={[styles.container, { backgroundColor: isDarkMode ? colors.background : colors.white }]}>
|
||||
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity
|
||||
style={styles.backButton}
|
||||
onPress={() => navigation.goBack()}
|
||||
>
|
||||
<MaterialIcons name="chevron-left" size={28} color={colors.white} />
|
||||
<Text style={styles.backText}>Settings</Text>
|
||||
<Text style={styles.title}>Addons</Text>
|
||||
<TouchableOpacity onPress={toggleReorderMode} style={styles.reorderButton}>
|
||||
<MaterialIcons
|
||||
name={reorderMode ? "done" : "reorder"}
|
||||
size={24}
|
||||
color={colors.primary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.headerActions}>
|
||||
{/* Reorder Mode Toggle Button */}
|
||||
<TouchableOpacity
|
||||
style={[styles.headerButton, reorderMode && styles.activeHeaderButton]}
|
||||
onPress={toggleReorderMode}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="swap-vert"
|
||||
size={24}
|
||||
color={reorderMode ? colors.primary : colors.white}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Refresh Button */}
|
||||
<TouchableOpacity
|
||||
style={styles.headerButton}
|
||||
onPress={refreshAddons}
|
||||
disabled={loading}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="refresh"
|
||||
size={24}
|
||||
color={loading ? colors.mediumGray : colors.white}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text style={styles.headerTitle}>
|
||||
Addons
|
||||
{reorderMode && <Text style={styles.reorderModeText}> (Reorder Mode)</Text>}
|
||||
</Text>
|
||||
|
||||
{reorderMode && (
|
||||
<View style={styles.reorderInfoBanner}>
|
||||
<MaterialIcons name="info-outline" size={18} color={colors.primary} />
|
||||
<Text style={styles.reorderInfoText}>
|
||||
Addons at the top have higher priority when loading content
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
||||
<View style={styles.statsContainer}>
|
||||
<StatsCard value={addons.length} label="Installed" />
|
||||
<StatsCard value={catalogCount} label="Catalogs" />
|
||||
</View>
|
||||
|
||||
<View style={styles.searchContainer}>
|
||||
<TextInput
|
||||
style={styles.searchInput}
|
||||
placeholder="Enter addon URL..."
|
||||
placeholderTextColor={colors.mediumGray}
|
||||
value={addonUrl}
|
||||
onChangeText={setAddonUrl}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={[styles.addButton, !addonUrl && styles.disabledButton]}
|
||||
onPress={() => handleAddAddon()}
|
||||
disabled={!addonUrl || installing}
|
||||
>
|
||||
{installing ? (
|
||||
<ActivityIndicator size="small" color={colors.white} />
|
||||
) : (
|
||||
<MaterialIcons name="add" size={24} color={colors.white} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{loading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
<Text style={styles.loadingText}>Loading addons...</Text>
|
||||
</View>
|
||||
) : (
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
>
|
||||
{/* Overview Section */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>OVERVIEW</Text>
|
||||
<View style={styles.statsContainer}>
|
||||
<StatsCard value={addons.length} label="Addons" />
|
||||
<View style={styles.statsDivider} />
|
||||
<StatsCard value={addons.length} label="Active" />
|
||||
<View style={styles.statsDivider} />
|
||||
<StatsCard value={catalogCount} label="Catalogs" />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Hide Add Addon Section in reorder mode */}
|
||||
{!reorderMode && (
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>ADD NEW ADDON</Text>
|
||||
<View style={styles.addAddonContainer}>
|
||||
<TextInput
|
||||
style={styles.addonInput}
|
||||
placeholder="Addon URL"
|
||||
placeholderTextColor={colors.mediumGray}
|
||||
value={addonUrl}
|
||||
onChangeText={setAddonUrl}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={[styles.addButton, {opacity: installing || !addonUrl ? 0.6 : 1}]}
|
||||
onPress={() => handleAddAddon()}
|
||||
disabled={installing || !addonUrl}
|
||||
>
|
||||
<Text style={styles.addButtonText}>
|
||||
{installing ? 'Loading...' : 'Add Addon'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<FlatList
|
||||
data={addons}
|
||||
renderItem={renderAddonItem}
|
||||
keyExtractor={(item) => item.id}
|
||||
style={styles.list}
|
||||
contentContainerStyle={styles.listContent}
|
||||
ListEmptyComponent={() => (
|
||||
<View style={styles.emptyContainer}>
|
||||
<MaterialIcons name="extension-off" size={48} color={colors.mediumGray} />
|
||||
<Text style={styles.emptyText}>No addons installed</Text>
|
||||
<Text style={styles.emptySubtext}>Add an addon using the URL field above</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Installed Addons Section */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>
|
||||
{reorderMode ? "DRAG ADDONS TO REORDER" : "INSTALLED ADDONS"}
|
||||
</Text>
|
||||
<View style={styles.addonList}>
|
||||
{addons.length === 0 ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<MaterialIcons name="extension-off" size={32} color={colors.mediumGray} />
|
||||
<Text style={styles.emptyText}>No addons installed</Text>
|
||||
</View>
|
||||
) : (
|
||||
addons.map((addon, index) => (
|
||||
<View
|
||||
key={addon.id}
|
||||
style={{ marginBottom: index === addons.length - 1 ? 32 : 0 }}
|
||||
>
|
||||
{renderAddonItem({ item: addon, index })}
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Separator */}
|
||||
<View style={styles.sectionSeparator} />
|
||||
|
||||
{/* Community Addons Section */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>COMMUNITY ADDONS</Text>
|
||||
<View style={styles.addonList}>
|
||||
{communityLoading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color={colors.primary} />
|
||||
</View>
|
||||
) : communityError ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<MaterialIcons name="error-outline" size={32} color={colors.error} />
|
||||
<Text style={styles.emptyText}>{communityError}</Text>
|
||||
</View>
|
||||
) : communityAddons.length === 0 ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<MaterialIcons name="extension-off" size={32} color={colors.mediumGray} />
|
||||
<Text style={styles.emptyText}>No community addons available</Text>
|
||||
</View>
|
||||
) : (
|
||||
communityAddons.map((item, index) => (
|
||||
<View
|
||||
key={item.transportUrl}
|
||||
style={{ marginBottom: index === communityAddons.length - 1 ? 32 : 16 }}
|
||||
>
|
||||
<View style={styles.addonItem}>
|
||||
<View style={styles.addonHeader}>
|
||||
{item.manifest.logo ? (
|
||||
<ExpoImage
|
||||
source={{ uri: item.manifest.logo }}
|
||||
style={styles.addonIcon}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.addonIconPlaceholder}>
|
||||
<MaterialIcons name="extension" size={22} color={colors.mediumGray} />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.addonTitleContainer}>
|
||||
<Text style={styles.addonName}>{item.manifest.name}</Text>
|
||||
<View style={styles.addonMetaContainer}>
|
||||
<Text style={styles.addonVersion}>v{item.manifest.version || 'N/A'}</Text>
|
||||
<Text style={styles.addonDot}>•</Text>
|
||||
<Text style={styles.addonCategory}>
|
||||
{item.manifest.types && item.manifest.types.length > 0
|
||||
? item.manifest.types.map(t => t.charAt(0).toUpperCase() + t.slice(1)).join(' • ')
|
||||
: 'General'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.addonActions}>
|
||||
{item.manifest.behaviorHints?.configurable && (
|
||||
<TouchableOpacity
|
||||
style={styles.configButton}
|
||||
onPress={() => handleConfigureAddon(item.manifest, item.transportUrl)}
|
||||
>
|
||||
<MaterialIcons name="settings" size={20} color={colors.primary} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
style={[styles.installButton, installing && { opacity: 0.6 }]}
|
||||
onPress={() => handleAddAddon(item.transportUrl)}
|
||||
disabled={installing}
|
||||
>
|
||||
{installing ? (
|
||||
<ActivityIndicator size="small" color={colors.white} />
|
||||
) : (
|
||||
<MaterialIcons name="add" size={20} color={colors.white} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text style={styles.addonDescription}>
|
||||
{item.manifest.description
|
||||
? (item.manifest.description.length > 100
|
||||
? item.manifest.description.substring(0, 100) + '...'
|
||||
: item.manifest.description)
|
||||
: 'No description provided.'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Addon Details Confirmation Modal */}
|
||||
{/* Community Addons Section */}
|
||||
<View style={styles.communitySection}>
|
||||
<Text style={styles.sectionTitle}>Community Addons</Text>
|
||||
{communityLoading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="small" color={colors.primary} />
|
||||
<Text style={styles.loadingText}>Loading community addons...</Text>
|
||||
</View>
|
||||
) : communityError ? (
|
||||
<View style={styles.errorContainer}>
|
||||
<MaterialIcons name="error-outline" size={24} color={colors.error} />
|
||||
<Text style={styles.errorText}>{communityError}</Text>
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
data={communityAddons}
|
||||
renderItem={renderCommunityAddonItem}
|
||||
keyExtractor={(item) => item.manifest.id}
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={styles.communityList}
|
||||
contentContainerStyle={styles.communityListContent}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Confirmation Modal */}
|
||||
<Modal
|
||||
visible={showConfirmModal}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => {
|
||||
setShowConfirmModal(false);
|
||||
setAddonDetails(null);
|
||||
}}
|
||||
onRequestClose={() => setShowConfirmModal(false)}
|
||||
>
|
||||
<BlurView intensity={80} style={styles.modalContainer} tint="dark">
|
||||
<BlurView intensity={100} style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<Text style={styles.modalTitle}>Install Addon</Text>
|
||||
{addonDetails && (
|
||||
<>
|
||||
<View style={styles.modalHeader}>
|
||||
<Text style={styles.modalTitle}>Install Addon</Text>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setShowConfirmModal(false);
|
||||
setAddonDetails(null);
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="close" size={24} color={colors.white} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<ScrollView
|
||||
style={styles.modalScrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
bounces={true}
|
||||
>
|
||||
<View style={styles.addonDetailHeader}>
|
||||
{/* @ts-ignore */}
|
||||
{addonDetails.logo ? (
|
||||
<ExpoImage
|
||||
source={{ uri: addonDetails.logo }}
|
||||
style={styles.addonLogo}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.addonLogoPlaceholder}>
|
||||
<MaterialIcons name="extension" size={40} color={colors.mediumGray} />
|
||||
</View>
|
||||
)}
|
||||
<Text style={styles.addonDetailName}>{addonDetails.name}</Text>
|
||||
<Text style={styles.addonDetailVersion}>v{addonDetails.version || '1.0.0'}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.addonDetailSection}>
|
||||
<Text style={styles.addonDetailSectionTitle}>Description</Text>
|
||||
<Text style={styles.addonDetailDescription}>
|
||||
{addonDetails.description || 'No description available'}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{addonDetails.types && addonDetails.types.length > 0 && (
|
||||
<View style={styles.addonDetailSection}>
|
||||
<Text style={styles.addonDetailSectionTitle}>Supported Types</Text>
|
||||
<View style={styles.addonDetailChips}>
|
||||
{addonDetails.types.map((type, index) => (
|
||||
<View key={index} style={styles.addonDetailChip}>
|
||||
<Text style={styles.addonDetailChipText}>{type}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{addonDetails.catalogs && addonDetails.catalogs.length > 0 && (
|
||||
<View style={styles.addonDetailSection}>
|
||||
<Text style={styles.addonDetailSectionTitle}>Catalogs</Text>
|
||||
<View style={styles.addonDetailChips}>
|
||||
{addonDetails.catalogs.map((catalog, index) => (
|
||||
<View key={index} style={styles.addonDetailChip}>
|
||||
<Text style={styles.addonDetailChipText}>
|
||||
{catalog.type} - {catalog.id}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
<View style={styles.modalActions}>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalButton, styles.cancelButton]}
|
||||
onPress={() => {
|
||||
setShowConfirmModal(false);
|
||||
setAddonDetails(null);
|
||||
}}
|
||||
>
|
||||
<Text style={styles.modalButtonText}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalButton, styles.installButton]}
|
||||
onPress={confirmInstallAddon}
|
||||
disabled={installing}
|
||||
>
|
||||
{installing ? (
|
||||
<ActivityIndicator size="small" color={colors.white} />
|
||||
) : (
|
||||
<Text style={styles.modalButtonText}>Install</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Text style={styles.modalAddonName}>{addonDetails.name}</Text>
|
||||
<Text style={styles.modalAddonDesc}>{addonDetails.description}</Text>
|
||||
</>
|
||||
)}
|
||||
<View style={styles.modalButtons}>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalButton, styles.cancelButton]}
|
||||
onPress={() => setShowConfirmModal(false)}
|
||||
>
|
||||
<Text style={styles.modalButtonText}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.modalButton, styles.confirmButton]}
|
||||
onPress={confirmInstallAddon}
|
||||
>
|
||||
<Text style={styles.modalButtonText}>Install</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</BlurView>
|
||||
</Modal>
|
||||
|
|
@ -839,197 +628,156 @@ const AddonsScreen = () => {
|
|||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.darkBackground,
|
||||
paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT : 0,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 8 : 8,
|
||||
paddingVertical: 12,
|
||||
},
|
||||
headerActions: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
headerButton: {
|
||||
padding: 8,
|
||||
marginLeft: 8,
|
||||
},
|
||||
activeHeaderButton: {
|
||||
backgroundColor: 'rgba(45, 156, 219, 0.2)',
|
||||
borderRadius: 6,
|
||||
},
|
||||
reorderModeText: {
|
||||
color: colors.primary,
|
||||
fontSize: 18,
|
||||
fontWeight: '400',
|
||||
},
|
||||
reorderInfoBanner: {
|
||||
backgroundColor: 'rgba(45, 156, 219, 0.15)',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
marginHorizontal: 16,
|
||||
borderRadius: 8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
},
|
||||
reorderInfoText: {
|
||||
color: colors.white,
|
||||
fontSize: 14,
|
||||
marginLeft: 8,
|
||||
},
|
||||
reorderButtons: {
|
||||
position: 'absolute',
|
||||
left: -12,
|
||||
top: '50%',
|
||||
marginTop: -40,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 10,
|
||||
},
|
||||
reorderButton: {
|
||||
backgroundColor: colors.elevation3,
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 15,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginVertical: 4,
|
||||
},
|
||||
disabledButton: {
|
||||
opacity: 0.5,
|
||||
backgroundColor: colors.elevation2,
|
||||
},
|
||||
priorityBadge: {
|
||||
backgroundColor: colors.primary,
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
},
|
||||
priorityText: {
|
||||
color: colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
backButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 8,
|
||||
},
|
||||
backText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '400',
|
||||
color: colors.primary,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 34,
|
||||
fontWeight: '700',
|
||||
color: colors.white,
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 16,
|
||||
paddingTop: 8,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
color: colors.mediumGray,
|
||||
marginHorizontal: 16,
|
||||
marginBottom: 8,
|
||||
letterSpacing: 0.5,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
statsContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginHorizontal: 16,
|
||||
backgroundColor: colors.elevation2,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 2,
|
||||
},
|
||||
statsCard: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
statsDivider: {
|
||||
width: 1,
|
||||
height: '80%',
|
||||
backgroundColor: 'rgba(150, 150, 150, 0.2)',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
statsValue: {
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
marginBottom: 4,
|
||||
},
|
||||
statsLabel: {
|
||||
fontSize: 13,
|
||||
color: colors.mediumGray,
|
||||
},
|
||||
addAddonContainer: {
|
||||
marginHorizontal: 16,
|
||||
backgroundColor: colors.elevation2,
|
||||
borderRadius: 12,
|
||||
statsContainer: {
|
||||
flexDirection: 'row',
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 2,
|
||||
justifyContent: 'space-around',
|
||||
},
|
||||
addonInput: {
|
||||
backgroundColor: colors.elevation1,
|
||||
searchContainer: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 16,
|
||||
alignItems: 'center',
|
||||
},
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
height: 40,
|
||||
backgroundColor: colors.darkGray,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
paddingHorizontal: 12,
|
||||
marginRight: 8,
|
||||
color: colors.white,
|
||||
marginBottom: 16,
|
||||
fontSize: 15,
|
||||
},
|
||||
addButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: colors.primary,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
addButtonText: {
|
||||
color: colors.white,
|
||||
fontWeight: '600',
|
||||
fontSize: 16,
|
||||
disabledButton: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
addonList: {
|
||||
list: {
|
||||
flex: 1,
|
||||
},
|
||||
listContent: {
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
emptyContainer: {
|
||||
backgroundColor: colors.elevation2,
|
||||
borderRadius: 12,
|
||||
padding: 32,
|
||||
alignItems: 'center',
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 2,
|
||||
alignItems: 'center',
|
||||
},
|
||||
loadingText: {
|
||||
marginTop: 12,
|
||||
color: colors.mediumGray,
|
||||
},
|
||||
emptyContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 32,
|
||||
},
|
||||
emptyText: {
|
||||
marginTop: 8,
|
||||
fontSize: 16,
|
||||
color: colors.mediumGray,
|
||||
fontSize: 15,
|
||||
marginTop: 16,
|
||||
},
|
||||
emptySubtext: {
|
||||
fontSize: 14,
|
||||
color: colors.mediumGray,
|
||||
marginTop: 8,
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
modalContent: {
|
||||
backgroundColor: colors.darkGray,
|
||||
borderRadius: 12,
|
||||
padding: 24,
|
||||
width: '80%',
|
||||
maxWidth: 400,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
marginBottom: 16,
|
||||
},
|
||||
modalAddonName: {
|
||||
fontSize: 16,
|
||||
color: colors.white,
|
||||
marginBottom: 8,
|
||||
},
|
||||
modalAddonDesc: {
|
||||
fontSize: 14,
|
||||
color: colors.mediumGray,
|
||||
marginBottom: 24,
|
||||
},
|
||||
modalButtons: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
modalButton: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 6,
|
||||
marginLeft: 12,
|
||||
},
|
||||
modalButtonText: {
|
||||
color: colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
cancelButton: {
|
||||
backgroundColor: colors.mediumGray,
|
||||
},
|
||||
confirmButton: {
|
||||
backgroundColor: colors.primary,
|
||||
},
|
||||
communitySection: {
|
||||
paddingTop: 16,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 12,
|
||||
},
|
||||
communityList: {
|
||||
height: 160,
|
||||
},
|
||||
communityListContent: {
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
errorContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
},
|
||||
errorText: {
|
||||
color: colors.error,
|
||||
marginLeft: 8,
|
||||
},
|
||||
addonItem: {
|
||||
backgroundColor: colors.elevation2,
|
||||
|
|
@ -1099,136 +847,29 @@ const styles = StyleSheet.create({
|
|||
lineHeight: 20,
|
||||
marginLeft: 48, // Align with title, accounting for icon width
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
reorderButton: {
|
||||
padding: 8,
|
||||
},
|
||||
reorderButtons: {
|
||||
position: 'absolute',
|
||||
left: -12,
|
||||
top: '50%',
|
||||
marginTop: -40,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 10,
|
||||
},
|
||||
modalContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
modalContent: {
|
||||
backgroundColor: colors.elevation2,
|
||||
borderRadius: 14,
|
||||
width: '85%',
|
||||
maxHeight: '85%',
|
||||
overflow: 'hidden',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 8,
|
||||
elevation: 5,
|
||||
},
|
||||
modalHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.elevation3,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 17,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
},
|
||||
modalScrollContent: {
|
||||
maxHeight: 400,
|
||||
},
|
||||
addonDetailHeader: {
|
||||
alignItems: 'center',
|
||||
padding: 24,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.elevation3,
|
||||
},
|
||||
addonLogo: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 12,
|
||||
marginBottom: 16,
|
||||
backgroundColor: colors.elevation3,
|
||||
},
|
||||
addonLogoPlaceholder: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 12,
|
||||
backgroundColor: colors.elevation3,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
},
|
||||
addonDetailName: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
marginBottom: 4,
|
||||
textAlign: 'center',
|
||||
},
|
||||
addonDetailVersion: {
|
||||
fontSize: 14,
|
||||
color: colors.mediumGray,
|
||||
},
|
||||
addonDetailSection: {
|
||||
padding: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.elevation3,
|
||||
},
|
||||
addonDetailSectionTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: colors.white,
|
||||
marginBottom: 8,
|
||||
},
|
||||
addonDetailDescription: {
|
||||
fontSize: 15,
|
||||
color: colors.mediumEmphasis,
|
||||
lineHeight: 20,
|
||||
},
|
||||
addonDetailChips: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
},
|
||||
addonDetailChip: {
|
||||
backgroundColor: colors.elevation3,
|
||||
priorityBadge: {
|
||||
backgroundColor: colors.primary,
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
paddingVertical: 3,
|
||||
},
|
||||
addonDetailChipText: {
|
||||
fontSize: 13,
|
||||
priorityText: {
|
||||
color: colors.white,
|
||||
},
|
||||
modalActions: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
padding: 16,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.elevation3,
|
||||
},
|
||||
modalButton: {
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 16,
|
||||
borderRadius: 8,
|
||||
minWidth: 80,
|
||||
alignItems: 'center',
|
||||
},
|
||||
cancelButton: {
|
||||
backgroundColor: colors.elevation3,
|
||||
marginRight: 8,
|
||||
},
|
||||
installButton: {
|
||||
backgroundColor: colors.success,
|
||||
borderRadius: 6,
|
||||
padding: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
modalButtonText: {
|
||||
color: colors.white,
|
||||
fontWeight: '600',
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
addonActions: {
|
||||
flexDirection: 'row',
|
||||
|
|
@ -1241,9 +882,6 @@ const styles = StyleSheet.create({
|
|||
padding: 6,
|
||||
marginRight: 8,
|
||||
},
|
||||
communityAddonsList: {
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
communityAddonItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
|
@ -1333,6 +971,30 @@ const styles = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
installButton: {
|
||||
backgroundColor: colors.success,
|
||||
borderRadius: 6,
|
||||
padding: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
statsCard: {
|
||||
backgroundColor: colors.darkGray,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
alignItems: 'center',
|
||||
minWidth: 100,
|
||||
},
|
||||
statsValue: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: colors.white,
|
||||
marginBottom: 4,
|
||||
},
|
||||
statsLabel: {
|
||||
fontSize: 13,
|
||||
color: colors.mediumGray,
|
||||
},
|
||||
});
|
||||
|
||||
export default AddonsScreen;
|
||||
|
|
@ -1237,8 +1237,8 @@ const styles = StyleSheet.create({
|
|||
width: '100%',
|
||||
},
|
||||
titleLogo: {
|
||||
width: width * 0.65,
|
||||
height: 70,
|
||||
width: width * 0.8,
|
||||
height: 100,
|
||||
marginBottom: 0,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
|
|
|
|||