This commit is contained in:
2026-02-26 20:49:21 -05:00
parent bebcf48a32
commit 4d9e715361
243 changed files with 25648 additions and 14573 deletions
+51 -25
View File
@@ -1,31 +1,57 @@
# Node # OS
node_modules/ .DS_Store
Thumbs.db
# Shadow-cljs
.shadow-cljs/
.nrepl-port
# Expo / React Native
todo-expo/app/
todo-expo/.expo/
todo-expo/android/
todo-expo/ios/
# Flutter / ClojureDart
todo-flutter/build/
todo-flutter/.clojuredart/
todo-flutter/.dart_tool/
todo-flutter/.cpcache/
todo-flutter/.packages
# Clojure
.cpcache/
# IDE # IDE
.idea/ .idea/
*.iml *.iml
.vscode/ .vscode/
# OS # Node
.DS_Store node_modules/
Thumbs.db npm-debug.*
# Expo / React Native
.expo/
app/
ios/
web-build/
# Shadow-cljs / Clojure
.shadow-cljs/
.nrepl-port
.cpcache/
.lsp/.cache
.lsp/sqlite.db
.clj-kondo/.cache
.calva/
pom.xml
# Flutter / Dart
**/build/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
.packages
**/doc/api/
# ClojureDart
.clojuredart/
lib/cljd-out/
test/cljd-out/
# Gradle / Android
.gradle/
local.properties
gradle/wrapper/gradle-wrapper.jar
captures/
# Kotlin
*.class
# Misc
*.log
*.pyc
*.swp
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -11,7 +11,9 @@ out/
pom.xml pom.xml
.idea/ .idea/
*.iml *.iml
app app/
android/
ios/
.cpcache .cpcache
/web-build /web-build
.calva/output-window/ .calva/output-window/
+35
View File
@@ -0,0 +1,35 @@
{
"expo": {
"name": "Todo App",
"slug": "todo-expo",
"privacy": "public",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"plugins": [
"expo-sqlite"
],
"android": {
"package": "com.anonymous.todoexpo"
}
}
}

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

+7
View File
@@ -0,0 +1,7 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'],
};
};
+42
View File
@@ -0,0 +1,42 @@
// Disable all Expo/Metro hot reload mechanisms BEFORE loading app
// This ensures only shadow-cljs hot reload remains active
if (typeof window !== 'undefined') {
// Disable React Fast Refresh
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => type => type;
// Disable Metro hot update
window.metroHotUpdateModule = () => {};
window.__accept = () => {};
// Disable webpack hot update (fallback)
window.webpackHotUpdate = () => {};
// Override WebSocket to block Metro connections
// const OriginalWebSocket = window.WebSocket;
// window.WebSocket = function(url) {
// if (url && (url.includes('metro') ||
// url.includes('packager') ||
// url.includes('19001') ||
// url.includes('19000') ||
// url.includes('8081'))) {
// console.log('Blocked Metro WebSocket connection:', url);
// return {
// send: () => {},
// close: () => {},
// addEventListener: () => {},
// removeEventListener: () => {}
// };
// }
// return new OriginalWebSocket(url);
// };
// Prevent module.hot usage
if (typeof module !== 'undefined' && module.hot) {
delete module.hot;
}
console.log('Expo/Metro hot reload disabled - shadow-cljs will handle hot reload');
}
import './app/index.js';
+20655
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
{
"scripts": {
"server": "npx shadow-cljs server",
"start": "npx expo start",
"android": "npx expo start --android",
"ios": "npx expo start --ios",
"web": "npx expo start --web",
"web-only": "npx expo start --web-only",
"release": "npx shadow-cljs release app",
"eject": "npx expo eject",
"eas-build-pre-install": "bash eas-build-pre-install.sh",
"eas-build-post-install": "npx shadow-cljs release app"
},
"dependencies": {
"@expo/vector-icons": "^15.0.2",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"babel-preset-expo": "^55.0.8",
"create-react-class": "^15.7.0",
"expo": "^55.0.2",
"expo-cli": "^6.3.8",
"expo-sqlite": "~55.0.10",
"expo-status-bar": "~55.0.4",
"react": "19.2.0",
"react-dom": "19.2.0",
"react-native": "0.83.2",
"react-native-gesture-handler": "~2.30.0",
"react-native-reanimated": "4.2.1",
"react-native-safe-area-context": "~5.6.2",
"react-native-screens": "~4.23.0",
"react-native-web": "^0.21.0"
},
"devDependencies": {
"@babel/plugin-transform-class-properties": "^7.28.6",
"@babel/plugin-transform-private-methods": "^7.28.6",
"@babel/plugin-transform-private-property-in-object": "^7.28.6",
"shadow-cljs": "^2.23.3"
},
"private": true
}

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

