initial migration commit into shiloh org
This commit is contained in:
parent
6d49fe892c
commit
99c84b9572
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.env
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
.vscode/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.wav
|
||||||
|
*.mp3
|
||||||
|
*.aac
|
||||||
|
*.opus
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
*.jpeg
|
||||||
|
*.zip
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Selah
|
||||||
|
|
||||||
|
Music audio streaming platform for independent content creators: music artists, podcast hosts, pastors, teachers, audiobook readers, etc.
|
||||||
|
|
||||||
|
Break away from the world's systems. Support and be apart of a streaming platform made by Christians, for Christians, for the Glory of the Lord Jesus Christ.
|
162
backend/README.md
Normal file
162
backend/README.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Selah Django backend API
|
||||||
|
|
||||||
|
|
||||||
|
*** DJANGO ENVIRONMENT LOCAL SETUP (installing libraries/dependencies) ***
|
||||||
|
|
||||||
|
|
||||||
|
- install ffmpeg for audio file processing on your system:
|
||||||
|
sudo apt-get install net-tools
|
||||||
|
sudo apt install ffmpeg
|
||||||
|
|
||||||
|
|
||||||
|
- install postgres
|
||||||
|
sudo apt install libpq-dev
|
||||||
|
sudo apt install postgresql
|
||||||
|
sudo apt install postgresql-contrib
|
||||||
|
|
||||||
|
- make sure python is installed (should come with ubuntu):
|
||||||
|
python (or python3) --version
|
||||||
|
Django setup (Using Python -v 3.10.6 and Django -v 4.2 as of April, 29, 2023):
|
||||||
|
|
||||||
|
- make dir on local machine where this project will be located
|
||||||
|
|
||||||
|
- clone this repo from github (ssh or https, see top level servers_ssl_db_setup digitalocean_server.txt)
|
||||||
|
|
||||||
|
- install python venv:
|
||||||
|
sudo apt install python3.10-venv
|
||||||
|
|
||||||
|
- Create python virtual environment on local machine (shouldn't need to do this step if cloning from git, just install venv and try to activate the env in next step):
|
||||||
|
|
||||||
|
python3 -m venv env
|
||||||
|
|
||||||
|
- Activate python virtual environment on local machine:
|
||||||
|
|
||||||
|
. env/bin/activate
|
||||||
|
(you'll see '(env)' on the left-most side the terminal signature when activated)
|
||||||
|
|
||||||
|
- Once your env is activated, install Django and other dependencies (see requirements.txt):
|
||||||
|
|
||||||
|
pip install django (the backend framework)
|
||||||
|
pip install django-rest-framework (creating the backend API, creates djangorestframework dir as well)
|
||||||
|
pip install djangorestframework-simplejwt (Simple JWT provides a JSON Web Token authentication backend for the Django REST Framework. Updated framework from djangorestframework-jwt which is now deprecated)
|
||||||
|
pip install pyjwt (Python library which allows you to encode and decode JSON Web Tokens JWT)
|
||||||
|
pip install social-auth-app-django (for 3rd party auth Facebook, Google, LinkedIn, etc.)
|
||||||
|
pip install django-cors-headers (provides security between backend and API)
|
||||||
|
pip install djoser (assists with user auth)
|
||||||
|
pip install pillow (image processing)
|
||||||
|
pip install django-imagekit (django image processing for track image)
|
||||||
|
pip install pydub (audio file processing)
|
||||||
|
pip install django-environ (for environment variables)
|
||||||
|
pip install stripe (payment processor for handling secure payments)
|
||||||
|
pip3 install --upgrade stripe (upgrade stripe)
|
||||||
|
pip install psycopg2 (database adapter for PostgreSQL DB. Use this for local development, but in the production server, install the binary:
|
||||||
|
pip install psycopg2-binary
|
||||||
|
)
|
||||||
|
install pgadmin4 desktop tool for DB management (https://www.pgadmin.org/download/)
|
||||||
|
pip install gunicorn
|
||||||
|
|
||||||
|
*** Note: You need to create empty DB in either psql shell or pgadmin tool before running python manage.py makemigrations and python manage.py migrate (If you want to use custom user model, DO NOT RUN python manage.py makemigrations/migrate until you have created your custom user model, otherwise Django will revert to its default User Model. Changing from Django's default user model to a custom user model is possible, but unsupported and prone to many errors. Do this first before anything else if that's what you want for your app)
|
||||||
|
|
||||||
|
- activate env
|
||||||
|
(linux) . env/bin.activate
|
||||||
|
- create new Django project (make sure you are in dir you want project to be created):
|
||||||
|
django-admin startproject yourprojectname
|
||||||
|
- start Django dev server:
|
||||||
|
|
||||||
|
set backend env DEV_MODE var:
|
||||||
|
DEV_MODE=True
|
||||||
|
|
||||||
|
|
||||||
|
- start Django dev server in terminal:
|
||||||
|
DJANGO_SETTINGS_MODULE=sheriff_crandy_project.dev_settings python manage.py runserver
|
||||||
|
|
||||||
|
|
||||||
|
- you should be able to access backend at
|
||||||
|
127.0.0.1:8000
|
||||||
|
|
||||||
|
- you can now create apps (for models --users, tracks, artists, etc; make sure you are in project root directory when creating apps. After creating an app, make sure you register it by adding it to the main project's settings.py file INSTALLED_APPS dict)
|
||||||
|
|
||||||
|
python manage.py startapp my_app_name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To run vs code debugger:
|
||||||
|
In top level directory, add this launch.json file:
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python: Django",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "/your/path/to/manage.py",
|
||||||
|
"args": [
|
||||||
|
"runserver"
|
||||||
|
],
|
||||||
|
"django": true,
|
||||||
|
"env": {
|
||||||
|
"DJANGO_SETTINGS_MODULE": "main_project.dev_settings"
|
||||||
|
},
|
||||||
|
"justMyCode": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In main_project folder, create .env file
|
||||||
|
add these env vars:
|
||||||
|
|
||||||
|
DOMAIN=https://sheriffcrandymusic.local:9443/api
|
||||||
|
DEV_DOMAIN=http://127.0.0.1:8000
|
||||||
|
DEV_MODE=True
|
||||||
|
|
||||||
|
Should be it. Activate your python env:
|
||||||
|
. env/bin/activate
|
||||||
|
And start Django's dev server with the dev settings:
|
||||||
|
|
||||||
|
start dev server using dev mode settings:
|
||||||
|
DJANGO_SETTINGS_MODULE=sheriff_crandy_project.dev_settings python manage.py runserver
|
||||||
|
|
||||||
|
|
||||||
|
Now go to an API endpoint and verify the dev site is being served:
|
||||||
|
http://127.0.0.1:8000/api/sc/v1/tracks/
|
||||||
|
|
||||||
|
|
||||||
|
If you were preveiously in production mode, stop all nginx and gunicorn servers and open
|
||||||
|
your browser's dev tools and clear all cache and cookie data:
|
||||||
|
|
||||||
|
sudo systemctl stop nginx
|
||||||
|
sudo systemctl stop gunicorn
|
||||||
|
sudo systemctl stop gunicorn.socket
|
||||||
|
|
||||||
|
|
||||||
|
*** PRODUCTION SIMULATION SERVER (https self-signed SSL certs) ***
|
||||||
|
|
||||||
|
TO START SIMULATED PROD SERVER
|
||||||
|
access site over https with self-signed ssl certs
|
||||||
|
(see servers_ssl_db_setup folder and complete those steps first if you haven't set up the servers locally in your machine yet):
|
||||||
|
set backend env DEV_MODE var:
|
||||||
|
DEV_MODE=False
|
||||||
|
|
||||||
|
|
||||||
|
restart nginx and gunicorn
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
sudo systemctl restart gunicorn.socket
|
||||||
|
sudo systemctl restart gunicorn
|
||||||
|
|
||||||
|
access the backend API/admin page at:
|
||||||
|
|
||||||
|
whatever you named your hosts name in nginx/gnicorn ssl setup
|
||||||
|
|
||||||
|
|
||||||
|
After everything is working, you can begin developing and making changes to code.
|
||||||
|
After you have made a code change, restart servers to see updates:
|
||||||
|
|
||||||
|
reset nginx and gunicorn
|
||||||
|
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
sudo systemctl restart gunicorn
|
0
backend/main_project/__init__.py
Normal file
0
backend/main_project/__init__.py
Normal file
7
backend/main_project/asgi.py
Normal file
7
backend/main_project/asgi.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main_project.settings')
|
||||||
|
|
||||||
|
application = get_asgi_application()
|
27
backend/main_project/custom_user_notes.txt
Normal file
27
backend/main_project/custom_user_notes.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
***
|
||||||
|
|
||||||
|
If you want to use custom user model,
|
||||||
|
DO NOT RUN python manage.py makemigrations/migrate until
|
||||||
|
you have created your custom user model,
|
||||||
|
otherwise Django will revert to its default User Model.
|
||||||
|
Changing from Django's default user model to a
|
||||||
|
custom user model is possible, but unsupported
|
||||||
|
and prone to many errors. Do this first before anything
|
||||||
|
else if that's what you want for your app
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
in root dir:
|
||||||
|
|
||||||
|
- activate env
|
||||||
|
(linux) . env/bin/activate
|
||||||
|
- create your user app
|
||||||
|
python manage.py startapp your_custom_user_app_name
|
||||||
|
- go to your main project's settings.py file and in the INSTALLED_APPS dictionary add your user app:
|
||||||
|
'your_custom_user_app_name'
|
||||||
|
see src for model/view/urls setup. Modify as desired.
|
||||||
|
|
||||||
|
- When you are satisfied with your user, run:
|
||||||
|
python manage.py makemigrations
|
||||||
|
- and then:
|
||||||
|
python manage.py migrate
|
49
backend/main_project/dev_settings.py
Normal file
49
backend/main_project/dev_settings.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# same settings as production but with debug to true and ssl redirects off
|
||||||
|
# useful for running vs code or python debugger
|
||||||
|
# needed to run local Django dev server over http
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
TO START DEV SERVER
|
||||||
|
|
||||||
|
run dev server to access site over http:
|
||||||
|
set backend env DEV_MODE var:
|
||||||
|
DEV_MODE=True
|
||||||
|
|
||||||
|
(if you haven't set up these servers yet, you can ignore the rest of the steps and just run:
|
||||||
|
|
||||||
|
DJANGO_SETTINGS_MODULE=main_project.dev_settings python manage.py runserver
|
||||||
|
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
stop nginx and gunicorn
|
||||||
|
sudo systemctl stop nginx
|
||||||
|
sudo systemctl stop gunicorn
|
||||||
|
sudo systemctl stop gunicorn.socket
|
||||||
|
|
||||||
|
in your browser, open dev tools
|
||||||
|
clear cache and cookie data in dev tools console (otherwise any previous http redirects will still be in affect)
|
||||||
|
|
||||||
|
start dev server:
|
||||||
|
DJANGO_SETTINGS_MODULE=main_project.dev_settings python manage.py runserver
|
||||||
|
|
||||||
|
|
||||||
|
make sure you can access the frontend at
|
||||||
|
127.0.0.1:8080
|
||||||
|
and the backend at
|
||||||
|
127.0.0.1:8000/api/
|
||||||
|
'''
|
||||||
|
|
||||||
|
from .settings import *
|
||||||
|
|
||||||
|
# for development
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
SECURE_SSL_REDIRECT=False
|
||||||
|
SESSION_COOKIE_SECURE=False
|
||||||
|
CSRF_COOKIE_SECURE=False
|
||||||
|
SECURE_REDIRECT_EXEMPT = ['^']
|
||||||
|
|
||||||
|
print('\nDEV MODE: ' + str(env("DEV_MODE")) + '\n')
|
226
backend/main_project/settings.py
Normal file
226
backend/main_project/settings.py
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
# global settings and live production settings
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
import environ
|
||||||
|
env = environ.Env()
|
||||||
|
environ.Env.read_env()
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
# set to False for production
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
# SECURE_SSL_REDIRECT=True
|
||||||
|
# SESSION_COOKIE_SECURE=True
|
||||||
|
# CSRF_COOKIE_SECURE=True
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = env('SECRET_KEY')
|
||||||
|
|
||||||
|
'''
|
||||||
|
TODO: set these when testing payments
|
||||||
|
|
||||||
|
# get stripe endpoint's webhook secret by running `stripe listen` in CLI
|
||||||
|
# STRIPE_WEBHOOK_SECRET = env("STRIPE_WEBHOOK_SECRET")
|
||||||
|
|
||||||
|
# stripe data
|
||||||
|
# STRIPE_PK = env("STRIPEPK")
|
||||||
|
# STRIPE_SK = env("STRIPESK")
|
||||||
|
# STRIPE_DOMAIN = env("STRIPE_DOMAIN")
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# add server ips
|
||||||
|
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
|
||||||
|
|
||||||
|
|
||||||
|
# rest framework global settings
|
||||||
|
# authentication classes: https://www.django-rest-framework.org/api-guide/settings/#default_authentication_classes
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
|
),
|
||||||
|
# permission policy: https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy
|
||||||
|
# can override these in views. Don't forget to add a comma
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': (
|
||||||
|
# allow anyone to access api data
|
||||||
|
# 'rest_framework.permissions.AllowAny',
|
||||||
|
# do not allow anyone to access API endpoints unless user is authenticated
|
||||||
|
# 'rest_framework.permissions.IsAuthenticated',
|
||||||
|
# allow full access to authenticated users, but allow read-only access to unauthenticated users
|
||||||
|
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
|
||||||
|
),
|
||||||
|
'DEFAULT_RENDERER_CLASSES': (
|
||||||
|
'rest_framework.renderers.JSONRenderer', # makes api views JSON data only
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
# DRF dependencies
|
||||||
|
'rest_framework',
|
||||||
|
'rest_framework.authtoken',
|
||||||
|
'corsheaders',
|
||||||
|
# djoser is a REST implementation of Django authentication system. Provides token based authentication
|
||||||
|
'djoser',
|
||||||
|
# Django image processing
|
||||||
|
'imagekit',
|
||||||
|
# custom user model
|
||||||
|
'users_app',
|
||||||
|
]
|
||||||
|
|
||||||
|
# set custom user model (appname.model name) to prevent Django from using
|
||||||
|
# its default model
|
||||||
|
AUTH_USER_MODEL = 'users_app.Users'
|
||||||
|
|
||||||
|
|
||||||
|
# add custom backend, if it fails, Django will use default backend
|
||||||
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
'users_app.custom_backend.Custom_Backend',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'corsheaders.middleware.CorsMiddleware', # this has to go above CommonMiddleware
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# change to whatever hostname react-native runs on
|
||||||
|
# CORS_ALLOWED_ORIGINS = [
|
||||||
|
# "http://127.0.0.1:8080",
|
||||||
|
# "http://localhost:8080",
|
||||||
|
# "https://sheriffcrandymusic.local:9443",
|
||||||
|
# "https://sheriffcrandymusic.com"
|
||||||
|
# ]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'main_project.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [os.path.join(BASE_DIR,'templates')],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'main_project.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||||
|
|
||||||
|
# PostgreSQL config
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
# postgresql
|
||||||
|
'ENGINE': env('DBENGINE'),
|
||||||
|
# name of database
|
||||||
|
'NAME': env('DBNAME'),
|
||||||
|
# owner of database
|
||||||
|
'USER': env('DBUSER'),
|
||||||
|
'PASSWORD': env('DBPASSWORD'),
|
||||||
|
# specify which machine where db is installed
|
||||||
|
# connect through TCP sockets,
|
||||||
|
'HOST': env('DBHOST')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation. Turning this off, handling custom password validation manually
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||||
|
'''
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
'''
|
||||||
|
# allow python to send emails
|
||||||
|
# Simple Mail Transfer Protocol SMPT config
|
||||||
|
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||||
|
EMAIL_HOST = env('EMAIL_HOST')
|
||||||
|
EMAIL_PORT = env('EMAIL_PORT')
|
||||||
|
EMAIL_USE_TLS = True
|
||||||
|
# email address that sends emails
|
||||||
|
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
|
||||||
|
# EMAIL_HOST_PASSWORD generated by Google. First you have to enable 2-step verification
|
||||||
|
# Go to Google-> manage Google account -> Security -> Sigining in to Google -> App passwords ->
|
||||||
|
# if you can't find app passwords. Search for it in the searchbar above
|
||||||
|
# click select app and choose Mail or Other -> select device name (make a custom name) -> generate -> get password and put it in .env file
|
||||||
|
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
|
||||||
|
|
||||||
|
EMAIL_ACTIVE_FIELD = 'is_active'
|
||||||
|
'''
|
||||||
|
|
||||||
|
# URL to use when referring to static files located in STATIC_ROOT.
|
||||||
|
# manually add this folder to the parent (backend) dir
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
os.path.join(BASE_DIR, 'static'),
|
||||||
|
]
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR,'staticfiles')
|
||||||
|
# media dir for files. This dir is created when the first model objects are created (either through admin page or otherwise)
|
||||||
|
MEDIA_URL = '/media/'
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
|
||||||
|
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
# receive error details of exceptions raised in the request/response cycle.
|
||||||
|
# https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-ADMINS
|
||||||
|
# ADMINS = [('name', 'email')]
|
||||||
|
|
||||||
|
# managers get broken link notifications
|
||||||
|
# MANAGERS = [('name', 'email')]
|
18
backend/main_project/urls.py
Normal file
18
backend/main_project/urls.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path, include
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls.static import static
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('api/admin/', admin.site.urls),
|
||||||
|
# djoser library is a REST implementation of Django authentication system
|
||||||
|
# url of API
|
||||||
|
path('api/selah/', include('djoser.urls')),
|
||||||
|
path('api/selah/', include('djoser.urls.authtoken')),
|
||||||
|
# include app urls --can check api endpoints at http://localhost:8000/api/selah/app-specific-urls
|
||||||
|
path('api/selah/', include ('users_app.urls')),
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
16
backend/main_project/wsgi.py
Normal file
16
backend/main_project/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
WSGI config for main_project project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main_project.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
22
backend/manage.py
Executable file
22
backend/manage.py
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main_project.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
0
backend/users_app/__init__.py
Normal file
0
backend/users_app/__init__.py
Normal file
13
backend/users_app/admin.py
Normal file
13
backend/users_app/admin.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import Users
|
||||||
|
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
@admin.register(Users)
|
||||||
|
class CustomUserAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
# put all other fields you want to be shown in listing
|
||||||
|
'username',
|
||||||
|
)
|
||||||
|
readonly_fields = ['id','date_user_added']
|
8
backend/users_app/apps.py
Normal file
8
backend/users_app/apps.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UsersAppConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'users_app'
|
||||||
|
# for admin page
|
||||||
|
verbose_name = 'Users App'
|
41
backend/users_app/custom_backend.py
Normal file
41
backend/users_app/custom_backend.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# custom auth backend to allow user to login with either username or email address
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
|
user_obj = get_user_model()
|
||||||
|
print("\nUSER: " + (str(user_obj)) +'\n')
|
||||||
|
|
||||||
|
# called when making api attempts to authenticate users and create auth tokens
|
||||||
|
class Custom_Backend(object):
|
||||||
|
|
||||||
|
def authenticate(self, request, username=None, password=None, **kwargs):
|
||||||
|
try:
|
||||||
|
# Try to fetch the user by searching the username or email field
|
||||||
|
user = user_obj.objects.get(Q(username=username)|Q(email=username))
|
||||||
|
print("\nUSER: " + (str(user)) +'\n')
|
||||||
|
# if password was correct, authenticate using token auth
|
||||||
|
if user.check_password(password):
|
||||||
|
|
||||||
|
# create token for this user:
|
||||||
|
# first try to get this token (if the user's token was deleted somehow)
|
||||||
|
try:
|
||||||
|
print("\nttempting to get token for user\n")
|
||||||
|
token = Token.objects.get(user=user)
|
||||||
|
except:
|
||||||
|
print("\nToken doesn't exist for user, creating one\n")
|
||||||
|
token = Token.objects.create(user=user)
|
||||||
|
|
||||||
|
|
||||||
|
return user
|
||||||
|
except user_obj.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# called when saving a new model obj
|
||||||
|
def get_user(self, user_id):
|
||||||
|
|
||||||
|
try:
|
||||||
|
return user_obj.objects.get(pk=user_id)
|
||||||
|
except user_obj.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
38
backend/users_app/migrations/0001_initial.py
Normal file
38
backend/users_app/migrations/0001_initial.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-06-14 10:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Users',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('email', models.EmailField(db_index=True, max_length=100, unique=True)),
|
||||||
|
('username', models.CharField(db_index=True, max_length=64, unique=True)),
|
||||||
|
('user_first_name', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('user_last_name', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('user_profile_pic', models.ImageField(blank=True, null=True, upload_to='user_pfps')),
|
||||||
|
('user_favorite_bible_verse', models.CharField(blank=True, max_length=20, null=True)),
|
||||||
|
('date_user_added', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('is_staff', models.BooleanField(default=False)),
|
||||||
|
('is_active', models.BooleanField(default=True)),
|
||||||
|
('is_superuser', models.BooleanField(default=False)),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set_custom', related_query_name='user_custom', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set_custom', related_query_name='user_custom', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'users',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
0
backend/users_app/migrations/__init__.py
Normal file
0
backend/users_app/migrations/__init__.py
Normal file
151
backend/users_app/models.py
Normal file
151
backend/users_app/models.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
# custom user model
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
# import classes needed for custom user model
|
||||||
|
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
|
||||||
|
from main_project import settings
|
||||||
|
|
||||||
|
|
||||||
|
# set up logger
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# create file handler and set level to INFO
|
||||||
|
log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_logs.txt')
|
||||||
|
fh = logging.FileHandler(log_file)
|
||||||
|
fh.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# create formatter
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
fh.setFormatter(formatter)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
|
|
||||||
|
# custom user manager
|
||||||
|
class Users_CustomUserManager(BaseUserManager):
|
||||||
|
|
||||||
|
|
||||||
|
# called when creating user through drf terminal and frontend
|
||||||
|
# only pass in req params, all other unrequired params like
|
||||||
|
# first/lastname, favorite color, etc. will be held in **extra_fields param
|
||||||
|
def create_user(self, email, username, password, **extra_fields):
|
||||||
|
|
||||||
|
if not email:
|
||||||
|
raise ValueError("Email must be provided")
|
||||||
|
if not username:
|
||||||
|
raise ValueError("Username must be provided")
|
||||||
|
|
||||||
|
user = Users(
|
||||||
|
email = self.normalize_email(email),
|
||||||
|
username = username,
|
||||||
|
**extra_fields
|
||||||
|
)
|
||||||
|
|
||||||
|
user.set_password(password)
|
||||||
|
user.save(using=self._db)
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
# called when creating superuser through terminal drf
|
||||||
|
def create_superuser(self, email, username, password, **extra_fields):
|
||||||
|
|
||||||
|
extra_fields.setdefault('is_staff',True)
|
||||||
|
extra_fields.setdefault('is_superuser',True)
|
||||||
|
if extra_fields.get('is_staff') is not True:
|
||||||
|
raise ValueError('Superuser must have is_staff=True.')
|
||||||
|
if extra_fields.get('is_superuser') is not True:
|
||||||
|
raise ValueError('Superuser must have is_superuser=True.')
|
||||||
|
return self.create_user(email, username, password, **extra_fields)
|
||||||
|
|
||||||
|
|
||||||
|
# Custom User Model
|
||||||
|
class Users(AbstractBaseUser, PermissionsMixin):
|
||||||
|
|
||||||
|
# ; define fields you want to be included in User Model, can be updated and changed later
|
||||||
|
# this is the power of implementing a custom user model
|
||||||
|
# allow authentication by username or email, or whatever else!
|
||||||
|
email = models.EmailField(db_index=True, max_length=100, unique=True)
|
||||||
|
username = models.CharField(db_index=True, max_length=64, unique=True)
|
||||||
|
|
||||||
|
# non-req'd fields
|
||||||
|
user_first_name = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
user_last_name = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
# will be uploaded to /media/ directory
|
||||||
|
user_profile_pic = models.ImageField(upload_to='user_pfps', blank=True, null=True)
|
||||||
|
# just for fun
|
||||||
|
user_favorite_bible_verse = models.CharField(max_length=20, blank=True, null=True)
|
||||||
|
date_user_added = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
# if user is allowed access to admin site.
|
||||||
|
is_staff = models.BooleanField(default=False)
|
||||||
|
# if user is currently active
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
is_superuser = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
# connect user class to manager class
|
||||||
|
objects = Users_CustomUserManager()
|
||||||
|
|
||||||
|
# choose wisely, password resets will use this field
|
||||||
|
# needs to have unique=True
|
||||||
|
USERNAME_FIELD = 'username'
|
||||||
|
# used by python manage.py createsuperuser and presumably drf for token authentication
|
||||||
|
# should not contain the USERNAME_FIELD or password as these fields will always be prompted for.
|
||||||
|
REQUIRED_FIELDS = ['user_first_name', 'user_last_name', 'email']
|
||||||
|
|
||||||
|
# this custom user model's name was clashing with Django's default user names, so adding these two
|
||||||
|
# fields for groups and user_permissions is required
|
||||||
|
groups = models.ManyToManyField(
|
||||||
|
'auth.Group',
|
||||||
|
related_name='user_set_custom',
|
||||||
|
related_query_name='user_custom',
|
||||||
|
blank=True,
|
||||||
|
help_text='The groups this user belongs to. A user will get all permissions '
|
||||||
|
'granted to each of their groups.',
|
||||||
|
verbose_name='groups',
|
||||||
|
)
|
||||||
|
user_permissions = models.ManyToManyField(
|
||||||
|
'auth.Permission',
|
||||||
|
related_name='user_set_custom',
|
||||||
|
related_query_name='user_custom',
|
||||||
|
blank=True,
|
||||||
|
help_text='Specific permissions for this user.',
|
||||||
|
verbose_name='user permissions',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'users'
|
||||||
|
# for admin page
|
||||||
|
verbose_name_plural = "Users"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# return self.email
|
||||||
|
return self.username
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def has_perm(perm, obj=None, **kwargs):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def has_module_perms(app_label, **kwargs):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# get user profile pic
|
||||||
|
def get_profile_pic(self):
|
||||||
|
if self.user_profile_pic:
|
||||||
|
if settings.env('DEV_MODE') == 'True':
|
||||||
|
return settings.env('DEV_DOMAIN') + self.user_profile_pic.url
|
||||||
|
else:
|
||||||
|
return settings.env('DOMAIN') + self.user_profile_pic.url
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
# create user obj for performing user actions
|
||||||
|
# get_user_model defined in settings.py
|
||||||
|
# has to be defined below since user model hasn't been installed/loaded yet
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
user_obj = get_user_model()
|
80
backend/users_app/serializers.py
Normal file
80
backend/users_app/serializers.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# manually created serializers.py file created to turn DB data into JSON to be used by frontend
|
||||||
|
from rest_framework import serializers
|
||||||
|
from .models import *
|
||||||
|
# import Users
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
# return authenticated user as well as user's cart data
|
||||||
|
user = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# creating new users
|
||||||
|
class CustomUserCreateSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Users
|
||||||
|
fields = ('email',
|
||||||
|
'username',
|
||||||
|
'password',
|
||||||
|
'user_first_name',
|
||||||
|
'user_last_name',
|
||||||
|
'user_profile_pic',
|
||||||
|
'user_favorite_bible_verse')
|
||||||
|
extra_kwargs = {'password': {'write_only': True}}
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
profile_pic = validated_data.pop('user_profile_pic', None)
|
||||||
|
current_user = self.Meta.model(**validated_data)
|
||||||
|
current_user.user_profile_pic = profile_pic
|
||||||
|
current_user.save() # Save user instance after adding profile_pic
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# for updating user account info
|
||||||
|
class UpdateUserAccountDataSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField(required=False)
|
||||||
|
email = serializers.EmailField(required=False)
|
||||||
|
user_first_name = serializers.CharField(required=False)
|
||||||
|
user_last_name = serializers.CharField(required=False)
|
||||||
|
user_favorite_bible_verse = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
# automatically called during validation process
|
||||||
|
def validate_username(self, value):
|
||||||
|
request = self.context.get('request')
|
||||||
|
current_user = request.user
|
||||||
|
if user.objects.filter(username=value).exists() and current_user.username != value:
|
||||||
|
raise serializers.ValidationError('Username already exists.')
|
||||||
|
return value
|
||||||
|
|
||||||
|
def validate_email(self, value):
|
||||||
|
request = self.context.get('request')
|
||||||
|
current_user = request.user
|
||||||
|
if user.objects.filter(email=value).exists() and current_user.email != value:
|
||||||
|
raise serializers.ValidationError('Email already exists.')
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# for displaying account info to user
|
||||||
|
class GetUserSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Users
|
||||||
|
fields = (
|
||||||
|
"id",
|
||||||
|
"email",
|
||||||
|
"username",
|
||||||
|
"user_first_name",
|
||||||
|
"user_last_name",
|
||||||
|
"user_favorite_bible_verse",
|
||||||
|
"get_profile_pic"
|
||||||
|
)
|
||||||
|
|
||||||
|
# get user's pfp
|
||||||
|
class GetUserPfpSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Users
|
||||||
|
fields = (
|
||||||
|
"get_profile_pic",
|
||||||
|
)
|
3
backend/users_app/tests.py
Normal file
3
backend/users_app/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
25
backend/users_app/urls.py
Normal file
25
backend/users_app/urls.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# have to manually create this file
|
||||||
|
# manually created urls.py file
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path, include
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls.static import static
|
||||||
|
from users_app import views
|
||||||
|
|
||||||
|
|
||||||
|
# user actions
|
||||||
|
urlpatterns = [
|
||||||
|
path('create-basic-user/', views.SaveCustomBasicUser.as_view()),
|
||||||
|
# for username validation, allow a string to be input as a param
|
||||||
|
path('check-username/<str:username>/', views.check_username),
|
||||||
|
path('check-email/<str:email>/', views.check_email),
|
||||||
|
path('logout', views.LogoutView.as_view()),
|
||||||
|
path('get-device-data/', views.get_user_device),
|
||||||
|
# path('send-password-reset-link/', views.send_password_reset_link),
|
||||||
|
# path('reset-user-password/', views.reset_password),
|
||||||
|
path('get-user-account-data/', views.get_user_account_data),
|
||||||
|
path('update-user-account-data/', views.update_user_account_data),
|
||||||
|
path('delete-user-account-data/', views.delete_user_account_data),
|
||||||
|
path('get-user-pfp/', views.get_user_pfp),
|
||||||
|
]
|
0
backend/users_app/user_logs.txt
Normal file
0
backend/users_app/user_logs.txt
Normal file
349
backend/users_app/views.py
Normal file
349
backend/users_app/views.py
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import authentication, permissions
|
||||||
|
from rest_framework.decorators import api_view, authentication_classes, permission_classes
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from users_app.serializers import *
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
|
# converts html template to a string message for emails
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.conf import settings
|
||||||
|
from main_project import settings
|
||||||
|
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
|
from django.utils.http import urlsafe_base64_encode
|
||||||
|
from django.utils.encoding import force_str
|
||||||
|
from django.utils.http import urlsafe_base64_decode
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from rest_framework import permissions, status
|
||||||
|
|
||||||
|
# for saving profilepic
|
||||||
|
from PIL import Image
|
||||||
|
from django.core.files import File
|
||||||
|
from io import BytesIO
|
||||||
|
from django.core.files.uploadedfile import InMemoryUploadedFile
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
|
||||||
|
# set up logger
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# create file handler and set level to INFO
|
||||||
|
log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_logs.txt')
|
||||||
|
fh = logging.FileHandler(log_file)
|
||||||
|
fh.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# create formatter
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
fh.setFormatter(formatter)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
|
|
||||||
|
user_obj = get_user_model()
|
||||||
|
|
||||||
|
# email stuff for sending email notifications
|
||||||
|
EMAIL_ON = False
|
||||||
|
URL = settings.env('DEV_DOMAIN') if settings.env('DEV_MODE') == 'True' else None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# save user pfp on signup
|
||||||
|
# SaveUserProfilePicture API view
|
||||||
|
class SaveCustomBasicUser(APIView):
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
serializer = CustomUserCreateSerializer(data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
profile_pic = request.FILES.get('user_profile_pic', None)
|
||||||
|
if profile_pic:
|
||||||
|
profile_pic.file.seek(0)
|
||||||
|
image = Image.open(BytesIO(profile_pic.read()))
|
||||||
|
if image.mode == 'RGBA':
|
||||||
|
image = image.convert('RGB')
|
||||||
|
pfp_name = serializer.validated_data['username'] + '_pfp'
|
||||||
|
buffer = BytesIO()
|
||||||
|
image.save(buffer, format='JPEG')
|
||||||
|
image_file = InMemoryUploadedFile(
|
||||||
|
buffer, None, pfp_name + '.jpg', 'image/jpeg',
|
||||||
|
buffer.getbuffer().nbytes, None
|
||||||
|
)
|
||||||
|
# Call create_user method of custom user manager to create user instance
|
||||||
|
user = Users_CustomUserManager().create_user(
|
||||||
|
email=serializer.validated_data['email'],
|
||||||
|
username=serializer.validated_data['username'],
|
||||||
|
password=serializer.validated_data['password'],
|
||||||
|
user_profile_pic=image_file,
|
||||||
|
user_first_name=serializer.validated_data['user_first_name'],
|
||||||
|
user_last_name=serializer.validated_data['user_last_name'],
|
||||||
|
user_favorite_bible_verse=serializer.validated_data['user_favorite_bible_verse']
|
||||||
|
)
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
else:
|
||||||
|
|
||||||
|
# save user without profile pic
|
||||||
|
user = Users_CustomUserManager().create_user(
|
||||||
|
email=serializer.validated_data['email'],
|
||||||
|
username=serializer.validated_data['username'],
|
||||||
|
password=serializer.validated_data['password'],
|
||||||
|
user_profile_pic=image_file,
|
||||||
|
user_first_name=serializer.validated_data['user_first_name'],
|
||||||
|
user_last_name=serializer.validated_data['user_last_name'],
|
||||||
|
user_favorite_bible_verse=serializer.validated_data['user_favorite_bible_verse']
|
||||||
|
)
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
else:
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
# get user pfp on login
|
||||||
|
@api_view(['GET'])
|
||||||
|
@authentication_classes([authentication.TokenAuthentication])
|
||||||
|
@permission_classes([permissions.IsAuthenticated])
|
||||||
|
def get_user_pfp(request):
|
||||||
|
current_user = user_obj.objects.get(pk=request.user.pk)
|
||||||
|
user_pfp_serializer = GetUserPfpSerializer(current_user)
|
||||||
|
|
||||||
|
response = Response(user_pfp_serializer.data, status=status.HTTP_200_OK)
|
||||||
|
# added this to prevent vuejs from caching cart data
|
||||||
|
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
||||||
|
response['Expires'] = '0'
|
||||||
|
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# delete user account
|
||||||
|
@api_view(['POST'])
|
||||||
|
@authentication_classes([authentication.TokenAuthentication])
|
||||||
|
@permission_classes([permissions.IsAuthenticated])
|
||||||
|
def delete_user_account_data(request):
|
||||||
|
|
||||||
|
# use first to prevent exception from being raised
|
||||||
|
try:
|
||||||
|
|
||||||
|
user_to_be_deleted = user_obj.objects.get(pk=request.user.pk)
|
||||||
|
user_to_be_deleted.delete()
|
||||||
|
# return no content, everything worked
|
||||||
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
except:
|
||||||
|
return Response({'error': 'Invalid token or user'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
# update user account info
|
||||||
|
@api_view(['POST'])
|
||||||
|
@authentication_classes([authentication.TokenAuthentication])
|
||||||
|
@permission_classes([permissions.IsAuthenticated])
|
||||||
|
def update_user_account_data(request):
|
||||||
|
|
||||||
|
# get the user
|
||||||
|
current_user = user_obj.objects.get(pk=request.user.pk)
|
||||||
|
# Deserialize incoming data
|
||||||
|
# serializer = UpdateUserAccountDataSerializer(data=request.data)
|
||||||
|
serializer = UpdateUserAccountDataSerializer(data=request.data, context={'request': request})
|
||||||
|
|
||||||
|
if serializer.is_valid():
|
||||||
|
|
||||||
|
# get the img file
|
||||||
|
profile_pic = request.FILES.get('user_profile_pic', None)
|
||||||
|
|
||||||
|
if profile_pic:
|
||||||
|
|
||||||
|
profile_pic.file.seek(0)
|
||||||
|
# Open the uploaded image file
|
||||||
|
image = Image.open(BytesIO(profile_pic.read()))
|
||||||
|
|
||||||
|
# Convert RGBA to RGB mode if it exists
|
||||||
|
if image.mode == 'RGBA':
|
||||||
|
image = image.convert('RGB')
|
||||||
|
|
||||||
|
pfp_name = current_user.username + '_pfp'
|
||||||
|
|
||||||
|
buffer = BytesIO()
|
||||||
|
image.save(buffer, format='JPEG')
|
||||||
|
image_file = InMemoryUploadedFile(
|
||||||
|
buffer, None, pfp_name + '.jpg', 'image/jpeg',
|
||||||
|
buffer.getbuffer().nbytes, None
|
||||||
|
)
|
||||||
|
current_user.user_profile_pic.save(pfp_name + '.jpg', image_file)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print('\n\n Image does not exist')
|
||||||
|
# save the rest of the text fields
|
||||||
|
# Update user fields
|
||||||
|
# validated_data is a dictionary that holds validated data ready to be saved to db
|
||||||
|
current_user.username = serializer.validated_data.get('username', current_user.username)
|
||||||
|
current_user.email = serializer.validated_data.get('email', current_user.email)
|
||||||
|
current_user.user_first_name = serializer.validated_data.get('user_first_name', current_user.first_name)
|
||||||
|
current_user.user_last_name = serializer.validated_data.get('user_last_name', current_user.last_name)
|
||||||
|
current_user.user_favorite_bible_verse = serializer.validated_data.get('user_favorite_bible_verse', current_user.favorite_color)
|
||||||
|
|
||||||
|
current_user.save()
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
else:
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
# get user account data
|
||||||
|
@api_view(['GET'])
|
||||||
|
@authentication_classes([authentication.TokenAuthentication])
|
||||||
|
@permission_classes([permissions.IsAuthenticated])
|
||||||
|
def get_user_account_data(request):
|
||||||
|
|
||||||
|
current_user = user.objects.get(pk=request.user.pk)
|
||||||
|
user_serializer = GetUserSerializer(current_user)
|
||||||
|
|
||||||
|
response = Response(user_serializer.data, status=status.HTTP_200_OK)
|
||||||
|
# added this to prevent frontend from caching cart data
|
||||||
|
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
||||||
|
response['Expires'] = '0'
|
||||||
|
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# reset password
|
||||||
|
@api_view(['POST'])
|
||||||
|
@permission_classes([permissions.AllowAny])
|
||||||
|
def reset_password(request):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
uid = force_str(urlsafe_base64_decode(request.data.get('uidb64')))
|
||||||
|
|
||||||
|
current_user = user.objects.get(pk=uid)
|
||||||
|
|
||||||
|
except (TypeError, ValueError, OverflowError, user.DoesNotExist):
|
||||||
|
current_user = None
|
||||||
|
|
||||||
|
# Check if the token is valid
|
||||||
|
if current_user is not None and default_token_generator.check_token(current_user, request.data.get('token')):
|
||||||
|
# Set the new password for the user
|
||||||
|
password = request.data.get('password')
|
||||||
|
current_user.set_password(password)
|
||||||
|
current_user.save()
|
||||||
|
|
||||||
|
template = render_to_string('../templates/changed_account_notice_email.html', {'name':current_user.first_name})
|
||||||
|
email = EmailMessage(
|
||||||
|
# email subject title default is 'subject'
|
||||||
|
'There was a change to your account -- アカウント情報変更のお知らせ',
|
||||||
|
# email template default is 'body'
|
||||||
|
template,
|
||||||
|
|
||||||
|
settings.EMAIL_HOST_USER,
|
||||||
|
# recipient list
|
||||||
|
[current_user.email],
|
||||||
|
)
|
||||||
|
email.fail_silently=False
|
||||||
|
# eonly send email if this flag is true
|
||||||
|
if EMAIL_ON:
|
||||||
|
email.send()
|
||||||
|
|
||||||
|
return Response({'success': 'Password reset successful'}, status=status.HTTP_200_OK)
|
||||||
|
else:
|
||||||
|
return Response({'error': 'Invalid token or user'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
# send password reset link
|
||||||
|
@api_view(['POST'])
|
||||||
|
@permission_classes([permissions.AllowAny])
|
||||||
|
def send_password_reset_link(request):
|
||||||
|
|
||||||
|
# get the email address from the POST request
|
||||||
|
email = request.data.get('potential_email_address')
|
||||||
|
|
||||||
|
# check if the email address is valid
|
||||||
|
try:
|
||||||
|
get_user = user.objects.get(email=email)
|
||||||
|
|
||||||
|
# creating a password reset url unique for each user
|
||||||
|
token_generator = PasswordResetTokenGenerator()
|
||||||
|
uidb64 = urlsafe_base64_encode(force_bytes(get_user.pk))
|
||||||
|
token = token_generator.make_token(get_user)
|
||||||
|
# create the password reset URL using the generated token
|
||||||
|
password_reset_url = f'{URL}{request.data.get("password_reset_url")}/{uidb64}/{token}/'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
EMAIL_ON = True
|
||||||
|
template = render_to_string('../templates/password-reset-email.html', {'name':get_user.first_name, 'password_reset_url': password_reset_url})
|
||||||
|
email = EmailMessage(
|
||||||
|
# email subject title default is 'subject'
|
||||||
|
'Password reset -- パスワードのリセット',
|
||||||
|
# email template default is 'body'
|
||||||
|
template,
|
||||||
|
settings.EMAIL_HOST_USER,
|
||||||
|
# recipient list
|
||||||
|
[get_user.email],
|
||||||
|
)
|
||||||
|
email.fail_silently=False
|
||||||
|
# only send email if this flag is true
|
||||||
|
if EMAIL_ON:
|
||||||
|
email.send()
|
||||||
|
# just return a 200 response
|
||||||
|
return HttpResponse(status=200)
|
||||||
|
except user.DoesNotExist:
|
||||||
|
# handle the case where the user does not exist
|
||||||
|
return Response({'error': 'User does not exist'}, status=200)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return Response({'error': 'Unknown error occurred'}, status=500)
|
||||||
|
'''
|
||||||
|
|
||||||
|
# get user data
|
||||||
|
@api_view(['GET'])
|
||||||
|
def get_user_device(request):
|
||||||
|
user_agent = request.META.get('HTTP_USER_AGENT', None)
|
||||||
|
# do something with usr's device
|
||||||
|
return Response({'message': 'success'})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# checking username in form validation
|
||||||
|
# need to include the request or else there will be an error
|
||||||
|
@api_view(['GET'])
|
||||||
|
def check_username(request, username):
|
||||||
|
username_available = not user.objects.filter(username=username).exists()
|
||||||
|
|
||||||
|
response = Response({'available': username_available})
|
||||||
|
# added this to prevent vuejs from caching cart data
|
||||||
|
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
||||||
|
response['Expires'] = '0'
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
# checking username in form validation
|
||||||
|
@api_view(['GET'])
|
||||||
|
def check_email(request, email):
|
||||||
|
email_available = not user.objects.filter(email=email).exists()
|
||||||
|
|
||||||
|
response = Response({'available': email_available})
|
||||||
|
# added this to prevent vuejs from caching cart data
|
||||||
|
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
||||||
|
response['Expires'] = '0'
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
# de-authenticate user by deleting auth token, and storing/updating and then saving the user's cart data
|
||||||
|
class LogoutView(APIView):
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
def post(self, request, format=None):
|
||||||
|
|
||||||
|
|
||||||
|
Token.objects.filter(user=user).delete()
|
||||||
|
return Response({'success': 'Logged out successfully.'})
|
29
frontend/README.md
Normal file
29
frontend/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# react native app setup (not using Expo)
|
||||||
|
# REACT NATIVE CLI QUICKSTART, NOT EXPO GO QUICKSTART
|
||||||
|
# follow setup guide for each specific platform (iOS or Android) and OS (Linux, Windows, MacOS): https://reactnative.dev/docs/environment-setup
|
||||||
|
|
||||||
|
- make sure you have node and npm installed
|
||||||
|
|
||||||
|
- cd to directory where you want this app to be installed
|
||||||
|
- create app:
|
||||||
|
|
||||||
|
npx react-native@latest init SelahReactNativeApp
|
||||||
|
|
||||||
|
- select yes for installing react-native packages
|
||||||
|
|
||||||
|
NOTE: some of the files might complain about not being able to detect the babel.config.js file. Add this to your
|
||||||
|
settings.json file in VS Code (Open command pallete: View-> Command Pallete or Ctrl+Shift-P and type "settings"):
|
||||||
|
|
||||||
|
```
|
||||||
|
"eslint.workingDirectories": [
|
||||||
|
{"mode": "auto"}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- to run the app, open a new terminal and start Metro which is the js bundler that ships with react native. cd into
|
||||||
|
react-native app directory and run:
|
||||||
|
|
||||||
|
npx react-native start
|
||||||
|
|
||||||
|
- to start the app in an android device
|
||||||
|
|
2
frontend/SelahReactNativeApp/.bundle/config
Normal file
2
frontend/SelahReactNativeApp/.bundle/config
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
BUNDLE_PATH: "vendor/bundle"
|
||||||
|
BUNDLE_FORCE_RUBY_PLATFORM: 1
|
4
frontend/SelahReactNativeApp/.eslintrc.js
Normal file
4
frontend/SelahReactNativeApp/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: '@react-native-community',
|
||||||
|
};
|
63
frontend/SelahReactNativeApp/.gitignore
vendored
Normal file
63
frontend/SelahReactNativeApp/.gitignore
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# OSX
|
||||||
|
#
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata
|
||||||
|
*.xccheckout
|
||||||
|
*.moved-aside
|
||||||
|
DerivedData
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.xcuserstate
|
||||||
|
ios/.xcode.env.local
|
||||||
|
|
||||||
|
# Android/IntelliJ
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
local.properties
|
||||||
|
*.iml
|
||||||
|
*.hprof
|
||||||
|
.cxx/
|
||||||
|
*.keystore
|
||||||
|
!debug.keystore
|
||||||
|
|
||||||
|
# node.js
|
||||||
|
#
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||||
|
# screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://docs.fastlane.tools/best-practices/source-control/
|
||||||
|
|
||||||
|
**/fastlane/report.xml
|
||||||
|
**/fastlane/Preview.html
|
||||||
|
**/fastlane/screenshots
|
||||||
|
**/fastlane/test_output
|
||||||
|
|
||||||
|
# Bundle artifact
|
||||||
|
*.jsbundle
|
||||||
|
|
||||||
|
# Ruby / CocoaPods
|
||||||
|
/ios/Pods/
|
||||||
|
/vendor/bundle/
|
||||||
|
|
||||||
|
# Temporary files created by Metro to check the health of the file watcher
|
||||||
|
.metro-health-check*
|
1
frontend/SelahReactNativeApp/.node-version
Normal file
1
frontend/SelahReactNativeApp/.node-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
18
|
7
frontend/SelahReactNativeApp/.prettierrc.js
Normal file
7
frontend/SelahReactNativeApp/.prettierrc.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
arrowParens: 'avoid',
|
||||||
|
bracketSameLine: true,
|
||||||
|
bracketSpacing: false,
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'all',
|
||||||
|
};
|
1
frontend/SelahReactNativeApp/.watchmanconfig
Normal file
1
frontend/SelahReactNativeApp/.watchmanconfig
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
118
frontend/SelahReactNativeApp/App.tsx
Normal file
118
frontend/SelahReactNativeApp/App.tsx
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* Sample React Native App
|
||||||
|
* https://github.com/facebook/react-native
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import type {PropsWithChildren} from 'react';
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
ScrollView,
|
||||||
|
StatusBar,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
useColorScheme,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Colors,
|
||||||
|
DebugInstructions,
|
||||||
|
Header,
|
||||||
|
LearnMoreLinks,
|
||||||
|
ReloadInstructions,
|
||||||
|
} from 'react-native/Libraries/NewAppScreen';
|
||||||
|
|
||||||
|
type SectionProps = PropsWithChildren<{
|
||||||
|
title: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
function Section({children, title}: SectionProps): JSX.Element {
|
||||||
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
|
return (
|
||||||
|
<View style={styles.sectionContainer}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.sectionTitle,
|
||||||
|
{
|
||||||
|
color: isDarkMode ? Colors.white : Colors.black,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.sectionDescription,
|
||||||
|
{
|
||||||
|
color: isDarkMode ? Colors.light : Colors.dark,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function App(): JSX.Element {
|
||||||
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
|
|
||||||
|
const backgroundStyle = {
|
||||||
|
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={backgroundStyle}>
|
||||||
|
<StatusBar
|
||||||
|
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
|
||||||
|
backgroundColor={backgroundStyle.backgroundColor}
|
||||||
|
/>
|
||||||
|
<ScrollView
|
||||||
|
contentInsetAdjustmentBehavior="automatic"
|
||||||
|
style={backgroundStyle}>
|
||||||
|
<Header />
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
backgroundColor: isDarkMode ? Colors.black : Colors.white,
|
||||||
|
}}>
|
||||||
|
<Section title="Step One">
|
||||||
|
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
|
||||||
|
screen and then come back to see your edits.
|
||||||
|
</Section>
|
||||||
|
<Section title="See Your Changes">
|
||||||
|
<ReloadInstructions />
|
||||||
|
</Section>
|
||||||
|
<Section title="Debug">
|
||||||
|
<DebugInstructions />
|
||||||
|
</Section>
|
||||||
|
<Section title="Learn More">
|
||||||
|
Read the docs to discover what to do next:
|
||||||
|
</Section>
|
||||||
|
<LearnMoreLinks />
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
sectionContainer: {
|
||||||
|
marginTop: 32,
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
sectionDescription: {
|
||||||
|
marginTop: 8,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '400',
|
||||||
|
},
|
||||||
|
highlight: {
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default App;
|
6
frontend/SelahReactNativeApp/Gemfile
Normal file
6
frontend/SelahReactNativeApp/Gemfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||||
|
ruby '>= 2.6.10'
|
||||||
|
|
||||||
|
gem 'cocoapods', '>= 1.11.3'
|
14
frontend/SelahReactNativeApp/__tests__/App-test.tsx
Normal file
14
frontend/SelahReactNativeApp/__tests__/App-test.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import App from '../App';
|
||||||
|
|
||||||
|
// Note: test renderer must be required after react-native.
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
it('renders correctly', () => {
|
||||||
|
renderer.create(<App />);
|
||||||
|
});
|
170
frontend/SelahReactNativeApp/android/app/build.gradle
Normal file
170
frontend/SelahReactNativeApp/android/app/build.gradle
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
apply plugin: "com.android.application"
|
||||||
|
apply plugin: "com.facebook.react"
|
||||||
|
|
||||||
|
import com.android.build.OutputFile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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"]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this to true to create four separate APKs instead of one,
|
||||||
|
* one for each native architecture. This is useful if you don't
|
||||||
|
* use App Bundles (https://developer.android.com/guide/app-bundle/)
|
||||||
|
* and want to have separate APKs to upload to the Play Store.
|
||||||
|
*/
|
||||||
|
def enableSeparateBuildPerCPUArchitecture = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:+'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private function to get the list of Native Architectures you want to build.
|
||||||
|
* This reads the value from reactNativeArchitectures in your gradle.properties
|
||||||
|
* file and works together with the --active-arch-only flag of react-native run-android.
|
||||||
|
*/
|
||||||
|
def reactNativeArchitectures() {
|
||||||
|
def value = project.getProperties().get("reactNativeArchitectures")
|
||||||
|
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
ndkVersion rootProject.ext.ndkVersion
|
||||||
|
|
||||||
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
|
|
||||||
|
namespace "com.selahreactnativeapp"
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.selahreactnativeapp"
|
||||||
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
splits {
|
||||||
|
abi {
|
||||||
|
reset()
|
||||||
|
enable enableSeparateBuildPerCPUArchitecture
|
||||||
|
universalApk false // If true, also generate a universal APK
|
||||||
|
include (*reactNativeArchitectures())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applicationVariants are e.g. debug, release
|
||||||
|
applicationVariants.all { variant ->
|
||||||
|
variant.outputs.each { output ->
|
||||||
|
// For each separate APK per architecture, set a unique version code as described here:
|
||||||
|
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||||
|
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
|
||||||
|
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||||
|
def abi = output.getFilter(OutputFile.ABI)
|
||||||
|
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||||
|
output.versionCodeOverride =
|
||||||
|
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// The version of react-native is set by the React Native Gradle Plugin
|
||||||
|
implementation("com.facebook.react:react-android")
|
||||||
|
|
||||||
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
|
||||||
|
|
||||||
|
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
|
||||||
|
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||||
|
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||||
|
}
|
||||||
|
|
||||||
|
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
|
||||||
|
if (hermesEnabled.toBoolean()) {
|
||||||
|
implementation("com.facebook.react:hermes-android")
|
||||||
|
} else {
|
||||||
|
implementation jscFlavor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
BIN
frontend/SelahReactNativeApp/android/app/debug.keystore
Normal file
BIN
frontend/SelahReactNativeApp/android/app/debug.keystore
Normal file
Binary file not shown.
10
frontend/SelahReactNativeApp/android/app/proguard-rules.pro
vendored
Normal file
10
frontend/SelahReactNativeApp/android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
tools:targetApi="28"
|
||||||
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||||
|
* directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.selahreactnativeapp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||||
|
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||||
|
import com.facebook.flipper.core.FlipperClient;
|
||||||
|
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||||
|
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||||
|
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||||
|
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||||
|
import com.facebook.react.ReactInstanceEventListener;
|
||||||
|
import com.facebook.react.ReactInstanceManager;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.modules.network.NetworkingModule;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class responsible of loading Flipper inside your React Native application. This is the debug
|
||||||
|
* flavor of it. Here you can add your own plugins and customize the Flipper setup.
|
||||||
|
*/
|
||||||
|
public class ReactNativeFlipper {
|
||||||
|
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||||
|
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||||
|
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||||
|
|
||||||
|
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||||
|
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||||
|
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||||
|
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||||
|
|
||||||
|
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||||
|
NetworkingModule.setCustomClientBuilder(
|
||||||
|
new NetworkingModule.CustomClientBuilder() {
|
||||||
|
@Override
|
||||||
|
public void apply(OkHttpClient.Builder builder) {
|
||||||
|
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.addPlugin(networkFlipperPlugin);
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||||
|
// Hence we run if after all native modules have been initialized
|
||||||
|
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||||
|
if (reactContext == null) {
|
||||||
|
reactInstanceManager.addReactInstanceEventListener(
|
||||||
|
new ReactInstanceEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onReactContextInitialized(ReactContext reactContext) {
|
||||||
|
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||||
|
reactContext.runOnNativeModulesQueueThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
client.addPlugin(new FrescoFlipperPlugin());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
client.addPlugin(new FrescoFlipperPlugin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".MainApplication"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.selahreactnativeapp;
|
||||||
|
|
||||||
|
import com.facebook.react.ReactActivity;
|
||||||
|
import com.facebook.react.ReactActivityDelegate;
|
||||||
|
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||||
|
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||||
|
|
||||||
|
public class MainActivity extends ReactActivity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||||
|
* rendering of the component.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String getMainComponentName() {
|
||||||
|
return "SelahReactNativeApp";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||||
|
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||||
|
* (aka React 18) with two boolean flags.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||||
|
return new DefaultReactActivityDelegate(
|
||||||
|
this,
|
||||||
|
getMainComponentName(),
|
||||||
|
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||||
|
DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
|
||||||
|
// If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
|
||||||
|
DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package com.selahreactnativeapp;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import com.facebook.react.PackageList;
|
||||||
|
import com.facebook.react.ReactApplication;
|
||||||
|
import com.facebook.react.ReactNativeHost;
|
||||||
|
import com.facebook.react.ReactPackage;
|
||||||
|
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||||
|
import com.facebook.react.defaults.DefaultReactNativeHost;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MainApplication extends Application implements ReactApplication {
|
||||||
|
|
||||||
|
private final ReactNativeHost mReactNativeHost =
|
||||||
|
new DefaultReactNativeHost(this) {
|
||||||
|
@Override
|
||||||
|
public boolean getUseDeveloperSupport() {
|
||||||
|
return BuildConfig.DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<ReactPackage> getPackages() {
|
||||||
|
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||||
|
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||||
|
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||||
|
// packages.add(new MyReactNativePackage());
|
||||||
|
return packages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getJSMainModuleName() {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isNewArchEnabled() {
|
||||||
|
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean isHermesEnabled() {
|
||||||
|
return BuildConfig.IS_HERMES_ENABLED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReactNativeHost getReactNativeHost() {
|
||||||
|
return mReactNativeHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
SoLoader.init(this, /* native exopackage */ false);
|
||||||
|
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||||
|
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||||
|
DefaultNewArchitectureEntryPoint.load();
|
||||||
|
}
|
||||||
|
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||||
|
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||||
|
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||||
|
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
|
||||||
|
|
||||||
|
<selector>
|
||||||
|
<!--
|
||||||
|
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||||
|
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
||||||
|
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
||||||
|
|
||||||
|
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||||
|
|
||||||
|
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
||||||
|
-->
|
||||||
|
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||||
|
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
||||||
|
</selector>
|
||||||
|
|
||||||
|
</inset>
|
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">SelahReactNativeApp</string>
|
||||||
|
</resources>
|
@ -0,0 +1,9 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||||
|
* directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.selahreactnativeapp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.facebook.react.ReactInstanceManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class responsible of loading Flipper inside your React Native application. This is the release
|
||||||
|
* flavor of it so it's empty as we don't want to load Flipper.
|
||||||
|
*/
|
||||||
|
public class ReactNativeFlipper {
|
||||||
|
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||||
|
// Do nothing as we don't want to initialize Flipper on Release.
|
||||||
|
}
|
||||||
|
}
|
21
frontend/SelahReactNativeApp/android/build.gradle
Normal file
21
frontend/SelahReactNativeApp/android/build.gradle
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
buildToolsVersion = "33.0.0"
|
||||||
|
minSdkVersion = 21
|
||||||
|
compileSdkVersion = 33
|
||||||
|
targetSdkVersion = 33
|
||||||
|
|
||||||
|
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||||
|
ndkVersion = "23.1.7779620"
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("com.android.tools.build:gradle:7.3.1")
|
||||||
|
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||||
|
}
|
||||||
|
}
|
44
frontend/SelahReactNativeApp/android/gradle.properties
Normal file
44
frontend/SelahReactNativeApp/android/gradle.properties
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# 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
|
||||||
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
|
android.enableJetifier=true
|
||||||
|
|
||||||
|
# Version of flipper SDK to use with React Native
|
||||||
|
FLIPPER_VERSION=0.125.0
|
||||||
|
|
||||||
|
# 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
|
BIN
frontend/SelahReactNativeApp/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
frontend/SelahReactNativeApp/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
frontend/SelahReactNativeApp/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
frontend/SelahReactNativeApp/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
234
frontend/SelahReactNativeApp/android/gradlew
vendored
Executable file
234
frontend/SelahReactNativeApp/android/gradlew
vendored
Executable file
@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
89
frontend/SelahReactNativeApp/android/gradlew.bat
vendored
Normal file
89
frontend/SelahReactNativeApp/android/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
4
frontend/SelahReactNativeApp/android/settings.gradle
Normal file
4
frontend/SelahReactNativeApp/android/settings.gradle
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
rootProject.name = 'SelahReactNativeApp'
|
||||||
|
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||||
|
include ':app'
|
||||||
|
includeBuild('../node_modules/react-native-gradle-plugin')
|
4
frontend/SelahReactNativeApp/app.json
Normal file
4
frontend/SelahReactNativeApp/app.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "SelahReactNativeApp",
|
||||||
|
"displayName": "SelahReactNativeApp"
|
||||||
|
}
|
3
frontend/SelahReactNativeApp/babel.config.js
Normal file
3
frontend/SelahReactNativeApp/babel.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
|
};
|
9
frontend/SelahReactNativeApp/index.js
Normal file
9
frontend/SelahReactNativeApp/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {AppRegistry} from 'react-native';
|
||||||
|
import App from './App';
|
||||||
|
import {name as appName} from './app.json';
|
||||||
|
|
||||||
|
AppRegistry.registerComponent(appName, () => App);
|
11
frontend/SelahReactNativeApp/ios/.xcode.env
Normal file
11
frontend/SelahReactNativeApp/ios/.xcode.env
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# This `.xcode.env` file is versioned and is used to source the environment
|
||||||
|
# used when running script phases inside Xcode.
|
||||||
|
# To customize your local environment, you can create an `.xcode.env.local`
|
||||||
|
# file that is not versioned.
|
||||||
|
|
||||||
|
# NODE_BINARY variable contains the PATH to the node executable.
|
||||||
|
#
|
||||||
|
# Customize the NODE_BINARY variable here.
|
||||||
|
# For example, to use nvm with brew, add the following line
|
||||||
|
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
||||||
|
export NODE_BINARY=$(command -v node)
|
60
frontend/SelahReactNativeApp/ios/Podfile
Normal file
60
frontend/SelahReactNativeApp/ios/Podfile
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||||
|
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||||
|
|
||||||
|
platform :ios, min_ios_version_supported
|
||||||
|
prepare_react_native_project!
|
||||||
|
|
||||||
|
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
|
||||||
|
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
|
||||||
|
#
|
||||||
|
# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
|
||||||
|
# ```js
|
||||||
|
# module.exports = {
|
||||||
|
# dependencies: {
|
||||||
|
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
|
||||||
|
# ```
|
||||||
|
flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
|
||||||
|
|
||||||
|
linkage = ENV['USE_FRAMEWORKS']
|
||||||
|
if linkage != nil
|
||||||
|
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
||||||
|
use_frameworks! :linkage => linkage.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
target 'SelahReactNativeApp' do
|
||||||
|
config = use_native_modules!
|
||||||
|
|
||||||
|
# Flags change depending on the env values.
|
||||||
|
flags = get_default_flags()
|
||||||
|
|
||||||
|
use_react_native!(
|
||||||
|
:path => config[:reactNativePath],
|
||||||
|
# Hermes is now enabled by default. Disable by setting this flag to false.
|
||||||
|
# Upcoming versions of React Native may rely on get_default_flags(), but
|
||||||
|
# we make it explicit here to aid in the React Native upgrade process.
|
||||||
|
:hermes_enabled => flags[:hermes_enabled],
|
||||||
|
:fabric_enabled => flags[:fabric_enabled],
|
||||||
|
# Enables Flipper.
|
||||||
|
#
|
||||||
|
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||||
|
# you should disable the next line.
|
||||||
|
:flipper_configuration => flipper_config,
|
||||||
|
# An absolute path to your application root.
|
||||||
|
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||||
|
)
|
||||||
|
|
||||||
|
target 'SelahReactNativeAppTests' do
|
||||||
|
inherit! :complete
|
||||||
|
# Pods for testing
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
react_native_post_install(
|
||||||
|
installer,
|
||||||
|
# Set `mac_catalyst_enabled` to `true` in order to apply patches
|
||||||
|
# necessary for Mac Catalyst builds
|
||||||
|
:mac_catalyst_enabled => false
|
||||||
|
)
|
||||||
|
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,702 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 54;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
00E356F31AD99517003FC87E /* SelahReactNativeAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SelahReactNativeAppTests.m */; };
|
||||||
|
0C80B921A6F3F58F76C31292 /* libPods-SelahReactNativeApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-SelahReactNativeApp.a */; };
|
||||||
|
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||||
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
|
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||||
|
7699B88040F8A987B510C191 /* libPods-SelahReactNativeApp-SelahReactNativeAppTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-SelahReactNativeApp-SelahReactNativeAppTests.a */; };
|
||||||
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||||
|
remoteInfo = SelahReactNativeApp;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
00E356EE1AD99517003FC87E /* SelahReactNativeAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SelahReactNativeAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
00E356F21AD99517003FC87E /* SelahReactNativeAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SelahReactNativeAppTests.m; sourceTree = "<group>"; };
|
||||||
|
13B07F961A680F5B00A75B9A /* SelahReactNativeApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SelahReactNativeApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = SelahReactNativeApp/AppDelegate.h; sourceTree = "<group>"; };
|
||||||
|
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = SelahReactNativeApp/AppDelegate.mm; sourceTree = "<group>"; };
|
||||||
|
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = SelahReactNativeApp/Images.xcassets; sourceTree = "<group>"; };
|
||||||
|
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = SelahReactNativeApp/Info.plist; sourceTree = "<group>"; };
|
||||||
|
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = SelahReactNativeApp/main.m; sourceTree = "<group>"; };
|
||||||
|
19F6CBCC0A4E27FBF8BF4A61 /* libPods-SelahReactNativeApp-SelahReactNativeAppTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SelahReactNativeApp-SelahReactNativeAppTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
3B4392A12AC88292D35C810B /* Pods-SelahReactNativeApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelahReactNativeApp.debug.xcconfig"; path = "Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
5709B34CF0A7D63546082F79 /* Pods-SelahReactNativeApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelahReactNativeApp.release.xcconfig"; path = "Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
5B7EB9410499542E8C5724F5 /* Pods-SelahReactNativeApp-SelahReactNativeAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelahReactNativeApp-SelahReactNativeAppTests.debug.xcconfig"; path = "Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
5DCACB8F33CDC322A6C60F78 /* libPods-SelahReactNativeApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SelahReactNativeApp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = SelahReactNativeApp/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
89C6BE57DB24E9ADA2F236DE /* Pods-SelahReactNativeApp-SelahReactNativeAppTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SelahReactNativeApp-SelahReactNativeAppTests.release.xcconfig"; path = "Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
00E356EB1AD99517003FC87E /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
7699B88040F8A987B510C191 /* libPods-SelahReactNativeApp-SelahReactNativeAppTests.a in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
0C80B921A6F3F58F76C31292 /* libPods-SelahReactNativeApp.a in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
00E356EF1AD99517003FC87E /* SelahReactNativeAppTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
00E356F21AD99517003FC87E /* SelahReactNativeAppTests.m */,
|
||||||
|
00E356F01AD99517003FC87E /* Supporting Files */,
|
||||||
|
);
|
||||||
|
path = SelahReactNativeAppTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
00E356F01AD99517003FC87E /* Supporting Files */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
00E356F11AD99517003FC87E /* Info.plist */,
|
||||||
|
);
|
||||||
|
name = "Supporting Files";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
13B07FAE1A68108700A75B9A /* SelahReactNativeApp */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||||
|
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||||
|
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||||
|
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||||
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||||
|
13B07FB71A68108700A75B9A /* main.m */,
|
||||||
|
);
|
||||||
|
name = SelahReactNativeApp;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||||
|
5DCACB8F33CDC322A6C60F78 /* libPods-SelahReactNativeApp.a */,
|
||||||
|
19F6CBCC0A4E27FBF8BF4A61 /* libPods-SelahReactNativeApp-SelahReactNativeAppTests.a */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Libraries;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
83CBB9F61A601CBA00E9B192 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
13B07FAE1A68108700A75B9A /* SelahReactNativeApp */,
|
||||||
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
|
00E356EF1AD99517003FC87E /* SelahReactNativeAppTests */,
|
||||||
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
|
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||||
|
);
|
||||||
|
indentWidth = 2;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
tabWidth = 2;
|
||||||
|
usesTabs = 0;
|
||||||
|
};
|
||||||
|
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
13B07F961A680F5B00A75B9A /* SelahReactNativeApp.app */,
|
||||||
|
00E356EE1AD99517003FC87E /* SelahReactNativeAppTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B4392A12AC88292D35C810B /* Pods-SelahReactNativeApp.debug.xcconfig */,
|
||||||
|
5709B34CF0A7D63546082F79 /* Pods-SelahReactNativeApp.release.xcconfig */,
|
||||||
|
5B7EB9410499542E8C5724F5 /* Pods-SelahReactNativeApp-SelahReactNativeAppTests.debug.xcconfig */,
|
||||||
|
89C6BE57DB24E9ADA2F236DE /* Pods-SelahReactNativeApp-SelahReactNativeAppTests.release.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
00E356ED1AD99517003FC87E /* SelahReactNativeAppTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SelahReactNativeAppTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
00E356EA1AD99517003FC87E /* Sources */,
|
||||||
|
00E356EB1AD99517003FC87E /* Frameworks */,
|
||||||
|
00E356EC1AD99517003FC87E /* Resources */,
|
||||||
|
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
|
||||||
|
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
00E356F51AD99517003FC87E /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = SelahReactNativeAppTests;
|
||||||
|
productName = SelahReactNativeAppTests;
|
||||||
|
productReference = 00E356EE1AD99517003FC87E /* SelahReactNativeAppTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
13B07F861A680F5B00A75B9A /* SelahReactNativeApp */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SelahReactNativeApp" */;
|
||||||
|
buildPhases = (
|
||||||
|
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
FD10A7F022414F080027D42C /* Start Packager */,
|
||||||
|
13B07F871A680F5B00A75B9A /* Sources */,
|
||||||
|
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||||
|
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||||
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
|
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
|
||||||
|
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = SelahReactNativeApp;
|
||||||
|
productName = SelahReactNativeApp;
|
||||||
|
productReference = 13B07F961A680F5B00A75B9A /* SelahReactNativeApp.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 1210;
|
||||||
|
TargetAttributes = {
|
||||||
|
00E356ED1AD99517003FC87E = {
|
||||||
|
CreatedOnToolsVersion = 6.2;
|
||||||
|
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||||
|
};
|
||||||
|
13B07F861A680F5B00A75B9A = {
|
||||||
|
LastSwiftMigration = 1120;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SelahReactNativeApp" */;
|
||||||
|
compatibilityVersion = "Xcode 12.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||||
|
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
13B07F861A680F5B00A75B9A /* SelahReactNativeApp */,
|
||||||
|
00E356ED1AD99517003FC87E /* SelahReactNativeAppTests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
00E356EC1AD99517003FC87E /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||||
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"$(SRCROOT)/.xcode.env.local",
|
||||||
|
"$(SRCROOT)/.xcode.env",
|
||||||
|
);
|
||||||
|
name = "Bundle React Native code and images";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
|
||||||
|
};
|
||||||
|
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-SelahReactNativeApp-SelahReactNativeAppTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-SelahReactNativeApp-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp/Pods-SelahReactNativeApp-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SelahReactNativeApp-SelahReactNativeAppTests/Pods-SelahReactNativeApp-SelahReactNativeAppTests-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
FD10A7F022414F080027D42C /* Start Packager */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Start Packager";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
00E356EA1AD99517003FC87E /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
00E356F31AD99517003FC87E /* SelahReactNativeAppTests.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||||
|
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 13B07F861A680F5B00A75B9A /* SelahReactNativeApp */;
|
||||||
|
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
00E356F61AD99517003FC87E /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-SelahReactNativeApp-SelahReactNativeAppTests.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = SelahReactNativeAppTests/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"-ObjC",
|
||||||
|
"-lc++",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SelahReactNativeApp.app/SelahReactNativeApp";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
00E356F71AD99517003FC87E /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-SelahReactNativeApp-SelahReactNativeAppTests.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
INFOPLIST_FILE = SelahReactNativeAppTests/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"-ObjC",
|
||||||
|
"-lc++",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SelahReactNativeApp.app/SelahReactNativeApp";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-SelahReactNativeApp.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = SelahReactNativeApp/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-ObjC",
|
||||||
|
"-lc++",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
|
PRODUCT_NAME = SelahReactNativeApp;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-SelahReactNativeApp.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
INFOPLIST_FILE = SelahReactNativeApp/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-ObjC",
|
||||||
|
"-lc++",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
|
PRODUCT_NAME = SelahReactNativeApp;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
/usr/lib/swift,
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||||
|
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||||
|
"\"$(inherited)\"",
|
||||||
|
);
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
|
"$(OTHER_CFLAGS)",
|
||||||
|
"-DFOLLY_NO_CONFIG",
|
||||||
|
"-DFOLLY_MOBILE=1",
|
||||||
|
"-DFOLLY_USE_LIBCPP=1",
|
||||||
|
);
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
83CBBA211A601CBA00E9B192 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = YES;
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
/usr/lib/swift,
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"\"$(SDKROOT)/usr/lib/swift\"",
|
||||||
|
"\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
|
||||||
|
"\"$(inherited)\"",
|
||||||
|
);
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
|
"$(OTHER_CFLAGS)",
|
||||||
|
"-DFOLLY_NO_CONFIG",
|
||||||
|
"-DFOLLY_MOBILE=1",
|
||||||
|
"-DFOLLY_USE_LIBCPP=1",
|
||||||
|
);
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SelahReactNativeAppTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
00E356F61AD99517003FC87E /* Debug */,
|
||||||
|
00E356F71AD99517003FC87E /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SelahReactNativeApp" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
13B07F941A680F5B00A75B9A /* Debug */,
|
||||||
|
13B07F951A680F5B00A75B9A /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SelahReactNativeApp" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||||
|
83CBBA211A601CBA00E9B192 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1210"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "SelahReactNativeApp.app"
|
||||||
|
BlueprintName = "SelahReactNativeApp"
|
||||||
|
ReferencedContainer = "container:SelahReactNativeApp.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||||
|
BuildableName = "SelahReactNativeAppTests.xctest"
|
||||||
|
BlueprintName = "SelahReactNativeAppTests"
|
||||||
|
ReferencedContainer = "container:SelahReactNativeApp.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "SelahReactNativeApp.app"
|
||||||
|
BlueprintName = "SelahReactNativeApp"
|
||||||
|
ReferencedContainer = "container:SelahReactNativeApp.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
BuildableName = "SelahReactNativeApp.app"
|
||||||
|
BlueprintName = "SelahReactNativeApp"
|
||||||
|
ReferencedContainer = "container:SelahReactNativeApp.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -0,0 +1,6 @@
|
|||||||
|
#import <RCTAppDelegate.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface AppDelegate : RCTAppDelegate
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,36 @@
|
|||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
|
{
|
||||||
|
self.moduleName = @"SelahReactNativeApp";
|
||||||
|
// You can add your custom initial props in the dictionary below.
|
||||||
|
// They will be passed down to the ViewController used by React Native.
|
||||||
|
self.initialProps = @{};
|
||||||
|
|
||||||
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||||
|
#else
|
||||||
|
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
|
||||||
|
///
|
||||||
|
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
|
||||||
|
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
|
||||||
|
/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
|
||||||
|
- (BOOL)concurrentRootEnabled
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "20x20"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "3x",
|
||||||
|
"size" : "20x20"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "29x29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "3x",
|
||||||
|
"size" : "29x29"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "40x40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "3x",
|
||||||
|
"size" : "40x40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "60x60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"scale" : "3x",
|
||||||
|
"size" : "60x60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>SelahReactNativeApp</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string></string>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SelahReactNativeApp" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
||||||
|
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
|
||||||
|
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
|
||||||
|
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
|
||||||
|
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
|
||||||
|
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
|
||||||
|
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
|
||||||
|
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="52.173913043478265" y="375"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
10
frontend/SelahReactNativeApp/ios/SelahReactNativeApp/main.m
Normal file
10
frontend/SelahReactNativeApp/ios/SelahReactNativeApp/main.m
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
@autoreleasepool {
|
||||||
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,66 @@
|
|||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#import <React/RCTLog.h>
|
||||||
|
#import <React/RCTRootView.h>
|
||||||
|
|
||||||
|
#define TIMEOUT_SECONDS 600
|
||||||
|
#define TEXT_TO_LOOK_FOR @"Welcome to React"
|
||||||
|
|
||||||
|
@interface SelahReactNativeAppTests : XCTestCase
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SelahReactNativeAppTests
|
||||||
|
|
||||||
|
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
|
||||||
|
{
|
||||||
|
if (test(view)) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
for (UIView *subview in [view subviews]) {
|
||||||
|
if ([self findSubviewInView:subview matching:test]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRendersWelcomeScreen
|
||||||
|
{
|
||||||
|
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
|
||||||
|
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||||
|
BOOL foundElement = NO;
|
||||||
|
|
||||||
|
__block NSString *redboxError = nil;
|
||||||
|
#ifdef DEBUG
|
||||||
|
RCTSetLogFunction(
|
||||||
|
^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||||
|
if (level >= RCTLogLevelError) {
|
||||||
|
redboxError = message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
|
||||||
|
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
|
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
|
|
||||||
|
foundElement = [self findSubviewInView:vc.view
|
||||||
|
matching:^BOOL(UIView *view) {
|
||||||
|
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||||
|
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
17
frontend/SelahReactNativeApp/metro.config.js
Normal file
17
frontend/SelahReactNativeApp/metro.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Metro configuration for React Native
|
||||||
|
* https://github.com/facebook/react-native
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
transformer: {
|
||||||
|
getTransformOptions: async () => ({
|
||||||
|
transform: {
|
||||||
|
experimentalImportSupport: false,
|
||||||
|
inlineRequires: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
14993
frontend/SelahReactNativeApp/package-lock.json
generated
Normal file
14993
frontend/SelahReactNativeApp/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
frontend/SelahReactNativeApp/package.json
Normal file
36
frontend/SelahReactNativeApp/package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "SelahReactNativeApp",
|
||||||
|
"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": "18.2.0",
|
||||||
|
"react-native": "0.71.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.20.0",
|
||||||
|
"@babel/preset-env": "^7.20.0",
|
||||||
|
"@babel/runtime": "^7.20.0",
|
||||||
|
"@react-native-community/eslint-config": "^3.2.0",
|
||||||
|
"@tsconfig/react-native": "^2.0.2",
|
||||||
|
"@types/jest": "^29.2.1",
|
||||||
|
"@types/react": "^18.0.24",
|
||||||
|
"@types/react-test-renderer": "^18.0.0",
|
||||||
|
"babel-jest": "^29.2.1",
|
||||||
|
"eslint": "^8.19.0",
|
||||||
|
"jest": "^29.2.1",
|
||||||
|
"metro-react-native-babel-preset": "0.73.10",
|
||||||
|
"prettier": "^2.4.1",
|
||||||
|
"react-test-renderer": "18.2.0",
|
||||||
|
"typescript": "4.8.4"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "react-native"
|
||||||
|
}
|
||||||
|
}
|
3
frontend/SelahReactNativeApp/tsconfig.json
Normal file
3
frontend/SelahReactNativeApp/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/react-native/tsconfig.json"
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
nothing
|
|
Loading…
Reference in New Issue
Block a user