Переделай минималистично, в стиле дискорд. Придумай светлую тему дискорда. Сам придумай. Не надо уродский обычный белый цвет делать. Форму элементов и их расопложение тоже переделай.
сделай по правилам UX. Интерфейс ужасен. Дизайн должен быть минималистичным, интерактивным, лайтовым. Текст на кнопках в Home.js должен быть по центру, все должно быть аккуратно, приятно для восприятия. При смене темы на светлый, должна быть гармоничная красивая тема. Придумай оригинальный сочетаемый дизайн, как лучший профессионал дизайнер UX UI. Полностью переделай код. Добавь тени и подсветку.
просто сделай нормальный код. Я не программист и не умею писать код, так что не ленись, покажи код без сокращений а-ля "пиши сам, я даю только идеи!"
покажи без сокражений. Полностью. От начала до конца весь код, буквально каждый символ покажи, ничего не оставляй мне, потому что я не умею псиать код
покажи исправленный код от начала до конца. Не оставляй части кода на потом, пиши сразу всё
ХВАТИТ ГОВОРИТЬ "ЗДЕСЬ ЧТО-ТО БУДЕТ", СДЕЛАЙ ЭТО БЕЗ ХАЛТУРЫ В ВИДЕ КОММЕНТАРИЕВ, СДЕЛАЙ ПОЛНОЦЕННЫЙ КОД!!!! БУДЬ ОРИГИНАЛЕН!!! СДЕЛАЙ МАКСИМАЛЬНО КРУТОЙ КОД, НАСКОЛЬКО ЭТО ВООБЩЕ ВОЗМОЖНО. Я ДАЮ ТЕБЕ ПОЛНОЕ ПРАВО ФАНТАЗИРОВАТЬ И СОЗДАТЬ САМОЕ КРУТОЕ, ЧТО ТЫ СМОЖЕШЬ СОЗДАТЬ ИЗ ЛУЧШИХ ПРАКТИК КРУТЕЙШИХ ПРОГРАММИСТОВ ЭТОГО МИРА! покажи весь код полностью
сделай в миллион раз круче, анимации, оформление, функицонал и вообще все. Сделай лучше приложение во вселенной!:
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/android/build.gradle
File: build.gradle
buildscript {
ext {
buildToolsVersion = "34.0.0"
minSdkVersion = 23
compileSdkVersion = 34
targetSdkVersion = 34
ndkVersion = "26.1.10909125"
kotlinVersion = "1.9.24"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
}
}
apply plugin: "com.facebook.react.rootproject"
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/android/app/build.gradle
File: build.gradle
apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"
/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
*/
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '../..'
// root = file("../../")
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
// reactNativeDir = file("../../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
// codegenDir = file("../../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
// cliFile = file("../../node_modules/react-native/cli.js")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
/* Bundling */
// A list containing the node command and its flags. Default is just 'node'.
// nodeExecutableAndArgs = ["node"]
//
// The command to run when bundling. By default is 'bundle'
// bundleCommand = "ram-bundle"
//
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/MyApplication.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */
// The hermes compiler command to run. By default it is 'hermesc'
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
/* Autolinking */
autolinkLibrariesWithApp()
}
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore (JSC)
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
android {
ndkVersion rootProject.ext.ndkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
compileSdk rootProject.ext.compileSdkVersion
namespace "com.arsennikiforovcli"
defaultConfig {
applicationId "com.arsennikiforovcli"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
}
dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
}
}
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/android/settings.gradle
File: settings.gradle
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
plugins { id("com.facebook.react.settings") }
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
rootProject.name = 'arsenNikiforovCLI'
include ':app'
includeBuild('../node_modules/@react-native/gradle-plugin')
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/App.js
File: App.js
// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { ThemeProvider } from './ThemeContext';
import Home from './screens/Home';
import Lab1 from './screens/Lab1';
import Lab2 from './screens/Lab2';
import Lab3 from './screens/Lab3';
import Lab4 from './screens/Lab4';
import SavedImage from './screens/SavedImage';
const Stack = createNativeStackNavigator();
const App = () => {
return (
<ThemeProvider>
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Lab1" component={Lab1} />
<Stack.Screen name="Lab2" component={Lab2} />
<Stack.Screen name="Lab3" component={Lab3} />
<Stack.Screen name="Lab4" component={Lab4} />
<Stack.Screen name="SavedImage" component={SavedImage} />
</Stack.Navigator>
</NavigationContainer>
</ThemeProvider>
);
};
export default App;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/screens/Home.js
File: Home.js
import React, { useContext } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Dimensions } from 'react-native';
import { ThemeContext } from '../ThemeContext';
const { width, height } = Dimensions.get('window');
function Home({ navigation }) {
const { colors } = useContext(ThemeContext);
const handleNavigation = (path) => {
navigation.navigate(path);
};
const menuItems = [
{ path: 'Lab1', text: 'Bubble Game', icon: '🎮' },
{ path: 'Lab2', text: 'NASA API', icon: '🛸' },
{ path: 'Lab3', text: 'Space Calculator', icon: '🧮' },
{ path: 'Lab4', text: 'Redux Theme', icon: '⚗️' },
];
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
<View style={styles.content}>
<View style={[styles.card, { backgroundColor: colors.secondary, borderColor: colors.primary }]}>
<Text style={[styles.title, { color: colors.primary, textAlign: 'center' }]}>CyberLabs 2077</Text>
<Text style={[styles.subtitle, { color: colors.text, textAlign: 'center' }]}>Лабораторные работы</Text>
<View style={styles.studentInfo}>
<Text style={[styles.studentText, { color: colors.text, textAlign: 'center' }]}>Студент: Никифоров Арсен</Text>
<Text style={[styles.studentText, { color: colors.text, textAlign: 'center' }]}>Группа: ФИИТ-21</Text>
</View>
<Text style={[styles.selectionText, { color: colors.text, textAlign: 'center' }]}>Выберите экран:</Text>
<View style={styles.menuContainer}>
{menuItems.map(({ path, text, icon }) => (
<TouchableOpacity
key={path}
onPress={() => handleNavigation(path)}
style={[styles.menuItem, { backgroundColor: colors.secondary, borderColor: colors.primary }]}
>
<View style={[styles.menuItemContent, { justifyContent: 'center' }]}>
<Text style={[styles.icon, { color: colors.primary }]}>{icon}</Text>
<Text style={[styles.menuText, { color: colors.text, textAlign: 'center' }]}>{text}</Text>
</View>
</TouchableOpacity>
))}
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
width,
height,
alignItems: 'center',
justifyContent: 'center',
},
content: {
width: '90%',
maxWidth: 400,
alignItems: 'center',
},
card: {
padding: 20,
borderRadius: 15,
borderWidth: 1.5,
width: '100%',
},
title: {
fontSize: 34,
fontWeight: 'bold',
marginBottom: 10,
},
subtitle: {
fontSize: 22,
marginBottom: 15,
},
studentInfo: {
marginBottom: 15,
alignItems: 'center',
},
studentText: {
fontSize: 18,
marginBottom: 2,
},
selectionText: {
fontSize: 18,
marginBottom: 15,
},
menuContainer: {
width: '100%',
marginTop: 10,
},
menuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 14,
paddingHorizontal: 20,
borderRadius: 12,
marginBottom: 10,
borderWidth: 1.5,
},
menuItemContent: {
flexDirection: 'row',
alignItems: 'center',
},
icon: {
fontSize: 22,
marginRight: 12,
},
menuText: {
fontSize: 18,
},
});
export default Home;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/screens/lab1.js
File: lab1.js
// Lab1.js
import React, { useState, useEffect, useCallback, useContext } from 'react';
import {
View,
TouchableOpacity,
Modal,
Text,
Pressable,
StyleSheet,
Dimensions,
} from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { incrementCounter } from '../store/store';
import ThemedBackground from '../components/ThemedBackground';
import { ThemedText } from '../components/ThemedText';
import Bubble from '../components/Bubble';
import { ThemeContext } from '../ThemeContext';
const { width, height } = Dimensions.get('window');
const Lab1 = () => {
const [bubbles, setBubbles] = useState([]);
const [score, setScore] = useState(0);
const [gameOver, setGameOver] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const counter = useSelector((state) => state.counter);
const dispatch = useDispatch();
const { colors } = useContext(ThemeContext);
const addBubble = useCallback(() => {
const id = Date.now() + Math.random();
setBubbles((prevBubbles) => [...prevBubbles, { id }]);
}, []);
const removeBubble = useCallback((id) => {
setBubbles((prevBubbles) => prevBubbles.filter((bubble) => bubble.id !== id));
}, []);
const onDrag = useCallback(() => {
setScore((prevScore) => prevScore + 5);
dispatch(incrementCounter());
}, [dispatch]);
useEffect(() => {
if (!gameOver) {
const interval = setInterval(addBubble, 2000);
return () => clearInterval(interval);
}
}, [addBubble, gameOver]);
useEffect(() => {
const timer = setTimeout(() => {
setGameOver(true);
}, 20000);
return () => clearTimeout(timer);
}, []);
const resetGame = () => {
setBubbles([]);
setScore(0);
setGameOver(false);
};
const styles = StyleSheet.create({
header: {
position: 'absolute',
top: 40,
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 20,
zIndex: 1,
},
infoButton: {
padding: 8,
borderRadius: 5,
backgroundColor: colors.accent,
alignItems: 'center',
},
infoButtonText: {
fontSize: 16,
color: colors.text,
fontWeight: '600',
},
scoreText: {
fontSize: 18,
fontWeight: '500',
color: colors.text,
},
counterText: {
fontSize: 18,
color: colors.text,
},
touchable: {
flex: 1,
justifyContent: 'center',
},
modalBackground: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.6)',
},
modalContainer: {
width: 320,
padding: 24,
borderRadius: 8,
backgroundColor: colors.secondary,
alignItems: 'center',
},
modalTitle: {
fontSize: 20,
fontWeight: '700',
color: colors.text,
marginBottom: 10,
},
modalText: {
fontSize: 16,
color: colors.text,
marginBottom: 20,
textAlign: 'center',
},
closeButton: {
padding: 10,
borderRadius: 5,
alignItems: 'center',
backgroundColor: colors.accent,
},
closeButtonText: {
color: colors.text,
fontSize: 16,
fontWeight: '600',
},
gameOverContainer: {
position: 'absolute',
top: height / 3,
left: width / 6,
right: width / 6,
backgroundColor: colors.secondary,
padding: 20,
borderRadius: 8,
alignItems: 'center',
},
gameOverText: {
fontSize: 22,
fontWeight: '700',
color: colors.text,
},
finalScoreText: {
fontSize: 18,
color: colors.text,
marginVertical: 10,
},
restartButton: {
padding: 10,
borderRadius: 5,
backgroundColor: colors.accent,
},
restartButtonText: {
fontSize: 16,
color: colors.text,
fontWeight: '600',
},
});
return (
<ThemedBackground>
<View style={styles.header}>
<TouchableOpacity style={styles.infoButton} onPress={() => setModalVisible(true)}>
<Text style={styles.infoButtonText}>Info</Text>
</TouchableOpacity>
<Text style={styles.scoreText}>Score: {score}</Text>
<Text style={styles.counterText}>Counter: {counter}</Text>
</View>
<TouchableOpacity style={styles.touchable} onPress={addBubble}>
{bubbles.map((bubble) => (
<Bubble
key={bubble.id}
id={bubble.id}
removeBubble={removeBubble}
onDrag={onDrag}
gameOver={gameOver}
/>
))}
</TouchableOpacity>
<Modal transparent={true} visible={modalVisible} animationType="fade">
<View style={styles.modalBackground}>
<View style={styles.modalContainer}>
<Text style={styles.modalTitle}>How to Play</Text>
<Text style={styles.modalText}>Create bubbles and drag them to earn points!</Text>
<Pressable style={styles.closeButton} onPress={() => setModalVisible(false)}>
<Text style={styles.closeButtonText}>Close</Text>
</Pressable>
</View>
</View>
</Modal>
{gameOver && (
<View style={styles.gameOverContainer}>
<Text style={styles.gameOverText}>Game Over!</Text>
<Text style={styles.finalScoreText}>Your Score: {score}</Text>
<TouchableOpacity style={styles.restartButton} onPress={resetGame}>
<Text style={styles.restartButtonText}>Play Again</Text>
</TouchableOpacity>
</View>
)}
</ThemedBackground>
);
};
export default Lab1;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/screens/lab2.js
File: lab2.js
// Lab2.js
import React, { useState, useEffect, useCallback, useMemo, useReducer, createContext, useContext } from 'react';
import {
View,
StyleSheet,
TouchableOpacity,
Image,
ScrollView,
ActivityIndicator,
Alert,
Text,
Dimensions,
} from 'react-native';
import WebView from 'react-native-webview';
import { ThemeContext } from '../ThemeContext';
const { width, height } = Dimensions.get('window');
const GlobalStateContext = createContext();
const initialState = {
data: null,
loading: false,
error: null,
};
const dataReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return { ...state, loading: true, error: null };
case 'FETCH_SUCCESS':
return { data: action.payload, loading: false, error: null };
case 'FETCH_FAILURE':
return { ...state, loading: false, error: action.payload };
default:
throw new Error();
}
};
const useAPOD = (date) => {
const [state, dispatch] = useReducer(dataReducer, initialState);
const fetchData = useCallback(async () => {
dispatch({ type: 'FETCH_INIT' });
try {
const response = await fetch(
`https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=${date}`
);
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.statusText}`);
}
const result = await response.json();
dispatch({ type: 'FETCH_SUCCESS', payload: result });
} catch (error) {
dispatch({ type: 'FETCH_FAILURE', payload: error.message });
}
}, [date]);
useEffect(() => {
fetchData();
}, [fetchData]);
return state;
};
const VideoPlayer = ({ url }) => {
const getYoutubeVideoId = (url) => {
const regExp =
/^.*(youtu.be\/|v\/|u\/w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
const match = url.match(regExp);
return match && match[2].length === 11 ? match[2] : null;
};
const videoId = getYoutubeVideoId(url);
const embedUrl = videoId
? `https://www.youtube.com/embed/${videoId}`
: url;
return (
<View style={styles.videoWrapper}>
<WebView
source={{ uri: embedUrl }}
style={styles.video}
allowsFullscreenVideo={true}
mediaPlaybackRequiresUserAction={false}
/>
</View>
);
};
const Lab2 = ({ navigation }) => {
const [date, setDate] = useState(() => {
return new Date().toISOString().slice(0, 10);
});
const { data, loading, error } = useAPOD(date);
const { globalData, setGlobalData } = useContext(GlobalStateContext);
const { colors } = useContext(ThemeContext);
const loadRandomAPOD = useCallback(() => {
const randomDate = new Date(
Date.now() - Math.floor(Math.random() * 1000 * 60 * 60 * 24 * 365)
)
.toISOString()
.slice(0, 10);
setDate(randomDate);
}, []);
const memoizedData = useMemo(() => data, [data]);
const updateGlobalData = useCallback(() => {
if (memoizedData) {
setGlobalData(memoizedData);
Alert.alert('Успешно', 'Изображение сохранено в избранное!');
}
}, [memoizedData, setGlobalData]);
const styles = StyleSheet.create({
container: {
flex: 1,
width,
height,
resizeMode: 'cover',
backgroundColor: colors.background,
},
header: {
marginTop: 40,
alignItems: 'flex-end',
marginRight: 20,
},
button: {
padding: 15,
borderRadius: 8,
borderWidth: 1.5,
backgroundColor: colors.accent,
},
buttonText: {
fontSize: 16,
color: colors.text,
},
content: {
alignItems: 'center',
padding: 20,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
loadingText: {
fontSize: 18,
color: colors.text,
},
errorContainer: {
alignItems: 'center',
marginTop: 20,
},
errorText: {
fontSize: 18,
color: 'red',
},
retryButton: {
marginTop: 10,
padding: 10,
borderRadius: 8,
backgroundColor: colors.accent,
},
retryButtonText: {
fontSize: 16,
color: colors.text,
},
dataContainer: {
alignItems: 'center',
},
titleText: {
fontSize: 22,
marginBottom: 10,
fontWeight: 'bold',
color: colors.text,
},
image: {
width: width - 40,
height: 300,
borderRadius: 10,
},
videoWrapper: {
width: width - 40,
height: 300,
borderRadius: 10,
overflow: 'hidden',
},
video: {
width: '100%',
height: '100%',
},
dateText: {
fontSize: 16,
marginVertical: 10,
color: colors.text,
},
explanationText: {
fontSize: 14,
textAlign: 'center',
marginTop: 10,
color: colors.text,
},
globalButton: {
padding: 12,
borderRadius: 8,
marginTop: 15,
backgroundColor: colors.accent,
},
globalButtonText: {
fontSize: 16,
color: colors.text,
},
savedDataContainer: {
marginTop: 30,
padding: 20,
borderRadius: 10,
backgroundColor: colors.secondary,
},
savedDataTitle: {
fontSize: 18,
color: colors.text,
},
savedDataText: {
fontSize: 14,
color: colors.text,
},
viewSavedButton: {
marginTop: 10,
padding: 10,
borderRadius: 8,
backgroundColor: colors.accent,
},
viewSavedButtonText: {
fontSize: 16,
color: colors.text,
},
videoContainer: {
width: width - 40,
height: 200,
justifyContent: 'center',
alignItems: 'center',
},
videoText: {
fontSize: 16,
textAlign: 'center',
color: colors.text,
},
});
return (
<View style={styles.container}>
<View style={styles.header}>
<TouchableOpacity
style={styles.button}
onPress={loadRandomAPOD}
>
<Text style={styles.buttonText}>
Загрузить другое изображение
</Text>
</TouchableOpacity>
</View>
<ScrollView contentContainerStyle={styles.content}>
{loading ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={colors.accent} />
<Text style={styles.loadingText}>Загрузка данных...</Text>
</View>
) : error ? (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Ошибка: {error}</Text>
<TouchableOpacity
style={styles.retryButton}
onPress={loadRandomAPOD}
>
<Text style={styles.retryButtonText}>Повторить</Text>
</TouchableOpacity>
</View>
) : memoizedData ? (
<View style={styles.dataContainer}>
<Text style={styles.titleText}>
{memoizedData.title || 'Название недоступно'}
</Text>
{memoizedData.media_type === 'image' ? (
<Image
source={{ uri: memoizedData.url }}
style={styles.image}
resizeMode="contain"
/>
) : memoizedData.media_type === 'video' ? (
<VideoPlayer url={memoizedData.url} />
) : (
<View style={styles.videoContainer}>
<Text style={styles.videoText}>
Формат медиа не поддерживается
</Text>
</View>
)}
<Text style={styles.dateText}>
Дата: {memoizedData.date}
</Text>
<Text style={styles.explanationText}>
{memoizedData.explanation}
</Text>
<TouchableOpacity
style={styles.globalButton}
onPress={updateGlobalData}
>
<Text style={styles.globalButtonText}>
Сохранить в избранное
</Text>
</TouchableOpacity>
</View>
) : (
<Text style={styles.errorText}>
Данные не загружены
</Text>
)}
{globalData && (
<View style={styles.savedDataContainer}>
<Text style={styles.savedDataTitle}>
Сохраненное изображение:
</Text>
<Text style={styles.savedDataText}>
{globalData.title}
</Text>
<Text style={styles.savedDataText}>
Дата: {globalData.date}
</Text>
<TouchableOpacity
style={styles.viewSavedButton}
onPress={() =>
navigation.navigate('SavedImage', { data: globalData })
}
>
<Text style={styles.viewSavedButtonText}>
Просмотреть
</Text>
</TouchableOpacity>
</View>
)}
</ScrollView>
</View>
);
};
const GlobalStateProvider = ({ children }) => {
const [globalData, setGlobalData] = useState(null);
return (
<GlobalStateContext.Provider value={{ globalData, setGlobalData }}>
{children}
</GlobalStateContext.Provider>
);
};
const Lab2WithProvider = (props) => (
<GlobalStateProvider>
<Lab2 {...props} />
</GlobalStateProvider>
);
export default Lab2WithProvider;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/screens/lab3.js
File: lab3.js
// Lab3.js
import React, { useState, useMemo, useCallback, useContext } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
ImageBackground,
Animated,
Dimensions,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { ThemeContext } from '../ThemeContext';
const { width, height } = Dimensions.get('window');
const OPERATIONS = ['+', '-', '*', '/'];
const MAX_LEVEL = 5;
const MAX_MISTAKES = 3;
const Lab3 = () => {
const [level, setLevel] = useState(1);
const [score, setScore] = useState(0);
const [gameOver, setGameOver] = useState(false);
const [currentProblem, setCurrentProblem] = useState(null);
const [userAnswer, setUserAnswer] = useState('');
const [mistakes, setMistakes] = useState(0);
const [shakeAnimation] = useState(new Animated.Value(0));
const { colors } = useContext(ThemeContext);
const generateProblem = useCallback(level => {
const operation = OPERATIONS[Math.floor(Math.random() * OPERATIONS.length)];
let num1, num2;
switch (operation) {
case '+':
num1 = Math.floor(Math.random() * (20 * level)) + 1;
num2 = Math.floor(Math.random() * (20 * level)) + 1;
break;
case '-':
num1 = Math.floor(Math.random() * (20 * level)) + 1;
num2 = Math.floor(Math.random() * num1) + 1;
break;
case '*':
num1 = Math.floor(Math.random() * (10 * level)) + 1;
num2 = Math.floor(Math.random() * (10 * level)) + 1;
break;
case '/':
num2 = Math.floor(Math.random() * (10 * level)) + 1;
num1 = num2 * (Math.floor(Math.random() * (10 * level)) + 1);
break;
}
return { num1, num2, operation };
}, []);
const calculateAnswer = useCallback(problem => {
const { num1, num2, operation } = problem;
switch (operation) {
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
case '/':
return num1 / num2;
}
}, []);
const memoizedProblem = useMemo(() => {
if (!currentProblem) {
const newProblem = generateProblem(level);
setCurrentProblem(newProblem);
return newProblem;
}
return currentProblem;
}, [level, currentProblem, generateProblem]);
const memoizedAnswer = useMemo(() => {
return calculateAnswer(memoizedProblem);
}, [memoizedProblem, calculateAnswer]);
const checkAnswer = useCallback(() => {
const userAnswerNum = parseFloat(userAnswer);
if (Math.abs(userAnswerNum - memoizedAnswer) < 0.01) {
ReactNativeHapticFeedback.trigger('notificationSuccess');
setScore(score + level * 10);
if (level < MAX_LEVEL) {
setLevel(level + 1);
} else {
setGameOver(true);
}
setCurrentProblem(null);
} else {
ReactNativeHapticFeedback.trigger('notificationError');
setMistakes(mistakes + 1);
if (mistakes + 1 >= MAX_MISTAKES) {
setGameOver(true);
}
Animated.sequence([
Animated.timing(shakeAnimation, {
toValue: 10,
duration: 100,
useNativeDriver: true,
}),
Animated.timing(shakeAnimation, {
toValue: -10,
duration: 100,
useNativeDriver: true,
}),
Animated.timing(shakeAnimation, {
toValue: 10,
duration: 100,
useNativeDriver: true,
}),
Animated.timing(shakeAnimation, {
toValue: 0,
duration: 100,
useNativeDriver: true,
}),
]).start();
}
setUserAnswer('');
}, [
userAnswer,
memoizedAnswer,
level,
score,
mistakes,
shakeAnimation,
]);
const restartGame = useCallback(() => {
setLevel(1);
setScore(0);
setGameOver(false);
setCurrentProblem(null);
setUserAnswer('');
setMistakes(0);
}, []);
const renderKeypad = useCallback(() => {
const keys = [
['7', '8', '9'],
['4', '5', '6'],
['1', '2', '3'],
['.', '0', '⌫'],
];
return keys.map((row, rowIndex) => (
<View key={`row-${rowIndex}`} style={styles.keypadRow}>
{row.map(key => (
<TouchableOpacity
key={key}
style={styles.keypadButton}
onPress={() => {
ReactNativeHapticFeedback.trigger('selection');
if (key === '⌫') {
setUserAnswer(userAnswer.slice(0, -1));
} else if (userAnswer.length < 8) {
setUserAnswer(userAnswer + key);
}
}}
>
<Text style={styles.keypadButtonText}>{key}</Text>
</TouchableOpacity>
))}
</View>
));
}, [userAnswer, colors]);
const styles = StyleSheet.create({
container: {
flex: 1,
width,
height,
resizeMode: 'cover',
},
gradient: {
flex: 1,
padding: 20,
backgroundColor: colors.background,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 20,
},
levelText: {
fontSize: 24,
fontWeight: 'bold',
color: colors.primary,
},
scoreText: {
fontSize: 24,
fontWeight: 'bold',
color: colors.primary,
},
mistakesText: {
fontSize: 24,
fontWeight: 'bold',
color: colors.primary,
},
problemContainer: {
backgroundColor: 'rgba(0, 0, 0, 0.6)',
padding: 20,
borderRadius: 10,
marginBottom: 20,
alignItems: 'center',
},
problemText: {
fontSize: 32,
textAlign: 'center',
color: colors.text,
},
answerContainer: {
backgroundColor: 'rgba(255, 255, 255, 0.2)',
padding: 10,
borderRadius: 5,
marginBottom: 20,
minWidth: 150,
alignItems: 'center',
},
answerText: {
fontSize: 28,
color: colors.text,
},
keypadContainer: {
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
},
keypadRow: {
flexDirection: 'row',
justifyContent: 'center',
},
keypadButton: {
width: 70,
height: 70,
justifyContent: 'center',
alignItems: 'center',
margin: 5,
borderRadius: 10,
borderWidth: 1.5,
backgroundColor: colors.secondary,
borderColor: colors.primary,
},
keypadButtonText: {
fontSize: 24,
color: colors.primary,
},
submitButton: {
padding: 15,
borderRadius: 10,
alignSelf: 'center',
width: '80%',
alignItems: 'center',
backgroundColor: colors.accent,
},
submitButtonText: {
fontSize: 24,
color: colors.text,
},
gameOverContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
gameOverText: {
fontSize: 36,
marginBottom: 20,
textAlign: 'center',
color: colors.text,
},
finalScoreText: {
fontSize: 28,
marginBottom: 20,
color: colors.text,
},
restartButton: {
padding: 15,
borderRadius: 10,
backgroundColor: colors.accent,
},
restartButtonText: {
fontSize: 24,
color: colors.text,
},
});
if (gameOver) {
return (
<ImageBackground
source={
mistakes >= MAX_MISTAKES
? require('../assets/space_defeat.jpg')
: require('../assets/space_victory.jpg')
}
style={styles.container}
>
<LinearGradient
colors={[colors.background, colors.background]}
style={styles.gradient}
>
<View style={styles.gameOverContainer}>
<Text style={styles.gameOverText}>
{mistakes >= MAX_MISTAKES
? 'The Galaxy has been conquered!'
: 'The Galaxy has been saved!'}
</Text>
<Text style={styles.finalScoreText}>
Final Score: {score}
</Text>
<TouchableOpacity
style={styles.restartButton}
onPress={restartGame}
>
<Text style={styles.restartButtonText}>
Start New Mission
</Text>
</TouchableOpacity>
</View>
</LinearGradient>
</ImageBackground>
);
}
return (
<ImageBackground
source={require('../assets/space_background.jpg')}
style={styles.container}
>
<LinearGradient
colors={[colors.background, colors.background]}
style={styles.gradient}
>
<View style={styles.header}>
<Text style={styles.levelText}>Level: {level}</Text>
<Text style={styles.scoreText}>Score: {score}</Text>
<Text style={styles.mistakesText}>
Mistakes: {mistakes}/{MAX_MISTAKES}
</Text>
</View>
<View style={styles.problemContainer}>
<Animated.Text
style={[
styles.problemText,
{ transform: [{ translateX: shakeAnimation }] },
]}
>
{`${memoizedProblem.num1} ${memoizedProblem.operation} ${memoizedProblem.num2} = ?`}
</Animated.Text>
</View>
<View style={styles.answerContainer}>
<Text style={styles.answerText}>{userAnswer}</Text>
</View>
<View style={styles.keypadContainer}>{renderKeypad()}</View>
<TouchableOpacity
style={styles.submitButton}
onPress={checkAnswer}
>
<Text style={styles.submitButtonText}>
Destroy Invaders
</Text>
</TouchableOpacity>
</LinearGradient>
</ImageBackground>
);
};
export default Lab3;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/screens/lab4.js
File: lab4.js
// Lab4.js
import React, { useEffect, useContext } from 'react';
import { View, TouchableOpacity, StyleSheet, Switch, Text } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { incrementCounter, decrementCounter } from '../store/store';
import { ThemeContext } from '../ThemeContext';
const Lab4 = ({ navigation }) => {
const counter = useSelector(state => state.counter);
const dispatch = useDispatch();
const { theme, toggleTheme, colors } = useContext(ThemeContext);
useEffect(() => {
console.log('Counter updated:', counter);
}, [counter]);
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
backgroundColor: colors.background,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
color: colors.text,
},
text: {
fontSize: 16,
marginBottom: 16,
color: colors.text,
},
switch: {
marginBottom: 24,
},
counterContainer: {
alignItems: 'center',
marginBottom: 24,
},
counterText: {
fontSize: 20,
marginBottom: 16,
fontWeight: 'bold',
color: colors.text,
},
buttonContainer: {
flexDirection: 'row',
},
button: {
padding: 12,
marginHorizontal: 8,
borderRadius: 50,
width: 120,
alignItems: 'center',
backgroundColor: colors.accent,
},
buttonText: {
fontSize: 16,
fontWeight: 'bold',
color: colors.text,
},
navigationButtonsContainer: {
alignItems: 'center',
marginTop: 24,
},
navigationButton: {
padding: 12,
borderRadius: 24,
alignItems: 'center',
width: 200,
marginVertical: 8,
backgroundColor: colors.accent,
},
});
return (
<View style={styles.container}>
<Text style={styles.title}>Redux Theme Switcher</Text>
<Text style={styles.text}>
Current theme: {theme === 'light' ? 'Light' : 'Dark'}
</Text>
<Switch
value={theme === 'dark'}
onValueChange={toggleTheme}
thumbColor={colors.accent}
trackColor={{ false: colors.secondary, true: colors.primary }}
style={styles.switch}
/>
<View style={styles.counterContainer}>
<Text style={styles.counterText}>
Redux Counter: {counter}
</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.button}
onPress={() => dispatch(incrementCounter())}
>
<Text style={styles.buttonText}>Increment</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => dispatch(decrementCounter())}
>
<Text style={styles.buttonText}>Decrement</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.navigationButtonsContainer}>
<TouchableOpacity
style={styles.navigationButton}
onPress={() => navigation.navigate('Lab1')}
>
<Text style={styles.buttonText}>Go to Lab 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.navigationButton}
onPress={() => navigation.navigate('Lab2')}
>
<Text style={styles.buttonText}>Go to Lab 2</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.navigationButton}
onPress={() => navigation.navigate('Lab3')}
>
<Text style={styles.buttonText}>Go to Lab 3</Text>
</TouchableOpacity>
</View>
</View>
);
};
export default Lab4;
Файл не найден: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024\arsenNikiforovCLI/SavedImage.js
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/package.json
File: package.json
{
"name": "arsenNikiforovCLI",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest"
},
"dependencies": {
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.11.0",
"@react-navigation/stack": "^6.4.1",
"expo-haptics": "^13.0.1",
"expo-linear-gradient": "^13.0.2",
"react": "18.3.1",
"react-native": "0.75.4",
"react-native-gesture-handler": "^2.20.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-linear-gradient": "^2.8.3",
"react-native-safe-area-context": "^4.11.0",
"react-native-screens": "^3.34.0",
"react-native-svg": "^15.7.1",
"react-native-webview": "^13.12.3",
"react-redux": "^9.1.2",
"redux": "^5.0.1",
"zustand": "^5.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native-community/cli": "latest",
"@react-native/babel-preset": "0.75.4",
"@react-native/eslint-config": "0.75.4",
"@react-native/metro-config": "0.75.4",
"@react-native/typescript-config": "0.75.4",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.6.3",
"eslint": "^8.19.0",
"jest": "^29.6.3",
"prettier": "2.8.8",
"react-test-renderer": "18.3.1"
},
"engines": {
"node": ">=18"
}
}
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/index.js
File: index.js
import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';
import React from 'react';
import { Provider } from 'react-redux';
import store from './store/store';
const ReduxApp = () => (
<Provider store={store}>
<App />
</Provider>
);
AppRegistry.registerComponent(appName, () => ReduxApp);
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/components/ThemedBackground.js
File: ThemedBackground.js
// ThemedBackground.js
import React, { useContext } from 'react';
import { View, StyleSheet } from 'react-native';
import { ThemeContext } from '../ThemeContext';
const ThemedBackground = ({ children }) => {
const { colors } = useContext(ThemeContext);
return <View style={[styles.container, { backgroundColor: colors.background }]}>{children}</View>;
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default ThemedBackground;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/components/ThemedText.js
File: ThemedText.js
// ThemedText.js
import React, { useContext } from 'react';
import { Text } from 'react-native';
import { ThemeContext } from '../ThemeContext';
export const ThemedText = ({ style, ...props }) => {
const { colors } = useContext(ThemeContext);
return <Text style={[{ color: colors.text }, style]} {...props} />;
};
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/store/store.js
File: store.js
// store.js
import { createStore } from 'redux';
const initialState = {
counter: 0,
};
export const incrementCounter = () => ({
type: 'INCREMENT_COUNTER',
});
export const decrementCounter = () => ({
type: 'DECREMENT_COUNTER',
});
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT_COUNTER':
return { ...state, counter: state.counter + 1 };
case 'DECREMENT_COUNTER':
return { ...state, counter: state.counter - 1 };
default:
return state;
}
};
const store = createStore(reducer);
export default store;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/android/gradle.properties
File: gradle.properties
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/android/gradle/wrapper/gradle-wrapper.properties
File: gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI/app.json
File: app.json
{
"name": "arsenNikiforovCLI",
"displayName": "arsenNikiforovCLI"
}
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLIFile: Bubble.js
// Bubble.js
import React, { useEffect, useState, useContext } from 'react';
import { Animated, StyleSheet, Dimensions, PanResponder } from 'react-native';
import { ThemeContext } from '../ThemeContext';
const { width, height } = Dimensions.get('window');
const Bubble = ({ id, removeBubble, onDrag, gameOver }) => {
const [position] = useState(
new Animated.ValueXY({ x: Math.random() * (width - 80), y: height })
);
const [opacity] = useState(new Animated.Value(1));
const { colors } = useContext(ThemeContext);
const color = colors.accent;
useEffect(() => {
if (!gameOver) {
Animated.timing(position, {
toValue: { x: position.x._value, y: -100 },
duration: 5000,
useNativeDriver: true,
}).start(({ finished }) => {
if (finished) {
removeBubble(id);
}
});
} else {
Animated.timing(opacity, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
}
}, [gameOver, id, position, removeBubble, opacity]);
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
Animated.sequence([
Animated.timing(opacity, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}),
]).start(() => {
removeBubble(id);
onDrag();
});
},
});
return (
<Animated.View
{...panResponder.panHandlers}
style={[
styles.bubble,
{
backgroundColor: color,
transform: position.getTranslateTransform(),
opacity,
},
]}
/>
);
};
const styles = StyleSheet.create({
bubble: {
position: 'absolute',
width: 80,
height: 80,
borderRadius: 40,
},
});
export default Bubble;
Full path: C:\Users\Arsenshmid\Desktop\AndroidArsen\mobile-develop-2024rsenNikiforovCLI\ThemeContext.js
File: ThemeContext.js
import React, { createContext, useState } from 'react';
export const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('dark'); // Темная тема по умолчанию
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'dark' ? 'light' : 'dark'));
};
const themeColors = {
dark: {
background: '#36393F',
text: '#FFFFFF',
primary: '#7289DA',
secondary: '#2F3136',
accent: '#5865F2',
},
light: {
background: '#FFFFFF',
text: '#000000',
primary: '#7289DA',
secondary: '#F2F3F5',
accent: '#5865F2',
},
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme, colors: themeColors[theme] }}>
{children}
</ThemeContext.Provider>
);
};
После этого скажи самый простой способ сделать макет всех экранов приложения на фигме