@@ -66,7 +66,7 @@
[:> rn/View {:style {:background-color (:primary theme/colors) [:> rn/View {:style {:background-color (:primary theme/colors)
:padding-top 48 :padding-bottom 16 :padding-horizontal 24}} :padding-top 48 :padding-bottom 16 :padding-horizontal 24}}
[:> rn/Text {:style {:font-size 28 :font-weight "bold" :color (:on-primary theme/colors)}} [:> rn/Text {:style {:font-size 28 :font-weight "bold" :color (:on-primary theme/colors)}}
"My Todos"]] "Todos CLJS Expo"]]
[filter-bar] [filter-bar]
[todo-list] [todo-list]
[fab]]) [fab]])
+16
View File
@@ -0,0 +1,16 @@
# OSX
#
.DS_Store
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
.cxx/
# Bundle artifacts
*.jsbundle
+24
View File
@@ -0,0 +1,24 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath('com.android.tools.build:gradle')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
}
apply plugin: "expo-root-project"
apply plugin: "com.facebook.react.rootproject"
+61
View File
@@ -0,0 +1,61 @@
# 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
# Enable AAPT2 PNG crunching
android.enablePngCrunchInReleaseBuilds=true
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=true
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=true
# Enable GIF support in React Native images (~200 B increase)
expo.gif.enabled=true
# Enable webp support in React Native images (~85 KB increase)
expo.webp.enabled=true
# Enable animated webp support (~3.4 MB increase)
# Disabled by default because iOS doesn't support animated webp
expo.webp.animated=false
# Enable network inspector
EX_DEV_CLIENT_NETWORK_INSPECTOR=true
# Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false
Binary file not shown.
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+251
View File
@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015 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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# 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/HEAD/platforms/jvm/plugins-application/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
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# 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="\\\"\\\""
# 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
if ! command -v java >/dev/null 2>&1
then
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
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
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
# 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"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# 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" "$@"
+94
View File
@@ -0,0 +1,94 @@
@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
@rem SPDX-License-Identifier: Apache-2.0
@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=.
@rem This is normally unused
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% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 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!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+39
View File
@@ -0,0 +1,39 @@
pluginManagement {
def reactNativeGradlePlugin = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })")
}.standardOutput.asText.get().trim()
).getParentFile().absolutePath
includeBuild(reactNativeGradlePlugin)
def expoPluginsPath = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })")
}.standardOutput.asText.get().trim(),
"../android/expo-gradle-plugin"
).absolutePath
includeBuild(expoPluginsPath)
}
plugins {
id("com.facebook.react.settings")
id("expo-autolinking-settings")
}
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
ex.autolinkLibrariesFromCommand()
} else {
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
}
}
expoAutolinking.useExpoModules()
rootProject.name = 'Todo App'
expoAutolinking.useExpoVersionCatalog()
include ':app'
includeBuild(expoAutolinking.reactNativeGradlePlugin)
+4 -25
View File
@@ -1,35 +1,14 @@
{ {
"expo": { "expo": {
"name": "Todo App", "name": "Todo App",
"slug": "todo-expo", "slug": "todo-expo-js",
"privacy": "public", "privacy": "public",
"platforms": [ "platforms": ["ios", "android"],
"ios",
"android",
"web"
],
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/icon.png", "plugins": ["expo-sqlite"],
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"plugins": [
"expo-sqlite"
],
"android": { "android": {
"package": "com.anonymous.todoexpo" "package": "com.anonymous.todoexpojs"
} }
} }
} }
Executable → Regular
View File
+3 -41
View File
@@ -1,42 +1,4 @@
// Disable all Expo/Metro hot reload mechanisms BEFORE loading app import { registerRootComponent } from 'expo';
// This ensures only shadow-cljs hot reload remains active import App from './src/App';
if (typeof window !== 'undefined') {
// Disable React Fast Refresh
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => type => type;
// Disable Metro hot update
window.metroHotUpdateModule = () => {};
window.__accept = () => {};
// Disable webpack hot update (fallback)
window.webpackHotUpdate = () => {};
// Override WebSocket to block Metro connections
// const OriginalWebSocket = window.WebSocket;
// window.WebSocket = function(url) {
// if (url && (url.includes('metro') ||
// url.includes('packager') ||
// url.includes('19001') ||
// url.includes('19000') ||
// url.includes('8081'))) {
// console.log('Blocked Metro WebSocket connection:', url);
// return {
// send: () => {},
// close: () => {},
// addEventListener: () => {},
// removeEventListener: () => {}
// };
// }
// return new OriginalWebSocket(url);
// };
// Prevent module.hot usage
if (typeof module !== 'undefined' && module.hot) {
delete module.hot;
}
console.log('Expo/Metro hot reload disabled - shadow-cljs will handle hot reload');
}
import './app/index.js'; registerRootComponent(App);
+1086 -14345
View File
File diff suppressed because it is too large Load Diff
+8 -23
View File
@@ -1,40 +1,25 @@
{ {
"name": "todo-expo",
"version": "1.0.0",
"private": true,
"main": "index.js",
"scripts": { "scripts": {
"server": "npx shadow-cljs server",
"start": "npx expo start", "start": "npx expo start",
"android": "npx expo start --android", "android": "npx expo start --android",
"ios": "npx expo start --ios", "ios": "npx expo start --ios"
"web": "npx expo start --web",
"web-only": "npx expo start --web-only",
"release": "npx shadow-cljs release app",
"eject": "npx expo eject",
"eas-build-pre-install": "bash eas-build-pre-install.sh",
"eas-build-post-install": "npx shadow-cljs release app"
}, },
"dependencies": { "dependencies": {
"@expo/vector-icons": "^15.0.2",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"babel-preset-expo": "^55.0.8",
"create-react-class": "^15.7.0",
"expo": "^55.0.2", "expo": "^55.0.2",
"expo-cli": "^6.3.8",
"expo-sqlite": "~55.0.10", "expo-sqlite": "~55.0.10",
"expo-status-bar": "~55.0.4", "expo-status-bar": "~55.0.4",
"react": "19.2.0", "react": "19.2.0",
"react-dom": "19.2.0",
"react-native": "0.83.2", "react-native": "0.83.2",
"react-native-gesture-handler": "~2.30.0", "react-native-gesture-handler": "~2.30.0",
"react-native-reanimated": "4.2.1", "react-native-reanimated": "4.2.1",
"react-native-safe-area-context": "~5.6.2", "react-native-safe-area-context": "~5.6.2",
"react-native-screens": "~4.23.0", "react-native-screens": "~4.23.0"
"react-native-web": "^0.21.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-transform-class-properties": "^7.28.6", "babel-preset-expo": "^55.0.8"
"@babel/plugin-transform-private-methods": "^7.28.6", }
"@babel/plugin-transform-private-property-in-object": "^7.28.6",
"shadow-cljs": "^2.23.3"
},
"private": true
} }
+43
View File
@@ -0,0 +1,43 @@
import React, { useEffect } from 'react';
import { LogBox, StatusBar } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
LogBox.ignoreAllLogs();
import { TodoProvider, useTodoState } from './state';
import * as db from './db';
import MainScreen from './components/MainScreen';
import TodoForm from './components/TodoForm';
function AppContent() {
const { state, dispatch } = useTodoState();
useEffect(() => {
(async () => {
try {
await db.initDb();
const todos = await db.getAllTodos();
dispatch({ type: 'TODOS_LOADED', rows: todos });
} catch (err) {
console.error('SQLite init error:', err);
}
})();
}, []);
return (
<>
<StatusBar barStyle="light-content" backgroundColor="#6200EE" />
<MainScreen />
{state.showForm && <TodoForm />}
</>
);
}
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<TodoProvider>
<AppContent />
</TodoProvider>
</GestureHandlerRootView>
);
}
@@ -0,0 +1,49 @@
import React from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { categories } from '../state';
import { colors } from '../theme';
export default function CategoryPicker({ selected, onSelect }) {
return (
<View style={styles.container}>
{categories.map(cat => {
const isSelected = cat === selected;
return (
<TouchableOpacity
key={cat}
onPress={() => onSelect(cat)}
style={[
styles.pill,
{
backgroundColor: isSelected ? colors.primary : colors.border,
},
]}
>
<Text
style={{
color: isSelected ? colors.onPrimary : colors.onSurface,
fontSize: 14,
}}
>
{cat}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
marginVertical: 8,
},
pill: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
});
+59
View File
@@ -0,0 +1,59 @@
import React from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { useTodoState, getTodoCounts } from '../state';
import { colors } from '../theme';
const filters = [
{ key: 'all', label: 'All' },
{ key: 'active', label: 'Active' },
{ key: 'completed', label: 'Done' },
];
export default function FilterBar() {
const { state, dispatch } = useTodoState();
const counts = getTodoCounts(state);
return (
<View style={styles.container}>
{filters.map(({ key, label }) => {
const isActive = state.filter === key;
return (
<TouchableOpacity
key={key}
onPress={() => dispatch({ type: 'SET_FILTER', filter: key })}
style={[
styles.pill,
{ backgroundColor: isActive ? colors.primary : colors.border },
]}
>
<Text
style={[
styles.label,
{ color: isActive ? colors.onPrimary : colors.textSecondary },
]}
>
{label} ({counts[key] || 0})
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
paddingHorizontal: 16,
paddingVertical: 8,
gap: 8,
},
pill: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
label: {
fontWeight: '600',
},
});
+110
View File
@@ -0,0 +1,110 @@
import React from 'react';
import { View, Text, TouchableOpacity, Pressable, FlatList, StyleSheet } from 'react-native';
import { useTodoState, getFilteredTodos } from '../state';
import { colors } from '../theme';
import FilterBar from './FilterBar';
import TodoItem from './TodoItem';
function EmptyState() {
return (
<View style={styles.emptyContainer}>
<Text style={styles.emptyIcon}>{'\uD83D\uDCDD'}</Text>
<Text style={styles.emptyText}>{'No todos yet!\nTap + to add one'}</Text>
</View>
);
}
function TodoList() {
const { state } = useTodoState();
const todos = getFilteredTodos(state);
if (todos.length === 0) {
return <EmptyState />;
}
return (
<FlatList
data={todos}
renderItem={({ item }) => <TodoItem todo={item} />}
keyExtractor={item => String(item.id)}
contentContainerStyle={{ paddingBottom: 100 }}
/>
);
}
export default function MainScreen() {
const { dispatch } = useTodoState();
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Todos JS Expo</Text>
</View>
<FilterBar />
<View style={{ flex: 1 }}>
<TodoList />
</View>
<Pressable
onPress={() => dispatch({ type: 'SHOW_ADD_FORM' })}
style={styles.fab}
>
<Text style={styles.fabText}>+</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
header: {
backgroundColor: colors.primary,
paddingTop: 48,
paddingBottom: 16,
paddingHorizontal: 24,
},
headerText: {
fontSize: 28,
fontWeight: 'bold',
color: colors.onPrimary,
},
fab: {
position: 'absolute',
right: 24,
bottom: 32,
zIndex: 10,
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: colors.primary,
justifyContent: 'center',
alignItems: 'center',
elevation: 6,
shadowColor: '#000',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.3,
shadowRadius: 4,
},
fabText: {
color: '#FFFFFF',
fontSize: 28,
lineHeight: 30,
},
emptyContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 48,
},
emptyIcon: {
fontSize: 48,
marginBottom: 16,
},
emptyText: {
fontSize: 18,
color: colors.textSecondary,
textAlign: 'center',
},
});
@@ -0,0 +1,50 @@
import React from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { priorities } from '../state';
export default function PriorityPicker({ selected, onSelect }) {
return (
<View style={styles.container}>
{priorities.map(({ key, label, color }) => {
const isSelected = key === selected;
return (
<TouchableOpacity
key={key}
onPress={() => onSelect(key)}
style={[
styles.pill,
{
borderColor: color,
backgroundColor: isSelected ? color : 'transparent',
},
]}
>
<Text
style={{
color: isSelected ? '#FFFFFF' : color,
fontWeight: '600',
fontSize: 14,
}}
>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
gap: 8,
marginVertical: 8,
},
pill: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
borderWidth: 2,
},
});
+156
View File
@@ -0,0 +1,156 @@
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
} from 'react-native';
import { useTodoState } from '../state';
import { colors } from '../theme';
import * as db from '../db';
import CategoryPicker from './CategoryPicker';
import PriorityPicker from './PriorityPicker';
export default function TodoForm() {
const { state, dispatch } = useTodoState();
const editing = state.editingTodo;
const [title, setTitle] = useState(editing?.title || '');
const [category, setCategory] = useState(editing?.category || 'Personal');
const [priority, setPriority] = useState(editing?.priority || 'medium');
const handleSubmit = async () => {
const trimmed = title.trim();
if (!trimmed) return;
if (editing) {
dispatch({
type: 'UPDATE_TODO',
id: editing.id,
title: trimmed,
category,
priority,
});
db.updateTodo(editing.id, trimmed, category, priority).catch(err =>
console.error('Update error:', err)
);
} else {
try {
const id = await db.insertTodo(trimmed, category, priority);
dispatch({
type: 'TODO_INSERTED',
id,
title: trimmed,
category,
priority,
});
} catch (err) {
console.error('Insert error:', err);
}
}
};
const handleCancel = () => {
dispatch({ type: 'HIDE_FORM' });
};
return (
<View style={styles.overlay}>
<View style={styles.card}>
<Text style={styles.heading}>
{editing ? 'Edit Todo' : 'Add New Todo'}
</Text>
<TextInput
style={styles.input}
placeholder="What needs to be done?"
placeholderTextColor={colors.textSecondary}
value={title}
onChangeText={setTitle}
autoFocus
/>
<Text style={styles.sectionLabel}>Category</Text>
<CategoryPicker selected={category} onSelect={setCategory} />
<Text style={[styles.sectionLabel, { marginTop: 8 }]}>Priority</Text>
<PriorityPicker selected={priority} onSelect={setPriority} />
<View style={styles.actions}>
<TouchableOpacity onPress={handleCancel} style={styles.cancelBtn}>
<Text style={{ color: colors.textSecondary, fontSize: 16 }}>
Cancel
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={handleSubmit} style={styles.submitBtn}>
<Text style={styles.submitText}>
{editing ? 'Save' : 'Add'}
</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
overlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'center',
padding: 24,
},
card: {
backgroundColor: colors.surface,
borderRadius: 16,
padding: 24,
elevation: 8,
},
heading: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
color: colors.onSurface,
},
input: {
borderWidth: 1,
borderColor: colors.border,
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 16,
},
sectionLabel: {
fontSize: 14,
fontWeight: '600',
color: colors.textSecondary,
marginBottom: 4,
},
actions: {
flexDirection: 'row',
justifyContent: 'flex-end',
gap: 12,
marginTop: 24,
},
cancelBtn: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
},
submitBtn: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
backgroundColor: colors.primary,
},
submitText: {
color: colors.onPrimary,
fontSize: 16,
fontWeight: '600',
},
});
+156
View File
@@ -0,0 +1,156 @@
import React from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import { useTodoState } from '../state';
import { colors, priorityColors } from '../theme';
import * as db from '../db';
export default function TodoItem({ todo }) {
const { dispatch } = useTodoState();
const { id, title, completed, category, priority } = todo;
const handleToggle = () => {
dispatch({ type: 'TOGGLE_TODO', id });
db.toggleTodo(id, !completed).catch(err =>
console.error('Toggle error:', err)
);
};
const handleEdit = () => {
dispatch({ type: 'SHOW_EDIT_FORM', todo });
};
const handleDelete = () => {
dispatch({ type: 'DELETE_TODO', id });
db.deleteTodo(id).catch(err => console.error('Delete error:', err));
};
const renderRightActions = () => (
<TouchableOpacity onPress={handleDelete} style={styles.deleteButton}>
<Text style={styles.deleteText}>Delete</Text>
</TouchableOpacity>
);
return (
<Swipeable renderRightActions={renderRightActions} overshootRight={false}>
<TouchableOpacity
onPress={handleEdit}
activeOpacity={0.7}
style={[
styles.container,
{
backgroundColor: completed ? colors.completedBg : colors.surface,
},
]}
>
<TouchableOpacity onPress={handleToggle} style={[
styles.checkbox,
{
borderColor: completed ? colors.primary : colors.border,
backgroundColor: completed ? colors.primary : 'transparent',
},
]}>
{completed && <Text style={styles.checkmark}>{'\u2713'}</Text>}
</TouchableOpacity>
<View style={styles.content}>
<Text
style={[
styles.title,
{
color: completed ? colors.completedText : colors.onSurface,
textDecorationLine: completed ? 'line-through' : 'none',
},
]}
>
{title}
</Text>
<View style={styles.meta}>
<View style={styles.categoryBadge}>
<Text style={styles.categoryText}>{category}</Text>
</View>
<View
style={[
styles.priorityDot,
{
backgroundColor:
priorityColors[priority] || '#999',
},
]}
/>
</View>
</View>
</TouchableOpacity>
</Swipeable>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
marginHorizontal: 16,
marginVertical: 4,
padding: 16,
borderRadius: 12,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
checkbox: {
width: 28,
height: 28,
borderRadius: 14,
borderWidth: 2,
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
},
checkmark: {
color: '#FFFFFF',
fontSize: 16,
},
content: {
flex: 1,
},
title: {
fontSize: 16,
},
meta: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 4,
gap: 8,
},
categoryBadge: {
backgroundColor: '#E0E0E0',
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 10,
},
categoryText: {
fontSize: 12,
color: '#666666',
},
priorityDot: {
width: 10,
height: 10,
borderRadius: 5,
},
deleteButton: {
backgroundColor: '#B00020',
justifyContent: 'center',
alignItems: 'center',
width: 80,
borderRadius: 12,
marginVertical: 4,
marginRight: 16,
},
deleteText: {
color: '#FFFFFF',
fontWeight: 'bold',
fontSize: 14,
},
});
+65
View File
@@ -0,0 +1,65 @@
import * as SQLite from 'expo-sqlite';
let db = null;
export async function openDb() {
db = await SQLite.openDatabaseAsync('todos.db');
return db;
}
export async function initDb() {
if (!db) await openDb();
await db.execAsync(`
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
completed INTEGER DEFAULT 0,
category TEXT DEFAULT 'Personal',
priority TEXT DEFAULT 'medium',
created_at INTEGER DEFAULT (strftime('%s','now'))
);
`);
}
export async function getAllTodos() {
if (!db) return [];
const rows = await db.getAllAsync('SELECT * FROM todos ORDER BY created_at DESC');
return rows.map(row => ({
id: row.id,
title: row.title,
completed: row.completed === 1,
category: row.category,
priority: row.priority,
createdAt: row.created_at,
}));
}
export async function insertTodo(title, category, priority) {
if (!db) return null;
const result = await db.runAsync(
'INSERT INTO todos (title, category, priority) VALUES (?, ?, ?)',
[title, category, priority]
);
return result.lastInsertRowId;
}
export async function updateTodo(id, title, category, priority) {
if (!db) return;
await db.runAsync(
'UPDATE todos SET title = ?, category = ?, priority = ? WHERE id = ?',
[title, category, priority, id]
);
}
export async function toggleTodo(id, completed) {
if (!db) return;
await db.runAsync(
'UPDATE todos SET completed = ? WHERE id = ?',
[completed ? 1 : 0, id]
);
}
export async function deleteTodo(id) {
if (!db) return;
await db.runAsync('DELETE FROM todos WHERE id = ?', [id]);
}
+123
View File
@@ -0,0 +1,123 @@
import React, { createContext, useContext, useReducer } from 'react';
export const categories = ['Personal', 'Work', 'Shopping', 'Health', 'Other'];
export const priorities = [
{ key: 'low', label: 'Low', color: '#4CAF50' },
{ key: 'medium', label: 'Medium', color: '#FF9800' },
{ key: 'high', label: 'High', color: '#F44336' },
];
const initialState = {
todos: {},
filter: 'all',
editingTodo: null,
showForm: false,
};
function reducer(state, action) {
switch (action.type) {
case 'TODOS_LOADED': {
const todos = {};
action.rows.forEach(row => {
todos[row.id] = row;
});
return { ...state, todos };
}
case 'TODO_INSERTED': {
const { id, title, category, priority } = action;
return {
...state,
showForm: false,
todos: {
...state.todos,
[id]: {
id,
title,
completed: false,
category,
priority,
createdAt: Math.floor(Date.now() / 1000),
},
},
};
}
case 'TOGGLE_TODO': {
const todo = state.todos[action.id];
if (!todo) return state;
return {
...state,
todos: {
...state.todos,
[action.id]: { ...todo, completed: !todo.completed },
},
};
}
case 'UPDATE_TODO': {
const { id, title, category, priority } = action;
const existing = state.todos[id];
if (!existing) return state;
return {
...state,
showForm: false,
editingTodo: null,
todos: {
...state.todos,
[id]: { ...existing, title, category, priority },
},
};
}
case 'DELETE_TODO': {
const { [action.id]: _, ...rest } = state.todos;
return { ...state, todos: rest };
}
case 'SET_FILTER':
return { ...state, filter: action.filter };
case 'SHOW_ADD_FORM':
return { ...state, showForm: true, editingTodo: null };
case 'SHOW_EDIT_FORM':
return { ...state, showForm: true, editingTodo: action.todo };
case 'HIDE_FORM':
return { ...state, showForm: false, editingTodo: null };
default:
return state;
}
}
const TodoContext = createContext(null);
export function TodoProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<TodoContext.Provider value={{ state, dispatch }}>
{children}
</TodoContext.Provider>
);
}
export function useTodoState() {
return useContext(TodoContext);
}
export function getFilteredTodos(state) {
const todoList = Object.values(state.todos).sort(
(a, b) => (b.createdAt || 0) - (a.createdAt || 0)
);
switch (state.filter) {
case 'active':
return todoList.filter(t => !t.completed);
case 'completed':
return todoList.filter(t => t.completed);
default:
return todoList;
}
}
export function getTodoCounts(state) {
const all = Object.values(state.todos);
return {
all: all.length,
active: all.filter(t => !t.completed).length,
completed: all.filter(t => t.completed).length,
};
}
+29
View File
@@ -0,0 +1,29 @@
export const colors = {
primary: '#6200EE',
primaryDark: '#3700B3',
secondary: '#03DAC6',
background: '#F5F5F5',
surface: '#FFFFFF',
error: '#B00020',
onPrimary: '#FFFFFF',
onSurface: '#000000',
onBackground: '#000000',
textSecondary: '#666666',
border: '#E0E0E0',
completedText: '#999999',
completedBg: '#F0F0F0',
};
export const priorityColors = {
low: '#4CAF50',
medium: '#FF9800',
high: '#F44336',
};
export const spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
};
+51
View File
@@ -0,0 +1,51 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# ClojureDart
.cpcache/
.clojuredart/
lib/cljd-out/
test/cljd-out/
+45
View File
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "d8a9f9a52e5af486f80d932e838ee93861ffd863"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: android
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: ios
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: linux
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: macos
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: web
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: windows
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
+16
View File
@@ -0,0 +1,16 @@
# cljd_todo_flutter
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+28
View File
@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
+13
View File
@@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 442 B

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 721 B

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

+18
View File
@@ -0,0 +1,18 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
+25
View File
@@ -0,0 +1,25 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.2.1" apply false
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
}
include ":app"

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