Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f66a59372 |
@@ -1,3 +0,0 @@
|
|||||||
[submodule "Backend"]
|
|
||||||
path = Backend
|
|
||||||
url = https://github.com/VergeDX/clock_api
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
HELP.md
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("org.springframework.boot") version "2.4.1"
|
||||||
|
id("io.spring.dependency-management") version "1.0.10.RELEASE"
|
||||||
|
kotlin("jvm") version "1.4.21"
|
||||||
|
kotlin("plugin.spring") version "1.4.21"
|
||||||
|
// kotlin("plugin.jpa") version "1.4.21"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "org.hydev.ios"
|
||||||
|
version = "0.0.1.1"
|
||||||
|
java.sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
implementation("org.springframework.boot", "spring-boot-starter-validation", "2.4.1")
|
||||||
|
|
||||||
|
// MongoDB
|
||||||
|
implementation(group = "org.springframework.data", name = "spring-data-mongodb", version = "3.1.2")
|
||||||
|
implementation(group = "org.springframework.boot", name = "spring-boot-starter-data-mongodb")
|
||||||
|
|
||||||
|
// MariaDB
|
||||||
|
// implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
|
// runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<KotlinCompile> {
|
||||||
|
kotlinOptions {
|
||||||
|
freeCompilerArgs = listOf("-Xjsr305=strict")
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test> {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or 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 UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$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 "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
;;
|
||||||
|
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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = "alarm-clock"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.hydev.ios.alarmclock
|
||||||
|
|
||||||
|
import org.hydev.ios.alarmclock.data.UserRepo
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.runApplication
|
||||||
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableMongoRepositories(basePackageClasses = [UserRepo::class])
|
||||||
|
class AlarmClockApplication
|
||||||
|
|
||||||
|
fun main(args: Array<String>)
|
||||||
|
{
|
||||||
|
runApplication<AlarmClockApplication>(*args)
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.hydev.ios.alarmclock
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.security.spec.KeySpec
|
||||||
|
import java.util.*
|
||||||
|
import javax.crypto.SecretKeyFactory
|
||||||
|
import javax.crypto.spec.PBEKeySpec
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate "Bad Request" response entity
|
||||||
|
*
|
||||||
|
* @param msg Message
|
||||||
|
* @return Response entity
|
||||||
|
*/
|
||||||
|
fun bad(msg: String): ResponseEntity<String> = ResponseEntity.badRequest().body(msg)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate random salt
|
||||||
|
*
|
||||||
|
* @param len Length of the salt in bytes
|
||||||
|
* @return Random byte array of size len
|
||||||
|
*/
|
||||||
|
fun randSalt(len: Int = 16): ByteArray
|
||||||
|
{
|
||||||
|
val random = SecureRandom()
|
||||||
|
val salt = ByteArray(len)
|
||||||
|
random.nextBytes(salt)
|
||||||
|
return salt
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a password
|
||||||
|
*
|
||||||
|
* @return <Hash, Salt>
|
||||||
|
*/
|
||||||
|
fun String.passwordHash(salt: String = randSalt().base64): Pair<String, String>
|
||||||
|
{
|
||||||
|
val spec: KeySpec = PBEKeySpec(toCharArray(), salt.base64, 32767, 512)
|
||||||
|
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
|
||||||
|
return Pair(factory.generateSecret(spec).encoded.base64, salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ByteArray.base64: String
|
||||||
|
get() = Base64.getEncoder().encodeToString(this)
|
||||||
|
|
||||||
|
val String.base64: ByteArray
|
||||||
|
get() = Base64.getDecoder().decode(this)
|
||||||
|
|
||||||
|
|
||||||
|
fun main(args: Array<String>)
|
||||||
|
{
|
||||||
|
val (hash, salt) = "password".passwordHash()
|
||||||
|
println(hash)
|
||||||
|
println(salt)
|
||||||
|
val (hash2, salt2) = "password".passwordHash(salt)
|
||||||
|
assert(hash == hash2 && salt == salt2)
|
||||||
|
println("Hash matches")
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.hydev.ios.alarmclock.data
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write a description for this class!
|
||||||
|
*
|
||||||
|
* @author HyDEV Team (https://github.com/HyDevelop)
|
||||||
|
* @author Hykilpikonna (https://github.com/hykilpikonna)
|
||||||
|
* @author Vanilla (https://github.com/VergeDX)
|
||||||
|
* @since 2021-01-12 09:28
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
@GetMapping("/echo")
|
||||||
|
fun echo(message: String?) = message
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package org.hydev.ios.alarmclock.data
|
||||||
|
|
||||||
|
import org.hydev.ios.alarmclock.bad
|
||||||
|
import org.hydev.ios.alarmclock.passwordHash
|
||||||
|
import org.springframework.data.annotation.Id
|
||||||
|
import org.springframework.data.domain.Example
|
||||||
|
import org.springframework.data.domain.ExampleMatcher
|
||||||
|
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.ignoreCase
|
||||||
|
import org.springframework.data.mongodb.core.index.Indexed
|
||||||
|
import org.springframework.data.mongodb.core.mapping.Document
|
||||||
|
import org.springframework.data.mongodb.repository.MongoRepository
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import javax.validation.constraints.Email
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The database model for an user
|
||||||
|
*
|
||||||
|
* @author HyDEV Team (https://github.com/HyDevelop)
|
||||||
|
* @author Hykilpikonna (https://github.com/hykilpikonna)
|
||||||
|
* @author Vanilla (https://github.com/VergeDX)
|
||||||
|
* @since 2021-01-09 10:48
|
||||||
|
*/
|
||||||
|
@Document(collation = "user")
|
||||||
|
data class User(
|
||||||
|
@Id
|
||||||
|
var id: Long = 0,
|
||||||
|
|
||||||
|
var name: String,
|
||||||
|
|
||||||
|
@Indexed(unique = true)
|
||||||
|
var email: String,
|
||||||
|
|
||||||
|
var passHash: String = "",
|
||||||
|
|
||||||
|
var passSalt: String = ""
|
||||||
|
)
|
||||||
|
{
|
||||||
|
constructor(name: String, email: String, pass: String) : this(name = name, email = email)
|
||||||
|
{
|
||||||
|
val (h, s) = pass.passwordHash()
|
||||||
|
passHash = h
|
||||||
|
passSalt = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserRepo: MongoRepository<User, String>
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/user")
|
||||||
|
class UserApi(val repo: UserRepo)
|
||||||
|
{
|
||||||
|
val em = ExampleMatcher.matching().withIgnorePaths("id", "passHash", "passSalt", "name").withMatcher("email", ignoreCase())
|
||||||
|
|
||||||
|
@GetMapping("/register")
|
||||||
|
fun register(@RequestParam name: String, @RequestParam pass: String, @RequestParam @Email email: String): Any
|
||||||
|
{
|
||||||
|
// Check name length
|
||||||
|
if (name.length !in 1..32) return bad("Name length not in range 1 to 32")
|
||||||
|
|
||||||
|
// Check if email exists
|
||||||
|
val user = User(name, email, pass)
|
||||||
|
if (repo.exists(Example.of(user, em))) return bad("Email is already registered")
|
||||||
|
|
||||||
|
// Check password strength
|
||||||
|
if (pass.length < 8) return bad("Password must be longer than 8 chars")
|
||||||
|
|
||||||
|
// Register
|
||||||
|
repo.save(user)
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/delete")
|
||||||
|
fun delete(@RequestParam email: String, @RequestParam pass: String): Any
|
||||||
|
{
|
||||||
|
// Check if username exists
|
||||||
|
val users = repo.findAll(Example.of(User("", email, pass), em))
|
||||||
|
if (users.isEmpty()) return bad("User doesn't exist")
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
users.forEach { repo.delete(it) }
|
||||||
|
return "Deleted"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
#spring.datasource.url=jdbc:mariadb://192.168.0.22:3306/clock
|
||||||
|
#spring.datasource.username=root
|
||||||
|
#spring.datasource.password=<insert password here>
|
||||||
|
#spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
||||||
|
#spring.jpa.hibernate.ddl-auto=update
|
||||||
|
|
||||||
|
spring.data.mongodb.uri=mongodb://hykilp:<insert-password-here>@192.168.0.22:27017/?authSource=admin&retryWrites=true
|
||||||
|
spring.data.mongodb.database=clock
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.hydev.ios.alarmclock
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class AlarmClockApplicationTests
|
||||||
|
{
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun contextLoads()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1230"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "4FF0683A25A5F18700304E6B"
|
|
||||||
BuildableName = "GetGoing.app"
|
|
||||||
BlueprintName = "GetGoing"
|
|
||||||
ReferencedContainer = "container:GetGoing.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</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 = "4FF0683A25A5F18700304E6B"
|
|
||||||
BuildableName = "GetGoing.app"
|
|
||||||
BlueprintName = "GetGoing"
|
|
||||||
ReferencedContainer = "container:GetGoing.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "4FF0683A25A5F18700304E6B"
|
|
||||||
BuildableName = "GetGoing.app"
|
|
||||||
BlueprintName = "GetGoing"
|
|
||||||
ReferencedContainer = "container:GetGoing.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 Hykilpikonna
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -9,49 +9,41 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
4F509BD225AE22D100726227 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F509BD125AE22D100726227 /* Models.swift */; };
|
4F509BD225AE22D100726227 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F509BD125AE22D100726227 /* Models.swift */; };
|
||||||
4F8A607125A9160400D88DC3 /* AddAlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */; };
|
4F8A607125A9160400D88DC3 /* AddAlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */; };
|
||||||
|
4F8A607525A919E600D88DC3 /* Logic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8A607425A919E600D88DC3 /* Logic.swift */; };
|
||||||
4F98955225A9260400F51321 /* Net.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F98955125A9260400F51321 /* Net.swift */; };
|
4F98955225A9260400F51321 /* Net.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F98955125A9260400F51321 /* Net.swift */; };
|
||||||
4FA419AF25AF93EC004CE0FC /* AlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */; };
|
4FA419AF25AF93EC004CE0FC /* AlarmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */; };
|
||||||
4FD642D325B48C380069171E /* AlarmActivator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD642D225B48C380069171E /* AlarmActivator.swift */; };
|
|
||||||
4FD642DB25B4B7F60069171E /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD642DA25B4B7F60069171E /* Utils.swift */; };
|
|
||||||
4FD642E025B4D5F30069171E /* AlarmActivationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD642DF25B4D5F30069171E /* AlarmActivationViewController.swift */; };
|
|
||||||
4FF0683F25A5F18700304E6B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0683E25A5F18700304E6B /* AppDelegate.swift */; };
|
4FF0683F25A5F18700304E6B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0683E25A5F18700304E6B /* AppDelegate.swift */; };
|
||||||
4FF0684325A5F18700304E6B /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0684225A5F18700304E6B /* Account.swift */; };
|
4FF0684125A5F18700304E6B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0684025A5F18700304E6B /* SceneDelegate.swift */; };
|
||||||
|
4FF0684325A5F18700304E6B /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF0684225A5F18700304E6B /* AccountViewController.swift */; };
|
||||||
4FF0684625A5F18700304E6B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684425A5F18700304E6B /* Main.storyboard */; };
|
4FF0684625A5F18700304E6B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684425A5F18700304E6B /* Main.storyboard */; };
|
||||||
4FF0684825A5F18800304E6B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684725A5F18800304E6B /* Assets.xcassets */; };
|
4FF0684825A5F18800304E6B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684725A5F18800304E6B /* Assets.xcassets */; };
|
||||||
4FF0684B25A5F18800304E6B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684925A5F18800304E6B /* LaunchScreen.storyboard */; };
|
4FF0684B25A5F18800304E6B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4FF0684925A5F18800304E6B /* LaunchScreen.storyboard */; };
|
||||||
7C12BC7825BE25C000E5659C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C12BC7725BE25C000E5659C /* Notification.swift */; };
|
|
||||||
7C5DAE9C25AF812200E44C52 /* clock.png in Resources */ = {isa = PBXBuildFile; fileRef = 7C5DAE9B25AF812200E44C52 /* clock.png */; };
|
7C5DAE9C25AF812200E44C52 /* clock.png in Resources */ = {isa = PBXBuildFile; fileRef = 7C5DAE9B25AF812200E44C52 /* clock.png */; };
|
||||||
7C60741E25C11DC000B0A154 /* AlarmLogo1.png in Resources */ = {isa = PBXBuildFile; fileRef = 7C60741D25C11DC000B0A154 /* AlarmLogo1.png */; };
|
7C83963625AF375B0027A94C /* NotificationLogic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963525AF375B0027A94C /* NotificationLogic.swift */; };
|
||||||
7C83963925AF68980027A94C /* DebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963825AF68980027A94C /* DebugViewController.swift */; };
|
7C83963925AF68980027A94C /* TestingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963825AF68980027A94C /* TestingViewController.swift */; };
|
||||||
7CD385A625BE4649007E9890 /* notif.caf in Resources */ = {isa = PBXBuildFile; fileRef = 7CD385A525BE4649007E9890 /* notif.caf */; };
|
7C83963C25AF6B6B0027A94C /* Alarm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C83963B25AF6B6B0027A94C /* Alarm.swift */; };
|
||||||
C7E638E825B88F8B00799512 /* MathExpressions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7E638E725B88F8B00799512 /* MathExpressions.swift */; };
|
|
||||||
F0DF7C0725BCD9FC0064A26B /* StopwatchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DF7C0625BCD9FC0064A26B /* StopwatchViewController.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
4F509BD125AE22D100726227 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
|
4F509BD125AE22D100726227 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
|
||||||
4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAlarmViewController.swift; sourceTree = "<group>"; };
|
4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAlarmViewController.swift; sourceTree = "<group>"; };
|
||||||
|
4F8A607425A919E600D88DC3 /* Logic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logic.swift; sourceTree = "<group>"; };
|
||||||
4F98955125A9260400F51321 /* Net.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Net.swift; sourceTree = "<group>"; };
|
4F98955125A9260400F51321 /* Net.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Net.swift; sourceTree = "<group>"; };
|
||||||
4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmViewController.swift; sourceTree = "<group>"; };
|
4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmViewController.swift; sourceTree = "<group>"; };
|
||||||
4FD642D225B48C380069171E /* AlarmActivator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmActivator.swift; sourceTree = "<group>"; };
|
4FF0683B25A5F18700304E6B /* ProjectClock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectClock.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4FD642DA25B4B7F60069171E /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
|
|
||||||
4FD642DF25B4D5F30069171E /* AlarmActivationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmActivationViewController.swift; sourceTree = "<group>"; };
|
|
||||||
4FF0683B25A5F18700304E6B /* GetGoing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GetGoing.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
4FF0683E25A5F18700304E6B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
4FF0683E25A5F18700304E6B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
4FF0684225A5F18700304E6B /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = "<group>"; };
|
4FF0684025A5F18700304E6B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
4FF0684225A5F18700304E6B /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
||||||
4FF0684525A5F18700304E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
4FF0684525A5F18700304E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
4FF0684725A5F18800304E6B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
4FF0684725A5F18800304E6B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
4FF0684A25A5F18800304E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
4FF0684A25A5F18800304E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
4FF0684C25A5F18800304E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
4FF0684C25A5F18800304E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
7C12BC7725BE25C000E5659C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
|
|
||||||
7C5DAE9B25AF812200E44C52 /* clock.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = clock.png; sourceTree = "<group>"; };
|
7C5DAE9B25AF812200E44C52 /* clock.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = clock.png; sourceTree = "<group>"; };
|
||||||
7C60741D25C11DC000B0A154 /* AlarmLogo1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AlarmLogo1.png; sourceTree = "<group>"; };
|
|
||||||
7C83962D25AF34F00027A94C /* ProjectClock.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ProjectClock.entitlements; sourceTree = "<group>"; };
|
7C83962D25AF34F00027A94C /* ProjectClock.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ProjectClock.entitlements; sourceTree = "<group>"; };
|
||||||
7C83962F25AF34F10027A94C /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
|
7C83962F25AF34F10027A94C /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
|
||||||
7C83963825AF68980027A94C /* DebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugViewController.swift; sourceTree = "<group>"; };
|
7C83963525AF375B0027A94C /* NotificationLogic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationLogic.swift; sourceTree = "<group>"; };
|
||||||
7CD385A525BE4649007E9890 /* notif.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = notif.caf; sourceTree = "<group>"; };
|
7C83963825AF68980027A94C /* TestingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestingViewController.swift; sourceTree = "<group>"; };
|
||||||
C7E638E725B88F8B00799512 /* MathExpressions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MathExpressions.swift; sourceTree = "<group>"; };
|
7C83963B25AF6B6B0027A94C /* Alarm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alarm.swift; sourceTree = "<group>"; };
|
||||||
F0DF7C0625BCD9FC0064A26B /* StopwatchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopwatchViewController.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -77,7 +69,7 @@
|
|||||||
4FF0683C25A5F18700304E6B /* Products */ = {
|
4FF0683C25A5F18700304E6B /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4FF0683B25A5F18700304E6B /* GetGoing.app */,
|
4FF0683B25A5F18700304E6B /* ProjectClock.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -85,27 +77,23 @@
|
|||||||
4FF0683D25A5F18700304E6B /* ProjectClock */ = {
|
4FF0683D25A5F18700304E6B /* ProjectClock */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7CD385A425BE4639007E9890 /* Sounds */,
|
|
||||||
4FF0684225A5F18700304E6B /* Account.swift */,
|
|
||||||
4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */,
|
|
||||||
4FD642DF25B4D5F30069171E /* AlarmActivationViewController.swift */,
|
|
||||||
4FD642D225B48C380069171E /* AlarmActivator.swift */,
|
|
||||||
4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */,
|
|
||||||
4FF0683E25A5F18700304E6B /* AppDelegate.swift */,
|
4FF0683E25A5F18700304E6B /* AppDelegate.swift */,
|
||||||
4FF0684725A5F18800304E6B /* Assets.xcassets */,
|
4FF0684025A5F18700304E6B /* SceneDelegate.swift */,
|
||||||
7C5DAE9B25AF812200E44C52 /* clock.png */,
|
|
||||||
7C60741D25C11DC000B0A154 /* AlarmLogo1.png */,
|
|
||||||
7C83963825AF68980027A94C /* DebugViewController.swift */,
|
|
||||||
4FF0684C25A5F18800304E6B /* Info.plist */,
|
|
||||||
4FF0684925A5F18800304E6B /* LaunchScreen.storyboard */,
|
|
||||||
4FF0684425A5F18700304E6B /* Main.storyboard */,
|
|
||||||
C7E638E725B88F8B00799512 /* MathExpressions.swift */,
|
|
||||||
4F509BD125AE22D100726227 /* Models.swift */,
|
|
||||||
4F98955125A9260400F51321 /* Net.swift */,
|
|
||||||
7C12BC7725BE25C000E5659C /* Notification.swift */,
|
|
||||||
7C83962D25AF34F00027A94C /* ProjectClock.entitlements */,
|
7C83962D25AF34F00027A94C /* ProjectClock.entitlements */,
|
||||||
F0DF7C0625BCD9FC0064A26B /* StopwatchViewController.swift */,
|
4FF0684225A5F18700304E6B /* AccountViewController.swift */,
|
||||||
4FD642DA25B4B7F60069171E /* Utils.swift */,
|
4FA419AE25AF93EC004CE0FC /* AlarmViewController.swift */,
|
||||||
|
4F8A607025A9160400D88DC3 /* AddAlarmViewController.swift */,
|
||||||
|
4FF0684425A5F18700304E6B /* Main.storyboard */,
|
||||||
|
4FF0684725A5F18800304E6B /* Assets.xcassets */,
|
||||||
|
4FF0684925A5F18800304E6B /* LaunchScreen.storyboard */,
|
||||||
|
4FF0684C25A5F18800304E6B /* Info.plist */,
|
||||||
|
4F8A607425A919E600D88DC3 /* Logic.swift */,
|
||||||
|
7C83963525AF375B0027A94C /* NotificationLogic.swift */,
|
||||||
|
4F98955125A9260400F51321 /* Net.swift */,
|
||||||
|
7C5DAE9B25AF812200E44C52 /* clock.png */,
|
||||||
|
4F509BD125AE22D100726227 /* Models.swift */,
|
||||||
|
7C83963825AF68980027A94C /* TestingViewController.swift */,
|
||||||
|
7C83963B25AF6B6B0027A94C /* Alarm.swift */,
|
||||||
);
|
);
|
||||||
path = ProjectClock;
|
path = ProjectClock;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -118,20 +106,12 @@
|
|||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
7CD385A425BE4639007E9890 /* Sounds */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
7CD385A525BE4649007E9890 /* notif.caf */,
|
|
||||||
);
|
|
||||||
path = Sounds;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
4FF0683A25A5F18700304E6B /* GetGoing */ = {
|
4FF0683A25A5F18700304E6B /* ProjectClock */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 4FF0684F25A5F18800304E6B /* Build configuration list for PBXNativeTarget "GetGoing" */;
|
buildConfigurationList = 4FF0684F25A5F18800304E6B /* Build configuration list for PBXNativeTarget "ProjectClock" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
4FF0683725A5F18700304E6B /* Sources */,
|
4FF0683725A5F18700304E6B /* Sources */,
|
||||||
4FF0683825A5F18700304E6B /* Frameworks */,
|
4FF0683825A5F18700304E6B /* Frameworks */,
|
||||||
@@ -141,9 +121,9 @@
|
|||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = GetGoing;
|
name = ProjectClock;
|
||||||
productName = ProjectClock;
|
productName = ProjectClock;
|
||||||
productReference = 4FF0683B25A5F18700304E6B /* GetGoing.app */;
|
productReference = 4FF0683B25A5F18700304E6B /* ProjectClock.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
@@ -163,7 +143,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 4FF0683625A5F18700304E6B /* Build configuration list for PBXProject "GetGoing" */;
|
buildConfigurationList = 4FF0683625A5F18700304E6B /* Build configuration list for PBXProject "ProjectClock" */;
|
||||||
compatibilityVersion = "Xcode 9.3";
|
compatibilityVersion = "Xcode 9.3";
|
||||||
developmentRegion = en;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
@@ -176,7 +156,7 @@
|
|||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
4FF0683A25A5F18700304E6B /* GetGoing */,
|
4FF0683A25A5F18700304E6B /* ProjectClock */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -186,12 +166,10 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
7C60741E25C11DC000B0A154 /* AlarmLogo1.png in Resources */,
|
|
||||||
4FF0684B25A5F18800304E6B /* LaunchScreen.storyboard in Resources */,
|
4FF0684B25A5F18800304E6B /* LaunchScreen.storyboard in Resources */,
|
||||||
7C5DAE9C25AF812200E44C52 /* clock.png in Resources */,
|
7C5DAE9C25AF812200E44C52 /* clock.png in Resources */,
|
||||||
4FF0684825A5F18800304E6B /* Assets.xcassets in Resources */,
|
4FF0684825A5F18800304E6B /* Assets.xcassets in Resources */,
|
||||||
4FF0684625A5F18700304E6B /* Main.storyboard in Resources */,
|
4FF0684625A5F18700304E6B /* Main.storyboard in Resources */,
|
||||||
7CD385A625BE4649007E9890 /* notif.caf in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -204,17 +182,15 @@
|
|||||||
files = (
|
files = (
|
||||||
4F8A607125A9160400D88DC3 /* AddAlarmViewController.swift in Sources */,
|
4F8A607125A9160400D88DC3 /* AddAlarmViewController.swift in Sources */,
|
||||||
4F98955225A9260400F51321 /* Net.swift in Sources */,
|
4F98955225A9260400F51321 /* Net.swift in Sources */,
|
||||||
7C83963925AF68980027A94C /* DebugViewController.swift in Sources */,
|
4F8A607525A919E600D88DC3 /* Logic.swift in Sources */,
|
||||||
4FF0684325A5F18700304E6B /* Account.swift in Sources */,
|
7C83963925AF68980027A94C /* TestingViewController.swift in Sources */,
|
||||||
F0DF7C0725BCD9FC0064A26B /* StopwatchViewController.swift in Sources */,
|
4FF0684325A5F18700304E6B /* AccountViewController.swift in Sources */,
|
||||||
4FF0683F25A5F18700304E6B /* AppDelegate.swift in Sources */,
|
4FF0683F25A5F18700304E6B /* AppDelegate.swift in Sources */,
|
||||||
4FD642D325B48C380069171E /* AlarmActivator.swift in Sources */,
|
4FF0684125A5F18700304E6B /* SceneDelegate.swift in Sources */,
|
||||||
4FD642DB25B4B7F60069171E /* Utils.swift in Sources */,
|
|
||||||
4FA419AF25AF93EC004CE0FC /* AlarmViewController.swift in Sources */,
|
4FA419AF25AF93EC004CE0FC /* AlarmViewController.swift in Sources */,
|
||||||
|
7C83963C25AF6B6B0027A94C /* Alarm.swift in Sources */,
|
||||||
4F509BD225AE22D100726227 /* Models.swift in Sources */,
|
4F509BD225AE22D100726227 /* Models.swift in Sources */,
|
||||||
7C12BC7825BE25C000E5659C /* Notification.swift in Sources */,
|
7C83963625AF375B0027A94C /* NotificationLogic.swift in Sources */,
|
||||||
C7E638E825B88F8B00799512 /* MathExpressions.swift in Sources */,
|
|
||||||
4FD642E025B4D5F30069171E /* AlarmActivationViewController.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -369,7 +345,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.hydev.GetGoing;
|
PRODUCT_BUNDLE_IDENTIFIER = org.hydev.ProjectClock;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@@ -389,7 +365,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.hydev.GetGoing;
|
PRODUCT_BUNDLE_IDENTIFIER = org.hydev.ProjectClock;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@@ -399,7 +375,7 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
4FF0683625A5F18700304E6B /* Build configuration list for PBXProject "GetGoing" */ = {
|
4FF0683625A5F18700304E6B /* Build configuration list for PBXProject "ProjectClock" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
4FF0684D25A5F18800304E6B /* Debug */,
|
4FF0684D25A5F18800304E6B /* Debug */,
|
||||||
@@ -408,7 +384,7 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
4FF0684F25A5F18800304E6B /* Build configuration list for PBXNativeTarget "GetGoing" */ = {
|
4FF0684F25A5F18800304E6B /* Build configuration list for PBXNativeTarget "ProjectClock" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
4FF0685025A5F18800304E6B /* Debug */,
|
4FF0685025A5F18800304E6B /* Debug */,
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?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>SchemeUserState</key>
|
||||||
|
<dict>
|
||||||
|
<key>ProjectClock.xcscheme_^#shared#^_</key>
|
||||||
|
<dict>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,542 +0,0 @@
|
|||||||
//
|
|
||||||
// ViewController.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Hykilpikonna on 1/6/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/**
|
|
||||||
Account view controller controlling the two separate view controllers
|
|
||||||
*/
|
|
||||||
class AccountViewController: UIViewController
|
|
||||||
{
|
|
||||||
@IBOutlet var vLogin: UIView!
|
|
||||||
@IBOutlet var vManage: UIView!
|
|
||||||
|
|
||||||
// For instance references
|
|
||||||
static var this: AccountViewController!
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user switch to this tab
|
|
||||||
*/
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
// Static instance reference
|
|
||||||
AccountViewController.this = self
|
|
||||||
|
|
||||||
// Check if already registered/logged in
|
|
||||||
if localStorage.string(forKey: "id") != nil { login() }
|
|
||||||
|
|
||||||
super.viewDidLoad()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Login from the account page
|
|
||||||
*/
|
|
||||||
func login()
|
|
||||||
{
|
|
||||||
vLogin.hide()
|
|
||||||
vManage.show()
|
|
||||||
ManageVC.this.display()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Logout
|
|
||||||
*/
|
|
||||||
func logout()
|
|
||||||
{
|
|
||||||
// Remove login info
|
|
||||||
["id", "user", "pass", "family"].forEach { localStorage.removeObject(forKey: $0) }
|
|
||||||
|
|
||||||
// Switch UI
|
|
||||||
vLogin.show()
|
|
||||||
vManage.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
View controller for registration and login
|
|
||||||
*/
|
|
||||||
class LoginVC: EndEditingOnReturn
|
|
||||||
{
|
|
||||||
@IBOutlet weak var username: UITextField!
|
|
||||||
@IBOutlet weak var password: UITextField!
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
username.delegate = self
|
|
||||||
password.delegate = self
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Send user login/registration request
|
|
||||||
|
|
||||||
- Parameter login: True: Login, False: Register
|
|
||||||
*/
|
|
||||||
func userRequest(login: Bool)
|
|
||||||
{
|
|
||||||
// Verify username and password
|
|
||||||
guard let name = username.text, name ~= "[A-Za-z0-9_-]{3,16}" else
|
|
||||||
{
|
|
||||||
msg("Username Invalid", "Username must be 3 to 16 characters long, and must only contain a-z, 0-9, underscore, and minus signs (-).")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let pass = password.text, pass.count >= 8 else
|
|
||||||
{
|
|
||||||
msg("Password Invalid", "Password must be more than or equal to 8 characters long")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error messages
|
|
||||||
let errors = ["409 - [\"A0111\"]": "Account already exists, please login instead.",
|
|
||||||
"401 -": "Incorrect username/password",
|
|
||||||
"404 -": "Username does not exist in the database",
|
|
||||||
"406 - [\"A0101\"]": "Username invalid."
|
|
||||||
]
|
|
||||||
|
|
||||||
// Send register request
|
|
||||||
sendReq(login ? APIs.login : APIs.register,
|
|
||||||
title: login ? "Logging in..." : "Registering...", errors: errors,
|
|
||||||
params: ["username": name, "password": pass.sha256])
|
|
||||||
{
|
|
||||||
// Store username and password
|
|
||||||
localStorage["name"] = name
|
|
||||||
localStorage["pass"] = pass.sha256
|
|
||||||
localStorage["id"] = $0
|
|
||||||
|
|
||||||
send(APIs.familyGet)
|
|
||||||
{
|
|
||||||
$0.localSave()
|
|
||||||
self.loginSuccess(login)
|
|
||||||
}
|
|
||||||
err: { it in print(it); self.loginSuccess(login) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loginSuccess(_ login: Bool)
|
|
||||||
{
|
|
||||||
ui
|
|
||||||
{
|
|
||||||
// Send feedback
|
|
||||||
if login { self.msg("Login success!", "Now you can use account features, yay!") }
|
|
||||||
else { self.msg("Registration success!", "Now you have an account, yay!") }
|
|
||||||
|
|
||||||
// Hide registration and show account detail view
|
|
||||||
AccountViewController.this.login()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks register
|
|
||||||
*/
|
|
||||||
@IBAction func register(_ sender: Any)
|
|
||||||
{
|
|
||||||
self.view.endEditing(true)
|
|
||||||
userRequest(login: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks login
|
|
||||||
*/
|
|
||||||
@IBAction func login(_ sender: Any)
|
|
||||||
{
|
|
||||||
self.view.endEditing(true)
|
|
||||||
userRequest(login: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Account manage view controller
|
|
||||||
*/
|
|
||||||
class ManageVC: UIViewController
|
|
||||||
{
|
|
||||||
static var this: ManageVC!
|
|
||||||
|
|
||||||
@IBOutlet weak var lUsername: UILabel!
|
|
||||||
@IBOutlet weak var lJoinDate: UILabel!
|
|
||||||
|
|
||||||
@IBOutlet weak var lCurrentFamily: UILabel!
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user switched to the account tab (whether the view container is hidden or not)
|
|
||||||
*/
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
// Static reference
|
|
||||||
ManageVC.this = self
|
|
||||||
super.viewDidLoad()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Display account info
|
|
||||||
*/
|
|
||||||
func display()
|
|
||||||
{
|
|
||||||
lUsername.text = localStorage.string(forKey: "name")
|
|
||||||
// TODO: Implement join date (not important)
|
|
||||||
lJoinDate.text = localStorage.string(forKey: "id")
|
|
||||||
|
|
||||||
// Display family name
|
|
||||||
if let family = Family.fromLocal()
|
|
||||||
{
|
|
||||||
lCurrentFamily.text = family.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the upload backup button
|
|
||||||
*/
|
|
||||||
@IBAction func uploadBackup(_ sender: Any)
|
|
||||||
{
|
|
||||||
sendReq(APIs.uploadConfig, title: "Uploading...", params: ["config": localStorage.string(forKey: "alarms")!])
|
|
||||||
{ it in self.msg("Success!", "You're backed up.") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the download backup button
|
|
||||||
*/
|
|
||||||
@IBAction func downloadBackup(_ sender: Any)
|
|
||||||
{
|
|
||||||
sendReq(APIs.downloadConfig, title: "Downloading...")
|
|
||||||
{
|
|
||||||
// Make sure backup exists and is parseable
|
|
||||||
if $0.isEmpty { self.msg("Nope", "No backups found"); return }
|
|
||||||
guard JSON.parse([Alarm].self, $0) != nil else { self.msg("Failed", "Backup restoration failed."); return }
|
|
||||||
|
|
||||||
// Save backup
|
|
||||||
localStorage.setValue($0, forKey: "alarms")
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
AlarmViewController.staticTable?.reloadData()
|
|
||||||
|
|
||||||
// Schedule any missing notifications
|
|
||||||
Notification.bulkScheduleNotifications(alarms: Alarms.fromLocal().list)
|
|
||||||
|
|
||||||
self.msg("Success!", "You're restored your backup.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the logout button
|
|
||||||
*/
|
|
||||||
@IBAction func logout(_ sender: Any)
|
|
||||||
{
|
|
||||||
AccountViewController.this.logout()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the delete account button
|
|
||||||
*/
|
|
||||||
@IBAction func deleteAccount(_ sender: Any)
|
|
||||||
{
|
|
||||||
enterPin("Are you sure?", "Enter 1234 to continue deleting your account, you can't undo this.")
|
|
||||||
{
|
|
||||||
guard $0 == "1234" else { return }
|
|
||||||
|
|
||||||
self.sendReq(APIs.delete, title: "Deleting...")
|
|
||||||
{
|
|
||||||
print("Deleted! \($0)")
|
|
||||||
self.msg("Deleted!", "You are erased from our database, you no longer exist.")
|
|
||||||
self.logout(sender)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Family view controller that displays family info or create/join family buttons
|
|
||||||
*/
|
|
||||||
class FamilyVC: UIViewController
|
|
||||||
{
|
|
||||||
static var this: FamilyVC!
|
|
||||||
|
|
||||||
// No family view - prompt to create/join a family
|
|
||||||
@IBOutlet weak var noFamilyView: UIView!
|
|
||||||
var createMode: Bool!
|
|
||||||
|
|
||||||
@IBAction func btnCreate(_ sender: Any)
|
|
||||||
{
|
|
||||||
createMode = true
|
|
||||||
performSegue(withIdentifier: "family-create-join", sender: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func btnJoin(_ sender: Any)
|
|
||||||
{
|
|
||||||
createMode = false
|
|
||||||
performSegue(withIdentifier: "family-create-join", sender: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBSegueAction func segueCreateJoin(_ coder: NSCoder) -> FamilyCreateJoinVC?
|
|
||||||
{
|
|
||||||
return FamilyCreateJoinVC(coder: coder, create: createMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Family view - Display family information and controls
|
|
||||||
@IBOutlet weak var familyView: UIView!
|
|
||||||
@IBOutlet weak var table: UITableView!
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when information is updated
|
|
||||||
*/
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
FamilyVC.this = self
|
|
||||||
|
|
||||||
if let _ = Family.fromLocal()
|
|
||||||
{
|
|
||||||
// Family exists
|
|
||||||
noFamilyView.hide()
|
|
||||||
familyView.show()
|
|
||||||
|
|
||||||
table.dataSource = self
|
|
||||||
table.delegate = self
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Family doesn't exist
|
|
||||||
noFamilyView.show()
|
|
||||||
familyView.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the refresh button
|
|
||||||
*/
|
|
||||||
@IBAction func btnRefresh(_ sender: Any)
|
|
||||||
{
|
|
||||||
sendReq(APIs.familyGet, title: "Updating family...")
|
|
||||||
{
|
|
||||||
$0.localSave()
|
|
||||||
self.table.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the change pin button
|
|
||||||
*/
|
|
||||||
@IBAction func btnChangePin(_ sender: Any)
|
|
||||||
{
|
|
||||||
self.enterPin("Change Pin", "Enter your OLD pin:") { oldPin in
|
|
||||||
|
|
||||||
self.enterPin("Change Pin", "Enter your NEW pin:") { newPin in
|
|
||||||
|
|
||||||
guard newPin.count >= 4 else { self.msg("Pin Too Weak", "Your family pin must be 4 numbers or more."); return }
|
|
||||||
|
|
||||||
self.sendReq(APIs.familyChangePin, title: "Updating Pin...", params: ["oldPin": oldPin, "newPin": newPin]) { it in
|
|
||||||
|
|
||||||
self.msg("Update Success!", "Your family pin is updated.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the leave or delete family button
|
|
||||||
*/
|
|
||||||
@IBAction func btnLeave(_ sender: UIButton)
|
|
||||||
{
|
|
||||||
let i = sender.tag
|
|
||||||
let action = ["Leave", "Delete"][i]
|
|
||||||
let title = ["Leaving...", "Deleting..."][i]
|
|
||||||
let msg = ["You left the family.", "You deleted the family."][i]
|
|
||||||
|
|
||||||
enterPin()
|
|
||||||
{
|
|
||||||
self.sendReq(APIs.familyAction, title: title, params: ["pin": $0, "action": action]) { it in
|
|
||||||
|
|
||||||
// Leave or delete, clear local storage's family section
|
|
||||||
if i == 0 || i == 1 { localStorage.removeObject(forKey: "family") }
|
|
||||||
|
|
||||||
self.msg("\(action) Success!", msg)
|
|
||||||
{
|
|
||||||
self.viewDidLoad()
|
|
||||||
AccountViewController.this.login()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Table data source
|
|
||||||
*/
|
|
||||||
extension FamilyVC: UITableViewDelegate, UITableViewDataSource
|
|
||||||
{
|
|
||||||
static var selectedUser: String = ""
|
|
||||||
|
|
||||||
/**
|
|
||||||
Define row count
|
|
||||||
*/
|
|
||||||
func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int
|
|
||||||
{
|
|
||||||
guard let family = Family.fromLocal() else { return 0 }
|
|
||||||
return family.membersList.count
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Set cell at i
|
|
||||||
*/
|
|
||||||
func tableView(_ view: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
|
||||||
{
|
|
||||||
let cell = view.dequeueReusableCell(withIdentifier: "family-member-cell", for: i)
|
|
||||||
cell.textLabel?.text = Family.fromLocal()!.membersList[i.row]
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks on the cell at i
|
|
||||||
*/
|
|
||||||
func tableView(_ view: UITableView, didSelectRowAt i: IndexPath)
|
|
||||||
{
|
|
||||||
FamilyVC.selectedUser = Family.fromLocal()!.membersList[i.row]
|
|
||||||
view.deselectRow(at: i, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Create or join a family
|
|
||||||
*/
|
|
||||||
class FamilyCreateJoinVC: EndEditingOnReturn
|
|
||||||
{
|
|
||||||
let createMode: Bool
|
|
||||||
@IBOutlet weak var lFamilyNameOrId: UILabel!
|
|
||||||
@IBOutlet weak var bCreateJoin: UIButton!
|
|
||||||
@IBOutlet weak var tNameOrId: UITextField!
|
|
||||||
@IBOutlet weak var tPin: UITextField!
|
|
||||||
|
|
||||||
/**
|
|
||||||
Pass in create mode from FamilyVC
|
|
||||||
*/
|
|
||||||
init?(coder: NSCoder, create: Bool)
|
|
||||||
{
|
|
||||||
createMode = create
|
|
||||||
super.init(coder: coder)
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
||||||
|
|
||||||
/**
|
|
||||||
On load
|
|
||||||
*/
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
// Set UI according to createMode
|
|
||||||
lFamilyNameOrId.text = createMode ? "Family Name" : "Family ID"
|
|
||||||
bCreateJoin.setTitle(createMode ? "Create" : "Join", for: .normal)
|
|
||||||
tNameOrId.keyboardType = createMode ? .default : .numberPad
|
|
||||||
|
|
||||||
// Default name
|
|
||||||
if createMode
|
|
||||||
{
|
|
||||||
tNameOrId.text = "\(localStorage.string(forKey: "name")!)'s Family"
|
|
||||||
}
|
|
||||||
|
|
||||||
// End editing on return
|
|
||||||
tNameOrId.delegate = self
|
|
||||||
tPin.delegate = self
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called after successfully joining or creating a family.
|
|
||||||
*/
|
|
||||||
func afterFamilyChange()
|
|
||||||
{
|
|
||||||
self.dismiss()
|
|
||||||
FamilyVC.this.viewDidLoad()
|
|
||||||
AccountViewController.this.login()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks create or join button
|
|
||||||
*/
|
|
||||||
@IBAction func btnCreateOrJoin(_ sender: Any)
|
|
||||||
{
|
|
||||||
// Check pin
|
|
||||||
guard let pin = tPin.text, pin.count >= 4 else { msg("Pin Too Weak", "Your family pin must be 4 numbers or more."); return }
|
|
||||||
|
|
||||||
if createMode
|
|
||||||
{
|
|
||||||
guard let name = tNameOrId.text, !name.isEmpty else { msg("Name Empty", "You must enter a family name"); return }
|
|
||||||
|
|
||||||
// Create family
|
|
||||||
sendReq(APIs.familyCreate, title: "Creating...", params: ["name": name, "pin": pin])
|
|
||||||
{
|
|
||||||
// Save
|
|
||||||
$0.localSave()
|
|
||||||
|
|
||||||
// Send success message
|
|
||||||
self.msg("Created!", "Your family ID is \($0.fid)") { self.afterFamilyChange() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
guard let idString = tNameOrId.text, !idString.isEmpty, let id = Int(idString) else
|
|
||||||
{ msg("ID Incorrect", "Please make sure your ID is an positive integer."); return }
|
|
||||||
|
|
||||||
// Join family
|
|
||||||
sendReq(APIs.familyAction, title: "Joining...", params: ["fid": String(id), "pin": pin, "action": "join"])
|
|
||||||
{
|
|
||||||
// Save
|
|
||||||
$0.localSave()
|
|
||||||
|
|
||||||
// Send success message
|
|
||||||
self.msg("Joined!", "Your family ID is \($0.fid)") { self.afterFamilyChange() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
View controller for adding an alarm to a fmaily member
|
|
||||||
*/
|
|
||||||
class FamilyAddAlarmVC: UIViewController, UITableViewDelegate, UITableViewDataSource
|
|
||||||
{
|
|
||||||
@IBOutlet weak var table: UITableView!
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
table.delegate = self
|
|
||||||
table.dataSource = self
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Define row count
|
|
||||||
*/
|
|
||||||
func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int
|
|
||||||
{
|
|
||||||
return Alarms.fromLocal().list.count
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Set cell at i
|
|
||||||
*/
|
|
||||||
func tableView(_ view: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
|
||||||
{
|
|
||||||
let cell = view.dequeueReusableCell(withIdentifier: "family-alarm-cell", for: i)
|
|
||||||
cell.textLabel?.text = Alarms.fromLocal().list[i.row].timeText
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks on the cell at i
|
|
||||||
*/
|
|
||||||
func tableView(_ view: UITableView, didSelectRowAt i: IndexPath)
|
|
||||||
{
|
|
||||||
view.deselectRow(at: i, animated: true)
|
|
||||||
enterPin()
|
|
||||||
{
|
|
||||||
self.sendReq(APIs.familyAddAlarm, title: "Adding...", params: ["to": FamilyVC.selectedUser, "pin": $0, "alarm": JSON.stringify(Alarms.fromLocal().list[i.row])!])
|
|
||||||
{
|
|
||||||
print($0)
|
|
||||||
self.msg("Added!", "The member will receive the alarm after opening the app.")
|
|
||||||
{
|
|
||||||
self.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// ViewController.swift
|
||||||
|
// ProjectClock
|
||||||
|
//
|
||||||
|
// Created by Hykilpikonna on 1/6/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class AccountViewController: UIViewController
|
||||||
|
{
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,11 +7,14 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class AddAlarmViewController: EndEditingOnReturn
|
class AddAlarmViewController: UIViewController
|
||||||
{
|
{
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
// Editing variables
|
// Editing variables
|
||||||
var alarmCell: AlarmTableCell? = nil
|
var alarmCell: AlarmTableCell? = nil
|
||||||
var editMode: Bool { alarmCell != nil }
|
var editMode: Bool { alarmCell != nil }
|
||||||
|
var originalTime: String = ""
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
{
|
{
|
||||||
@@ -19,30 +22,61 @@ class AddAlarmViewController: EndEditingOnReturn
|
|||||||
alarmNameTextField.delegate = self
|
alarmNameTextField.delegate = self
|
||||||
|
|
||||||
// Load alarm to edit if in edit mode
|
// Load alarm to edit if in edit mode
|
||||||
if let alarmCell = alarmCell, let alarm = alarmCell.alarm
|
if let alarmCell = alarmCell
|
||||||
{
|
{
|
||||||
// Toggle editing mode
|
// Toggle editing mode
|
||||||
viewTitle.text = "Edit Alarm"
|
viewTitle.text = "Edit Alarm"
|
||||||
|
|
||||||
|
// Convert string to Date
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "h:mma"
|
||||||
|
let date = dateFormatter.date(from: "\(alarmCell.time.text!)\(alarmCell.ampm.text!)")
|
||||||
|
|
||||||
// Set all the original values to be edited
|
// Set all the original values to be edited
|
||||||
let (y,m,d) = Date().getYMD()
|
timePicker.date = date!
|
||||||
timePicker.date = Date.create(y, m, d, alarm.hour, alarm.minute)
|
originalTime = String(dateFormatter.string(from: date!).dropLast(2))
|
||||||
|
|
||||||
// Toggle proper repeats
|
// Toggle proper repeats
|
||||||
repeatWeekdaysSwitch.isOn = alarm.repeats[1...5].allSatisfy { $0 }
|
if let repeats = alarmCell.repeatText.text {
|
||||||
repeatWeekendsSwitch.isOn = alarm.repeats[0] && alarm.repeats[6]
|
if repeats == "Repeats: Weekdays" {
|
||||||
|
repeatWeekdaysSwitch.isOn = true
|
||||||
|
repeatWeekendsSwitch.isOn = false
|
||||||
|
} else if repeats == "Repeats: Weekends" {
|
||||||
|
repeatWeekendsSwitch.isOn = true
|
||||||
|
repeatWeekdaysSwitch.isOn = false
|
||||||
|
} else if repeats == "Repeats: Daily" {
|
||||||
|
repeatWeekdaysSwitch.isOn = true
|
||||||
|
repeatWeekendsSwitch.isOn = true
|
||||||
|
} else {
|
||||||
|
repeatWeekendsSwitch.isOn = false
|
||||||
|
repeatWeekdaysSwitch.isOn = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
alarmNameTextField.text = alarm.text
|
alarmNameTextField.text = String(alarmCell.descriptionText.text!.dropFirst(2))
|
||||||
updateETA()
|
updateETA()
|
||||||
|
|
||||||
// Sets the WVM
|
// Sets the WVM
|
||||||
wvmPicker.selectRow(alarm.wakeMethod.index, inComponent: 0, animated: true)
|
if let wvm = alarmCell.wvmText.text {
|
||||||
|
for index in 0...wvms.count-1 {
|
||||||
|
if wvm == wvms[index].name {
|
||||||
|
wvmPicker.selectRow(index, inComponent: 0, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sets alarm tone
|
//Sets alarm tone
|
||||||
ringtonePicker.selectRow(ringtones.firstIndex { $0.tone == alarm.alarmTone }!, inComponent: 0, animated: true)
|
if let toneName = alarmCell.toneLabel.text {
|
||||||
|
for index in 0...ringtones.count-1 {
|
||||||
|
if toneName == ringtones[index].name {
|
||||||
|
ringtonePicker.selectRow(index, inComponent: 0, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// UI: Make scroll view scrollable
|
// UI: Make scroll view scrollable
|
||||||
@IBOutlet weak var scrollView: UIScrollView!
|
@IBOutlet weak var scrollView: UIScrollView!
|
||||||
@IBOutlet weak var scrollViewInner: UIView!
|
@IBOutlet weak var scrollViewInner: UIView!
|
||||||
@@ -55,93 +89,12 @@ class AddAlarmViewController: EndEditingOnReturn
|
|||||||
// Pickers
|
// Pickers
|
||||||
@IBOutlet weak var timePicker: UIDatePicker!
|
@IBOutlet weak var timePicker: UIDatePicker!
|
||||||
@IBOutlet weak var wvmPicker: UIPickerView!
|
@IBOutlet weak var wvmPicker: UIPickerView!
|
||||||
@IBOutlet weak var ringtonePicker: UIPickerView!
|
|
||||||
|
|
||||||
// UI Elements
|
override func viewDidLoad()
|
||||||
@IBOutlet weak var repeatWeekdaysSwitch: UISwitch!
|
|
||||||
@IBOutlet weak var repeatWeekendsSwitch: UISwitch!
|
|
||||||
@IBOutlet weak var alarmNameTextField: UITextField!
|
|
||||||
@IBOutlet weak var timeTillAlarmLabel: UILabel!
|
|
||||||
@IBOutlet weak var viewTitle: UILabel!
|
|
||||||
|
|
||||||
/**
|
|
||||||
Removes the currently selcted alarm.
|
|
||||||
Returns the removed Alarm object.
|
|
||||||
*/
|
|
||||||
@discardableResult
|
|
||||||
func removeCurrentAlarm() -> Alarm?
|
|
||||||
{
|
|
||||||
guard let alarm = alarmCell?.alarm else { return nil }
|
|
||||||
|
|
||||||
// Removes the alarm from stored alarms
|
|
||||||
let alarms = Alarms.fromLocal()
|
|
||||||
alarms.list = alarms.list.filter { $0 != alarmCell?.alarm }
|
|
||||||
alarms.localSave()
|
|
||||||
|
|
||||||
return alarm
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the time for the alarm is changed.
|
|
||||||
Sets the time away at the top of the View.
|
|
||||||
*/
|
|
||||||
@IBAction func alarmTimeUpdated(_ sender: Any) { updateETA() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks the remove button and brings them back to the home page
|
|
||||||
*/
|
|
||||||
@IBAction func cancelAlarmButton(_ sender: Any) {
|
|
||||||
if editMode {
|
|
||||||
removeCurrentAlarm()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dismiss(animated: true, completion: nil)
|
|
||||||
//might need to reset all UI elements
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the user clicks Add Alarm
|
|
||||||
*/
|
|
||||||
@IBAction func addAlarmButton(_ sender: Any)
|
|
||||||
{
|
|
||||||
var oldAlarm: Alarm? = nil
|
|
||||||
let alarm = createAlarm()
|
|
||||||
let alarms = Alarms.fromLocal()
|
|
||||||
|
|
||||||
// Check if editing alarm
|
|
||||||
if (editMode)
|
|
||||||
{
|
|
||||||
oldAlarm = removeCurrentAlarm()
|
|
||||||
}
|
|
||||||
// Check for existing alarm
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (alarms.list.contains { $0 == alarm })
|
|
||||||
{
|
|
||||||
msg("Sorry", "An identical or similar alarm already exists, please try again")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the alarm to the list and save the list
|
|
||||||
Alarms.fromLocal().apply { $0.list.append(alarm) }.localSave();
|
|
||||||
|
|
||||||
//Schedules notification for the alarm
|
|
||||||
if editMode
|
|
||||||
{
|
|
||||||
Notification.removeNotification(alarm: oldAlarm!)
|
|
||||||
}
|
|
||||||
Notification.scheduleNotification(alarm: alarm)
|
|
||||||
|
|
||||||
// Dismiss this view
|
|
||||||
self.dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Create alarm, but it doesn't add the alarm to the list
|
|
||||||
*/
|
|
||||||
func createAlarm() -> Alarm
|
|
||||||
{
|
{
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
super.viewDidLoad()
|
||||||
|
=======
|
||||||
let (h, m, _) = timePicker.date.getHMS()
|
let (h, m, _) = timePicker.date.getHMS()
|
||||||
|
|
||||||
// Create the alarm
|
// Create the alarm
|
||||||
@@ -164,6 +117,7 @@ class AddAlarmViewController: EndEditingOnReturn
|
|||||||
func updateETA() {
|
func updateETA() {
|
||||||
let timeTill = createAlarm().nextActivate!.timeIntervalSince(Date()).str()
|
let timeTill = createAlarm().nextActivate!.timeIntervalSince(Date()).str()
|
||||||
timeTillAlarmLabel.text = "Going off in \(timeTill)"
|
timeTillAlarmLabel.text = "Going off in \(timeTill)"
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,31 +145,3 @@ class WVMDataSource: UIPickerView, UIPickerViewDelegate, UIPickerViewDataSource
|
|||||||
return wvms[r].name + " - " + wvms[r].desc
|
return wvms[r].name + " - " + wvms[r].desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RingtonesDataSource: UIPickerView, UIPickerViewDelegate, UIPickerViewDataSource
|
|
||||||
{
|
|
||||||
required init?(coder: NSCoder)
|
|
||||||
{
|
|
||||||
super.init(coder: coder)
|
|
||||||
delegate = self
|
|
||||||
dataSource = self
|
|
||||||
}
|
|
||||||
|
|
||||||
func numberOfComponents(in pickerView: UIPickerView) -> Int
|
|
||||||
{
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func pickerView(_ v: UIPickerView, numberOfRowsInComponent: Int) -> Int
|
|
||||||
{
|
|
||||||
return ringtones.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func pickerView(_ v: UIPickerView, titleForRow r: Int, forComponent: Int) -> String?
|
|
||||||
{
|
|
||||||
return ringtones[r].name
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Alarm.swift
|
||||||
|
// ProjectClock
|
||||||
|
//
|
||||||
|
// Created by Aaron Saporito on 1/13/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class Alarm {
|
||||||
|
var alarmTime: Date
|
||||||
|
var text: String
|
||||||
|
var wakeMethod: WVM
|
||||||
|
|
||||||
|
init(alarmTime: Date, text: String, wakeMethod: WVM) {
|
||||||
|
|
||||||
|
self.alarmTime = alarmTime
|
||||||
|
self.text = text
|
||||||
|
self.wakeMethod = wakeMethod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
//
|
|
||||||
// AlarmActivationViewController.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Hykilpikonna on 1/17/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import AVFoundation
|
|
||||||
import CoreMotion
|
|
||||||
|
|
||||||
var motion = CMMotionManager()
|
|
||||||
var alarmStarted = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
View controlling alarm activation and dismissal
|
|
||||||
*/
|
|
||||||
class AlarmActivationViewController: EndEditingOnReturn
|
|
||||||
{
|
|
||||||
var currentAlarm: Alarm
|
|
||||||
|
|
||||||
// Puzzle outlets
|
|
||||||
@IBOutlet weak var puzzleView: UIView!
|
|
||||||
@IBOutlet weak var puzzleQuestionLabel: UILabel!
|
|
||||||
@IBOutlet weak var puzzleAnswerInput: UITextField!
|
|
||||||
var puzzleAnswers: [Int] = []
|
|
||||||
|
|
||||||
// RPS Outlets
|
|
||||||
@IBOutlet weak var rpsView: UIView!
|
|
||||||
@IBOutlet weak var rpsResult: UILabel!
|
|
||||||
|
|
||||||
// Shake Outlets
|
|
||||||
@IBOutlet weak var shakeView: UIView!
|
|
||||||
|
|
||||||
// Other Outlets
|
|
||||||
@IBOutlet weak var timeLabel: UILabel!
|
|
||||||
@IBOutlet weak var dateLabel: UILabel!
|
|
||||||
|
|
||||||
var solved = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
Constructor to receive alarm data from segue
|
|
||||||
*/
|
|
||||||
init?(coder: NSCoder, currentAlarm: Alarm)
|
|
||||||
{
|
|
||||||
self.currentAlarm = currentAlarm
|
|
||||||
super.init(coder: coder)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Unused init
|
|
||||||
*/
|
|
||||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the alarm activates
|
|
||||||
*/
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
// Set the time and date
|
|
||||||
dateLabel.text = Date().str("MMM d, Y")
|
|
||||||
timeLabel.text = currentAlarm.timeText
|
|
||||||
|
|
||||||
// Hide all inactive wakemethods
|
|
||||||
puzzleView.hide()
|
|
||||||
rpsView.hide()
|
|
||||||
shakeView.hide()
|
|
||||||
|
|
||||||
// Play sound
|
|
||||||
playSound()
|
|
||||||
vibrate()
|
|
||||||
|
|
||||||
// Run alarm
|
|
||||||
runAlarm()
|
|
||||||
|
|
||||||
// End edit on return
|
|
||||||
puzzleAnswerInput.delegate = self
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Play alarm sound
|
|
||||||
*/
|
|
||||||
func playSound()
|
|
||||||
{
|
|
||||||
AudioServicesPlayAlertSoundWithCompletion(currentAlarm.alarmTone) {
|
|
||||||
if alarmStarted { self.playSound() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func vibrate()
|
|
||||||
{
|
|
||||||
AudioServicesPlayAlertSoundWithCompletion(kSystemSoundID_Vibrate) {
|
|
||||||
if alarmStarted { self.vibrate() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Run alarm dismissal logic
|
|
||||||
*/
|
|
||||||
func runAlarm()
|
|
||||||
{
|
|
||||||
// Check if the device has accelerometer
|
|
||||||
var wvm = currentAlarm.wakeMethod.name
|
|
||||||
if wvm == "Shake" && !motion.isDeviceMotionAvailable
|
|
||||||
{
|
|
||||||
msg("Error", "Accelerometer is not available on your device, so shaking your phone wouldn't work.")
|
|
||||||
wvm = "Factor"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize alarm
|
|
||||||
switch wvm
|
|
||||||
{
|
|
||||||
case "Math 1", "Math 2", "Math 3":
|
|
||||||
let q = MathExpProblem(size: Int(wvm[5...5])!)
|
|
||||||
puzzleQuestionLabel.text = "Solve: \(q.prob.replacingOccurrences(of: "**", with: "^"))"
|
|
||||||
puzzleAnswers = [q.ans]
|
|
||||||
puzzleView.show()
|
|
||||||
case "Factor":
|
|
||||||
initFactorProblem()
|
|
||||||
puzzleView.show()
|
|
||||||
case "RPS":
|
|
||||||
rpsView.show()
|
|
||||||
case "Shake":
|
|
||||||
shakeView.show()
|
|
||||||
|
|
||||||
// Start motion detection
|
|
||||||
let q = OperationQueue()
|
|
||||||
motion.deviceMotionUpdateInterval = 0.2
|
|
||||||
motion.startDeviceMotionUpdates(to: q) { data, error in
|
|
||||||
if let a = data?.userAcceleration, sqrt(pow(a.x, 2) + pow(a.y, 2) + pow(a.z, 2)) > 4
|
|
||||||
{
|
|
||||||
ui { self.endAlarm() }
|
|
||||||
motion.stopDeviceMotionUpdates()
|
|
||||||
q.cancelAllOperations()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
print("Invalid alarm type")
|
|
||||||
}
|
|
||||||
|
|
||||||
alarmStarted = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func initFactorProblem()
|
|
||||||
{
|
|
||||||
let problem = QuadraticProb()
|
|
||||||
puzzleAnswers = problem.getAnswer()
|
|
||||||
|
|
||||||
puzzleQuestionLabel.text = "Solve: \(problem.getProblem())"
|
|
||||||
print("Answer: \(puzzleAnswers)")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Verfies and ends factoring WVM
|
|
||||||
*/
|
|
||||||
@IBAction func checkBinomialSolution(_ sender: Any)
|
|
||||||
{
|
|
||||||
if let input = puzzleAnswerInput.text,
|
|
||||||
let numericalInput = Int(input),
|
|
||||||
puzzleAnswers.contains(numericalInput)
|
|
||||||
{
|
|
||||||
endAlarm()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets RPS choice
|
|
||||||
*/
|
|
||||||
@IBAction func rpsChoice(_ sender: UIButton)
|
|
||||||
{
|
|
||||||
if RPS.playRPS(you: [.rock, .paper, .scissors][sender.tag], computer: RPS.choices.randomElement()!)
|
|
||||||
{
|
|
||||||
endAlarm()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rpsResult.text = "\(["Paper", "Scissors", "Rock"][sender.tag]): You lost, try again"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Standard way to turn off and close the alarm
|
|
||||||
*/
|
|
||||||
func endAlarm()
|
|
||||||
{
|
|
||||||
alarmStarted = false
|
|
||||||
print("Alarm solved")
|
|
||||||
dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
//
|
|
||||||
// AlarmActivator.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Hykilpikonna on 1/17/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/**
|
|
||||||
Class to activate alarms when the user is inside the app
|
|
||||||
|
|
||||||
Note: This will not run when app is switched to the background or when the display is turned off, but it will run right after the user switched back to the app.
|
|
||||||
*/
|
|
||||||
class AlarmActivator: UITabBarController
|
|
||||||
{
|
|
||||||
/// Interval in seconds
|
|
||||||
static var interval = 2.0
|
|
||||||
|
|
||||||
/// Timer for scheduled calls
|
|
||||||
var timer: Timer?
|
|
||||||
var alarm: Alarm?
|
|
||||||
|
|
||||||
/// Timer for getting family alarm updates
|
|
||||||
var familyTimer: Timer?
|
|
||||||
|
|
||||||
/**
|
|
||||||
Called when the app started
|
|
||||||
*/
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
start()
|
|
||||||
|
|
||||||
// Get notification permissions from user
|
|
||||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
|
|
||||||
if success {
|
|
||||||
print("All set!")
|
|
||||||
} else if let error = error {
|
|
||||||
print(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Start detecting alarms
|
|
||||||
*/
|
|
||||||
func start()
|
|
||||||
{
|
|
||||||
if timer != nil { return }
|
|
||||||
timer = Timer.scheduledTimer(timeInterval: AlarmActivator.interval, target: self, selector: #selector(AlarmActivator.check), userInfo: nil, repeats: true)
|
|
||||||
familyTimer = Timer.scheduledTimer(timeInterval: 20.0, target: self, selector: #selector(AlarmActivator.checkFamily), userInfo: nil, repeats: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Stop detecting alarms
|
|
||||||
*/
|
|
||||||
func stop()
|
|
||||||
{
|
|
||||||
timer?.invalidate()
|
|
||||||
timer = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Check alarm
|
|
||||||
*/
|
|
||||||
@objc func check()
|
|
||||||
{
|
|
||||||
//NSLog("Check")
|
|
||||||
|
|
||||||
// Get the alarm to activate
|
|
||||||
let alarms = Alarms.fromLocal()
|
|
||||||
guard let alarm = alarms.listActivating.first else { return }
|
|
||||||
|
|
||||||
// Update alarm info
|
|
||||||
alarm.apply {
|
|
||||||
$0.lastActivate = Date()
|
|
||||||
if $0.oneTime { $0.enabled = false }
|
|
||||||
else {
|
|
||||||
Notification.scheduleNotification(alarm: alarm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alarms.localSave()
|
|
||||||
self.alarm = alarm
|
|
||||||
|
|
||||||
// Avoid starting duplicate alarms
|
|
||||||
if !alarmStarted { performSegue(withIdentifier: "activate-alarm", sender: alarm) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBSegueAction func sendAlarm(_ coder: NSCoder) -> AlarmActivationViewController?
|
|
||||||
{
|
|
||||||
return AlarmActivationViewController(coder: coder, currentAlarm: alarm!)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Check family alarm updates
|
|
||||||
*/
|
|
||||||
@objc func checkFamily()
|
|
||||||
{
|
|
||||||
guard localStorage.string(forKey: "family") != nil else { return }
|
|
||||||
|
|
||||||
send(APIs.familyAlarmUpdates)
|
|
||||||
{
|
|
||||||
guard $0 != "" else { return }
|
|
||||||
|
|
||||||
// Update alarms list
|
|
||||||
var changed = false
|
|
||||||
let alarms = Alarms.fromLocal()
|
|
||||||
$0.csv.forEach
|
|
||||||
{
|
|
||||||
guard let alarm = JSON.parse(Alarm.self, $0) else { return }
|
|
||||||
if (!alarms.list.contains { $0.timeText == alarm.timeText })
|
|
||||||
{
|
|
||||||
alarms.list.append(alarm)
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
alarms.localSave()
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
guard changed else { return }
|
|
||||||
ui
|
|
||||||
{
|
|
||||||
self.msg("New alarm!", "A family member added an alarm for you!")
|
|
||||||
{
|
|
||||||
AlarmViewController.staticTable?.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 3.0 MiB |
@@ -3,14 +3,15 @@ import UIKit
|
|||||||
class AlarmViewController: UIViewController
|
class AlarmViewController: UIViewController
|
||||||
{
|
{
|
||||||
@IBOutlet weak var table: UITableView!
|
@IBOutlet weak var table: UITableView!
|
||||||
static var staticTable: UITableView?
|
|
||||||
|
// TODO: Remove example and use localStorage
|
||||||
|
var data: [Alarm] = [Alarm(alarmTime: Date(), text: "Wake up lol", wakeMethod: wvms[0])]
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
// Assign table delegate and data source
|
// Assign table delegate and data source
|
||||||
AlarmViewController.staticTable = table
|
|
||||||
table.delegate = self
|
table.delegate = self
|
||||||
table.dataSource = self
|
table.dataSource = self
|
||||||
}
|
}
|
||||||
@@ -25,7 +26,7 @@ extension AlarmViewController: UITableViewDelegate, UITableViewDataSource
|
|||||||
func numberOfSections(in: UITableView) -> Int { return 1 }
|
func numberOfSections(in: UITableView) -> Int { return 1 }
|
||||||
|
|
||||||
/// How many rows are there
|
/// How many rows are there
|
||||||
func tableView(_ v: UITableView, numberOfRowsInSection s: Int) -> Int { return Alarms.fromLocal().list.count }
|
func tableView(_ v: UITableView, numberOfRowsInSection s: Int) -> Int { return data.count }
|
||||||
|
|
||||||
/// Configure each cell
|
/// Configure each cell
|
||||||
func tableView(_ v: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
func tableView(_ v: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
||||||
@@ -33,7 +34,7 @@ extension AlarmViewController: UITableViewDelegate, UITableViewDataSource
|
|||||||
// Get the cell and item at index i
|
// Get the cell and item at index i
|
||||||
let rawCell = v.dequeueReusableCell(withIdentifier: "alarm", for: i)
|
let rawCell = v.dequeueReusableCell(withIdentifier: "alarm", for: i)
|
||||||
guard let cell = rawCell as? AlarmTableCell else { return rawCell }
|
guard let cell = rawCell as? AlarmTableCell else { return rawCell }
|
||||||
let item = Alarms.fromLocal().list[i.row]
|
let item = data[i.row]
|
||||||
|
|
||||||
// Set the content of the cell to the content of the item.
|
// Set the content of the cell to the content of the item.
|
||||||
cell.setData(item)
|
cell.setData(item)
|
||||||
@@ -46,14 +47,6 @@ extension AlarmViewController: UITableViewDelegate, UITableViewDataSource
|
|||||||
|
|
||||||
/// IDK what this does (TODO: Find out what this does)
|
/// IDK what this does (TODO: Find out what this does)
|
||||||
func tableView(_ v: UITableView, didSelectRowAt i: IndexPath) { v.deselectRow(at: i, animated: true) }
|
func tableView(_ v: UITableView, didSelectRowAt i: IndexPath) { v.deselectRow(at: i, animated: true) }
|
||||||
|
|
||||||
/// Sends the selected alarm to be edited
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
|
||||||
if segue.identifier == "edit-alarm" {
|
|
||||||
let vc = segue.destination as! AddAlarmViewController
|
|
||||||
vc.alarmCell = (sender as! AlarmTableCell)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,21 +60,22 @@ class AlarmTableCell: UITableViewCell
|
|||||||
@IBOutlet weak var enable: UISwitch!
|
@IBOutlet weak var enable: UISwitch!
|
||||||
@IBOutlet weak var repeatText: UILabel!
|
@IBOutlet weak var repeatText: UILabel!
|
||||||
@IBOutlet weak var goingOffText: UILabel!
|
@IBOutlet weak var goingOffText: UILabel!
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
@IBOutlet weak var wvmText: UILabel!
|
@IBOutlet weak var wvmText: UILabel!
|
||||||
@IBOutlet weak var toneLabel: UILabel!
|
@IBOutlet weak var toneLabel: UILabel!
|
||||||
|
|
||||||
var alarm: Alarm!
|
>>>>>>> Stashed changes
|
||||||
|
|
||||||
/**
|
/// Update information on the cell to information in the alarm object
|
||||||
Update information on the cell to information in the alarm object
|
|
||||||
*/
|
|
||||||
func setData(_ alarm: Alarm)
|
func setData(_ alarm: Alarm)
|
||||||
{
|
{
|
||||||
self.alarm = alarm
|
|
||||||
descriptionText.text = "- " + alarm.text
|
descriptionText.text = "- " + alarm.text
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
enable.isOn = alarm.enabled
|
enable.isOn = alarm.enabled
|
||||||
wvmText.text = alarm.wakeMethod.name
|
wvmText.text = alarm.wakeMethod.name
|
||||||
toneLabel.text = ringtones.first { $0.tone.description == alarm.alarmTone.description }?.name
|
toneLabel.text = alarm.toneName
|
||||||
|
|
||||||
// Display Hour, Minute, and AM or PM
|
// Display Hour, Minute, and AM or PM
|
||||||
ampm.text = alarm.hour < 12 || alarm.hour == 24 ? "AM" : "PM"
|
ampm.text = alarm.hour < 12 || alarm.hour == 24 ? "AM" : "PM"
|
||||||
@@ -132,5 +126,6 @@ class AlarmTableCell: UITableViewCell
|
|||||||
Alarms.fromLocal().apply {
|
Alarms.fromLocal().apply {
|
||||||
$0.list.first { $0.hour == self.alarm.hour && $0.minute == self.alarm.minute }?.enabled = enable.isOn
|
$0.list.first { $0.hour == self.alarm.hour && $0.minute == self.alarm.minute }?.enabled = enable.isOn
|
||||||
}.localSave()
|
}.localSave()
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,76 +8,29 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@main
|
@main
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
{
|
|
||||||
/// Override point for customization after application launch.
|
|
||||||
func application(_ app: UIApplication, didFinishLaunchingWithOptions op: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
|
||||||
{
|
|
||||||
// Init default settings
|
|
||||||
localStorage.register(defaults: [
|
|
||||||
"alarms": JSON.stringify([Alarm(hour: 7, minute: 20, text: "Wake up lol", wakeMethod: wvms[0])])!
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
// Override point for customization after application launch.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ app: UIApplication, configurationForConnecting session: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
|
// MARK: UISceneSession Lifecycle
|
||||||
{
|
|
||||||
|
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||||
// Called when a new scene session is being created.
|
// Called when a new scene session is being created.
|
||||||
// Use this method to select a configuration to create the new scene with.
|
// Use this method to select a configuration to create the new scene with.
|
||||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: session.role)
|
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ app: UIApplication, didDiscardSceneSessions sessions: Set<UISceneSession>)
|
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||||
{
|
|
||||||
// Called when the user discards a scene session.
|
// Called when the user discards a scene session.
|
||||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate
|
|
||||||
{
|
|
||||||
var window: UIWindow?
|
|
||||||
|
|
||||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
|
|
||||||
{
|
|
||||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
|
||||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
|
||||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
|
||||||
guard let _ = (scene as? UIWindowScene) else { return }
|
|
||||||
}
|
|
||||||
|
|
||||||
func sceneDidDisconnect(_ scene: UIScene)
|
|
||||||
{
|
|
||||||
// Called as the scene is being released by the system.
|
|
||||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
|
||||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
|
||||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
|
||||||
}
|
|
||||||
|
|
||||||
func sceneDidBecomeActive(_ scene: UIScene)
|
|
||||||
{
|
|
||||||
// Called when the scene has moved from an inactive state to an active state.
|
|
||||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
|
||||||
}
|
|
||||||
|
|
||||||
func sceneWillResignActive(_ scene: UIScene)
|
|
||||||
{
|
|
||||||
// Called when the scene will move from an active state to an inactive state.
|
|
||||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
|
||||||
}
|
|
||||||
|
|
||||||
func sceneWillEnterForeground(_ scene: UIScene)
|
|
||||||
{
|
|
||||||
// Called as the scene transitions from the background to the foreground.
|
|
||||||
// Use this method to undo the changes made on entering the background.
|
|
||||||
}
|
|
||||||
|
|
||||||
func sceneDidEnterBackground(_ scene: UIScene)
|
|
||||||
{
|
|
||||||
// Called as the scene transitions from the foreground to the background.
|
|
||||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
|
||||||
// to restore the scene back to its current state.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,109 +1,91 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "40.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "60.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "3x",
|
"scale" : "3x",
|
||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "58.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "87.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "3x",
|
"scale" : "3x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "80.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "120.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "3x",
|
"scale" : "3x",
|
||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "120-1.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "60x60"
|
"size" : "60x60"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "180.png",
|
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"scale" : "3x",
|
"scale" : "3x",
|
||||||
"size" : "60x60"
|
"size" : "60x60"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "20.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "40-1.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "20x20"
|
"size" : "20x20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "29.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "58-1.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "29x29"
|
"size" : "29x29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "40-2.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "80-1.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "40x40"
|
"size" : "40x40"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "76.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "76x76"
|
"size" : "76x76"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "152.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "76x76"
|
"size" : "76x76"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "167.png",
|
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "83.5x83.5"
|
"size" : "83.5x83.5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "appstore.png",
|
|
||||||
"idiom" : "ios-marketing",
|
"idiom" : "ios-marketing",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="s8U-C8-W2S">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="s8U-C8-W2S">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
@@ -13,12 +13,12 @@
|
|||||||
<!--Alarm-->
|
<!--Alarm-->
|
||||||
<scene sceneID="xoX-u0-81t">
|
<scene sceneID="xoX-u0-81t">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="Bqt-du-DT2" customClass="AlarmViewController" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="Bqt-du-DT2" customClass="AlarmViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="lLA-ys-PRx">
|
<view key="view" contentMode="scaleToFill" id="lLA-ys-PRx">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="VaN-9W-uoG">
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="VaN-9W-uoG">
|
||||||
<rect key="frame" x="0.0" y="98" width="414" height="798"/>
|
<rect key="frame" x="0.0" y="98" width="414" height="798"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<button key="tableFooterView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="DQJ-kT-79q">
|
<button key="tableFooterView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="DQJ-kT-79q">
|
||||||
@@ -26,11 +26,11 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<state key="normal" title="Add Alarm"/>
|
<state key="normal" title="Add Alarm"/>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="Mki-dC-5Kc" kind="presentation" identifier="alarm-edit" id="Qim-4Q-43N"/>
|
<segue destination="Mki-dC-5Kc" kind="presentation" id="Qim-4Q-43N"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="alarm" rowHeight="120" id="pz0-Vu-M1U" customClass="AlarmTableCell" customModule="GetGoing" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="alarm" rowHeight="120" id="pz0-Vu-M1U" customClass="AlarmTableCell" customModule="ProjectClock" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="28" width="414" height="120"/>
|
<rect key="frame" x="0.0" y="28" width="414" height="120"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pz0-Vu-M1U" id="Wl6-IQ-lVM">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pz0-Vu-M1U" id="Wl6-IQ-lVM">
|
||||||
@@ -61,44 +61,32 @@
|
|||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yra-wA-O7B">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="| Going off in 23 hr 22 m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EPJ-eL-Kek">
|
||||||
<rect key="frame" x="345" y="26" width="51" height="31"/>
|
<rect key="frame" x="179" y="70" width="215" height="21"/>
|
||||||
<connections>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<action selector="switchChange:" destination="pz0-Vu-M1U" eventType="valueChanged" id="cJh-QG-G5K"/>
|
|
||||||
</connections>
|
|
||||||
</switch>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="(Going off in 23 hr 22 m)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EPJ-eL-Kek">
|
|
||||||
<rect key="frame" x="179" y="74.5" width="215" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kaa-5k-xfL" userLabel="wvmLabel">
|
|
||||||
<rect key="frame" x="246" y="11" width="0.0" height="0.0"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<accessibility key="accessibilityConfiguration">
|
|
||||||
<accessibilityTraits key="traits" staticText="YES" notEnabled="YES"/>
|
|
||||||
</accessibility>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="1"/>
|
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mQf-S4-pMF">
|
<<<<<<< Updated upstream
|
||||||
<rect key="frame" x="225" y="19" width="0.0" height="0.0"/>
|
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yra-wA-O7B">
|
||||||
|
<rect key="frame" x="345" y="26" width="49" height="31"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
</switch>
|
||||||
|
=======
|
||||||
|
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eZP-Zd-RS2" userLabel="toneLabel">
|
||||||
|
<rect key="frame" x="246" y="19" width="42" height="21"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
|
>>>>>>> Stashed changes
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="dWq-jh-9Ue" firstAttribute="baseline" secondItem="4hj-fb-FF3" secondAttribute="baseline" id="4H2-o6-GfV"/>
|
|
||||||
<constraint firstItem="YhV-X3-y1v" firstAttribute="leading" secondItem="Wl6-IQ-lVM" secondAttribute="leadingMargin" constant="10" id="Hur-MO-GTA"/>
|
<constraint firstItem="YhV-X3-y1v" firstAttribute="leading" secondItem="Wl6-IQ-lVM" secondAttribute="leadingMargin" constant="10" id="Hur-MO-GTA"/>
|
||||||
<constraint firstItem="yra-wA-O7B" firstAttribute="top" secondItem="Wl6-IQ-lVM" secondAttribute="topMargin" constant="15" id="Jvt-p2-9cx"/>
|
|
||||||
<constraint firstAttribute="trailingMargin" secondItem="EPJ-eL-Kek" secondAttribute="trailing" id="LhS-n6-zst"/>
|
<constraint firstAttribute="trailingMargin" secondItem="EPJ-eL-Kek" secondAttribute="trailing" id="LhS-n6-zst"/>
|
||||||
<constraint firstAttribute="trailingMargin" secondItem="yra-wA-O7B" secondAttribute="trailing" id="R9H-AC-mf6"/>
|
|
||||||
<constraint firstItem="mjO-SX-f31" firstAttribute="leading" secondItem="Wl6-IQ-lVM" secondAttribute="leadingMargin" constant="10" id="VOR-Go-oCw"/>
|
<constraint firstItem="mjO-SX-f31" firstAttribute="leading" secondItem="Wl6-IQ-lVM" secondAttribute="leadingMargin" constant="10" id="VOR-Go-oCw"/>
|
||||||
<constraint firstItem="EPJ-eL-Kek" firstAttribute="baseline" secondItem="YhV-X3-y1v" secondAttribute="firstBaseline" id="ilR-ei-lE6"/>
|
<constraint firstItem="EPJ-eL-Kek" firstAttribute="top" secondItem="4hj-fb-FF3" secondAttribute="bottom" constant="13" id="je6-UM-5Kc"/>
|
||||||
<constraint firstItem="dWq-jh-9Ue" firstAttribute="leading" secondItem="mjO-SX-f31" secondAttribute="trailing" constant="8" symbolic="YES" id="ocx-bN-3M8"/>
|
<constraint firstItem="dWq-jh-9Ue" firstAttribute="leading" secondItem="mjO-SX-f31" secondAttribute="trailing" constant="8" symbolic="YES" id="ocx-bN-3M8"/>
|
||||||
<constraint firstItem="EPJ-eL-Kek" firstAttribute="leading" secondItem="YhV-X3-y1v" secondAttribute="trailing" constant="8" symbolic="YES" id="po7-2V-fZ6"/>
|
<constraint firstItem="EPJ-eL-Kek" firstAttribute="leading" secondItem="YhV-X3-y1v" secondAttribute="trailing" constant="8" symbolic="YES" id="po7-2V-fZ6"/>
|
||||||
<constraint firstItem="4hj-fb-FF3" firstAttribute="top" secondItem="Wl6-IQ-lVM" secondAttribute="topMargin" constant="25" id="r6f-mB-n9x"/>
|
<constraint firstItem="4hj-fb-FF3" firstAttribute="top" secondItem="Wl6-IQ-lVM" secondAttribute="topMargin" constant="25" id="r6f-mB-n9x"/>
|
||||||
@@ -116,9 +104,12 @@
|
|||||||
<outlet property="goingOffText" destination="EPJ-eL-Kek" id="QNo-WD-AyS"/>
|
<outlet property="goingOffText" destination="EPJ-eL-Kek" id="QNo-WD-AyS"/>
|
||||||
<outlet property="repeatText" destination="YhV-X3-y1v" id="Alj-Ko-6y6"/>
|
<outlet property="repeatText" destination="YhV-X3-y1v" id="Alj-Ko-6y6"/>
|
||||||
<outlet property="time" destination="mjO-SX-f31" id="OQC-eJ-Qjc"/>
|
<outlet property="time" destination="mjO-SX-f31" id="OQC-eJ-Qjc"/>
|
||||||
<outlet property="toneLabel" destination="mQf-S4-pMF" id="gw1-8p-j1O"/>
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
<outlet property="toneLabel" destination="eZP-Zd-RS2" id="sH0-Be-ekr"/>
|
||||||
<outlet property="wvmText" destination="Kaa-5k-xfL" id="gsX-DJ-xmb"/>
|
<outlet property="wvmText" destination="Kaa-5k-xfL" id="gsX-DJ-xmb"/>
|
||||||
<segue destination="Mki-dC-5Kc" kind="show" identifier="edit-alarm" id="WYD-ju-WX0"/>
|
<segue destination="Mki-dC-5Kc" kind="show" identifier="edit-alarm" id="WYD-ju-WX0"/>
|
||||||
|
>>>>>>> Stashed changes
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</prototypes>
|
</prototypes>
|
||||||
@@ -158,12 +149,12 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LKF-bm-JFg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="LKF-bm-JFg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-494.20289855072468" y="868.52678571428567"/>
|
<point key="canvasLocation" x="-494" y="869"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Account-->
|
<!--Account-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="AccountViewController" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="BYZ-38-t0r" customClass="AccountViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
@@ -174,275 +165,233 @@
|
|||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="sXr-25-joh">
|
||||||
|
<rect key="frame" x="20" y="336" width="374" height="224"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" text="Account" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yoz-zy-c7V">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="374" height="41"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="xzo-A1-wgr">
|
||||||
|
<rect key="frame" x="0.0" y="61" width="374" height="41"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lPF-ex-duz">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="78.5" height="41"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="hbK-Dt-9tn">
|
||||||
|
<rect key="frame" x="98.5" y="0.0" width="275.5" height="41"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<textInputTraits key="textInputTraits"/>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="yvB-Yi-jqQ">
|
||||||
|
<rect key="frame" x="0.0" y="122" width="374" height="41"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="twj-MC-8je">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="73.5" height="41"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="97S-CL-8Wq">
|
||||||
|
<rect key="frame" x="93.5" y="0.0" width="280.5" height="41"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<textInputTraits key="textInputTraits" secureTextEntry="YES"/>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="htn-tb-R70">
|
||||||
|
<rect key="frame" x="0.0" y="183" width="374" height="41"/>
|
||||||
|
<subviews>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WK1-Pr-EyJ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="177" height="41"/>
|
||||||
|
<state key="normal" title="Register"/>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Exj-c9-ucM">
|
||||||
|
<rect key="frame" x="197" y="0.0" width="177" height="41"/>
|
||||||
|
<state key="normal" title="Login"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eS2-n9-bE3" userLabel="Divider Line">
|
||||||
|
<rect key="frame" x="20" y="92" width="374" height="1"/>
|
||||||
|
<color key="backgroundColor" systemColor="labelColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="1" id="Da5-Yc-aYa"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="hkW-EE-XPg">
|
||||||
|
<rect key="frame" x="20" y="109" width="374" height="321"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Wi-Rg-PJy">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="374" height="170"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZOn-IP-cwj">
|
||||||
|
<rect key="frame" x="132" y="0.0" width="110" height="110"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="110" id="6IV-Ay-C4i"/>
|
||||||
|
<constraint firstAttribute="height" constant="110" id="xCo-yb-dyX"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Username 233" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0xm-Sd-S80">
|
||||||
|
<rect key="frame" x="20" y="118" width="334" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Joined on Jan 8, 2021" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yeb-TX-Edr">
|
||||||
|
<rect key="frame" x="20" y="138.5" width="334" height="14.5"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||||
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="yeb-TX-Edr" secondAttribute="trailing" constant="20" id="E18-zy-QnK"/>
|
||||||
|
<constraint firstItem="0xm-Sd-S80" firstAttribute="leading" secondItem="3Wi-Rg-PJy" secondAttribute="leading" constant="20" id="KTh-5Z-Icc"/>
|
||||||
|
<constraint firstItem="ZOn-IP-cwj" firstAttribute="top" secondItem="3Wi-Rg-PJy" secondAttribute="top" id="aUR-fX-A3S"/>
|
||||||
|
<constraint firstItem="ZOn-IP-cwj" firstAttribute="centerX" secondItem="3Wi-Rg-PJy" secondAttribute="centerX" id="awM-Rr-agJ"/>
|
||||||
|
<constraint firstAttribute="height" constant="170" id="e42-Ry-PNW"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="0xm-Sd-S80" secondAttribute="trailing" constant="20" id="hXl-GG-yik"/>
|
||||||
|
<constraint firstItem="0xm-Sd-S80" firstAttribute="top" secondItem="ZOn-IP-cwj" secondAttribute="bottom" constant="8" symbolic="YES" id="lkR-sx-rEQ"/>
|
||||||
|
<constraint firstItem="yeb-TX-Edr" firstAttribute="leading" secondItem="3Wi-Rg-PJy" secondAttribute="leading" constant="20" id="nH7-Kg-HzV"/>
|
||||||
|
<constraint firstItem="yeb-TX-Edr" firstAttribute="top" secondItem="0xm-Sd-S80" secondAttribute="bottom" id="pUg-uh-chQ"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="3aB-bs-ULc">
|
||||||
|
<rect key="frame" x="0.0" y="190" width="374" height="31"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Auto Backup" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Syv-4b-ZfJ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="305" height="31"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ebp-xK-if6">
|
||||||
|
<rect key="frame" x="325" y="0.0" width="51" height="31"/>
|
||||||
|
</switch>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="q7T-mb-6ax">
|
||||||
|
<rect key="frame" x="0.0" y="241" width="374" height="30"/>
|
||||||
|
<subviews>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5a1-FL-1KK">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="187" height="30"/>
|
||||||
|
<color key="backgroundColor" red="0.9137254901960784" green="0.95294117647058818" blue="0.99607843137254903" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<state key="normal" title="Backup Config"/>
|
||||||
|
<userDefinedRuntimeAttributes>
|
||||||
|
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
|
||||||
|
<integer key="value" value="5"/>
|
||||||
|
</userDefinedRuntimeAttribute>
|
||||||
|
<userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
|
||||||
|
<integer key="value" value="2"/>
|
||||||
|
</userDefinedRuntimeAttribute>
|
||||||
|
<userDefinedRuntimeAttribute type="color" keyPath="layer.borderColor">
|
||||||
|
<color key="value" name="AccentColor"/>
|
||||||
|
</userDefinedRuntimeAttribute>
|
||||||
|
</userDefinedRuntimeAttributes>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1on-v1-fQc">
|
||||||
|
<rect key="frame" x="187" y="0.0" width="187" height="30"/>
|
||||||
|
<state key="normal" title="Restore Backup"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="l9d-j4-iaa">
|
||||||
|
<rect key="frame" x="0.0" y="291" width="374" height="30"/>
|
||||||
|
<state key="normal" title="Delete Account">
|
||||||
|
<color key="titleColor" systemColor="systemRedColor"/>
|
||||||
|
</state>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Manage Account and Settings" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cPO-q9-S2N">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Manage Account and Settings" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cPO-q9-S2N">
|
||||||
<rect key="frame" x="20" y="74.5" width="374" height="14.5"/>
|
<rect key="frame" x="20" y="74.5" width="374" height="14.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uu6-RV-xw3">
|
|
||||||
<rect key="frame" x="0.0" y="109" width="414" height="200"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="200" id="Kko-qD-93X"/>
|
|
||||||
</constraints>
|
|
||||||
<connections>
|
|
||||||
<segue destination="ZAC-hn-zGl" kind="embed" id="foq-Zo-2nt"/>
|
|
||||||
</connections>
|
|
||||||
</containerView>
|
|
||||||
<containerView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RSY-ha-JUW">
|
|
||||||
<rect key="frame" x="0.0" y="109" width="414" height="704"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="3gV-kF-UbK" kind="embed" id="dNK-l6-bgo"/>
|
|
||||||
</connections>
|
|
||||||
</containerView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="RSY-ha-JUW" firstAttribute="top" secondItem="cPO-q9-S2N" secondAttribute="bottom" constant="20" id="40e-vU-3lH"/>
|
<constraint firstItem="hkW-EE-XPg" firstAttribute="top" secondItem="cPO-q9-S2N" secondAttribute="bottom" constant="20" id="6om-ac-MMy"/>
|
||||||
<constraint firstItem="RSY-ha-JUW" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="FTb-v0-N63"/>
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="eS2-n9-bE3" secondAttribute="trailing" constant="20" id="7G3-4D-wJh"/>
|
||||||
|
<constraint firstItem="sXr-25-joh" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="Jul-zo-yqj"/>
|
||||||
|
<constraint firstItem="sXr-25-joh" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="WM6-BP-vb6"/>
|
||||||
<constraint firstItem="cPO-q9-S2N" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="Wio-lA-v4A"/>
|
<constraint firstItem="cPO-q9-S2N" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="Wio-lA-v4A"/>
|
||||||
<constraint firstItem="uu6-RV-xw3" firstAttribute="top" secondItem="cPO-q9-S2N" secondAttribute="bottom" constant="20" id="b4k-8J-XlA"/>
|
|
||||||
<constraint firstItem="RSY-ha-JUW" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="bap-y2-ZtA"/>
|
|
||||||
<constraint firstItem="cPO-q9-S2N" firstAttribute="top" secondItem="xUt-DT-D6S" secondAttribute="bottom" id="d6f-SJ-atD"/>
|
<constraint firstItem="cPO-q9-S2N" firstAttribute="top" secondItem="xUt-DT-D6S" secondAttribute="bottom" id="d6f-SJ-atD"/>
|
||||||
<constraint firstItem="uu6-RV-xw3" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="e63-Mb-cxd"/>
|
|
||||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="xUt-DT-D6S" secondAttribute="trailing" constant="20" id="h0T-oL-Nsj"/>
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="xUt-DT-D6S" secondAttribute="trailing" constant="20" id="h0T-oL-Nsj"/>
|
||||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="cPO-q9-S2N" secondAttribute="trailing" constant="20" id="mC6-HG-Sid"/>
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="cPO-q9-S2N" secondAttribute="trailing" constant="20" id="mC6-HG-Sid"/>
|
||||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="uu6-RV-xw3" secondAttribute="trailing" id="nQR-3Z-BvQ"/>
|
<constraint firstItem="eS2-n9-bE3" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="mgE-TM-WQ6"/>
|
||||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="RSY-ha-JUW" secondAttribute="bottom" id="oyr-RU-84k"/>
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="hkW-EE-XPg" secondAttribute="trailing" constant="20" id="olr-Sd-MDD"/>
|
||||||
|
<constraint firstItem="hkW-EE-XPg" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="pm5-wU-KhI"/>
|
||||||
|
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="sXr-25-joh" secondAttribute="trailing" constant="20" id="qEv-DD-dGy"/>
|
||||||
<constraint firstItem="xUt-DT-D6S" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="10" id="vI0-ip-gSt"/>
|
<constraint firstItem="xUt-DT-D6S" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="10" id="vI0-ip-gSt"/>
|
||||||
<constraint firstItem="xUt-DT-D6S" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="yqR-Rd-3o7"/>
|
<constraint firstItem="xUt-DT-D6S" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="yqR-Rd-3o7"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
<variation key="default">
|
||||||
|
<mask key="subviews">
|
||||||
|
<exclude reference="sXr-25-joh"/>
|
||||||
|
<exclude reference="eS2-n9-bE3"/>
|
||||||
|
</mask>
|
||||||
|
</variation>
|
||||||
</view>
|
</view>
|
||||||
<tabBarItem key="tabBarItem" title="Account" image="person.crop.circle" catalog="system" selectedImage="person.crop.circle" id="pml-pe-eBz"/>
|
<tabBarItem key="tabBarItem" title="Account" image="person.crop.circle" catalog="system" selectedImage="person.crop.circle" id="pml-pe-eBz"/>
|
||||||
<connections>
|
|
||||||
<outlet property="vLogin" destination="uu6-RV-xw3" id="HDR-hN-k2a"/>
|
|
||||||
<outlet property="vManage" destination="RSY-ha-JUW" id="gYK-lZ-5iB"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="897" y="869"/>
|
<point key="canvasLocation" x="242" y="869"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Debug-->
|
<!--Item-->
|
||||||
<scene sceneID="x23-kV-b5u">
|
<scene sceneID="x23-kV-b5u">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="r8W-6e-Hn2" customClass="DebugViewController" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="r8W-6e-Hn2" customClass="TestingViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="xd9-wc-Vc7">
|
<view key="view" contentMode="scaleToFill" id="xd9-wc-Vc7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Debug" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="w0P-Jb-Y3X">
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GhB-GA-PWg">
|
||||||
<rect key="frame" x="20" y="54" width="374" height="20.5"/>
|
<rect key="frame" x="93" y="128" width="229" height="40"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<nil key="textColor"/>
|
<fontDescription key="fontDescription" type="system" pointSize="23"/>
|
||||||
<nil key="highlightedColor"/>
|
<state key="normal" title="Send Accel. to Console"/>
|
||||||
</label>
|
<connections>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="What is this?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tcW-ZW-HgZ">
|
<action selector="getAccel:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="hDu-Hj-vnU"/>
|
||||||
<rect key="frame" x="20" y="74.5" width="374" height="14.5"/>
|
</connections>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
</button>
|
||||||
<nil key="textColor"/>
|
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8o4-jF-mCC">
|
||||||
<nil key="highlightedColor"/>
|
<rect key="frame" x="154" y="232" width="110" height="41"/>
|
||||||
</label>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="oTa-Ok-U9h">
|
<fontDescription key="fontDescription" type="system" pointSize="24"/>
|
||||||
<rect key="frame" x="20" y="109" width="374" height="266"/>
|
<state key="normal" title="Send Notif"/>
|
||||||
<subviews>
|
<connections>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8o4-jF-mCC">
|
<action selector="sendNotification:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="ry0-VK-YRb"/>
|
||||||
<rect key="frame" x="0.0" y="0.0" width="374" height="36"/>
|
</connections>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
</button>
|
||||||
<state key="normal" title="Send Notification"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="sendNotification:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="ry0-VK-YRb"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VdA-Ax-Nzf">
|
|
||||||
<rect key="frame" x="0.0" y="46" width="374" height="36"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
|
||||||
<state key="normal" title="Delete All Alarms"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="deleteAlarm:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="gqI-7A-W9C"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zrg-H8-O73">
|
|
||||||
<rect key="frame" x="0.0" y="92" width="374" height="36"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
|
||||||
<state key="normal" title="Switch to Dark Mode"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="switchViewingMode:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="GFd-hP-L8n"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oK7-CW-NUv">
|
|
||||||
<rect key="frame" x="0.0" y="138" width="374" height="36"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
|
||||||
<state key="normal" title="Add activating alarm"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="addAlarm:" destination="r8W-6e-Hn2" eventType="touchUpInside" id="Ctz-XA-1z2"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="↓ Add Alarm Wvm ↓" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="39b-xF-Uu5">
|
|
||||||
<rect key="frame" x="0.0" y="184" width="374" height="36"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="cDZ-bm-jzY">
|
|
||||||
<rect key="frame" x="0.0" y="230" width="374" height="36"/>
|
|
||||||
<subviews>
|
|
||||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" wraps="YES" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="Em8-2h-OLJ">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="94" height="36"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="wvmStepperChange:" destination="r8W-6e-Hn2" eventType="valueChanged" id="8FF-vP-TMD"/>
|
|
||||||
</connections>
|
|
||||||
</stepper>
|
|
||||||
<textField opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="0" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="xm9-jG-GRO">
|
|
||||||
<rect key="frame" x="114" y="0.0" width="260" height="36"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="numberPad" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<viewLayoutGuide key="safeArea" id="hX1-uy-i87"/>
|
<viewLayoutGuide key="safeArea" id="hX1-uy-i87"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="w0P-Jb-Y3X" firstAttribute="top" secondItem="hX1-uy-i87" secondAttribute="top" constant="10" id="42B-VU-jVH"/>
|
|
||||||
<constraint firstItem="hX1-uy-i87" firstAttribute="trailing" secondItem="oTa-Ok-U9h" secondAttribute="trailing" constant="20" id="BZf-0X-jDK"/>
|
|
||||||
<constraint firstItem="oTa-Ok-U9h" firstAttribute="leading" secondItem="hX1-uy-i87" secondAttribute="leading" constant="20" id="DjJ-yy-Jc0"/>
|
|
||||||
<constraint firstItem="tcW-ZW-HgZ" firstAttribute="leading" secondItem="hX1-uy-i87" secondAttribute="leading" constant="20" id="MMc-9X-wep"/>
|
|
||||||
<constraint firstItem="hX1-uy-i87" firstAttribute="trailing" secondItem="tcW-ZW-HgZ" secondAttribute="trailing" constant="20" id="TuV-bF-NqF"/>
|
|
||||||
<constraint firstItem="oTa-Ok-U9h" firstAttribute="top" secondItem="tcW-ZW-HgZ" secondAttribute="bottom" constant="20" id="Xiz-02-dUA"/>
|
|
||||||
<constraint firstItem="tcW-ZW-HgZ" firstAttribute="top" secondItem="w0P-Jb-Y3X" secondAttribute="bottom" id="i43-E7-hoY"/>
|
|
||||||
<constraint firstItem="w0P-Jb-Y3X" firstAttribute="leading" secondItem="hX1-uy-i87" secondAttribute="leading" constant="20" id="mKZ-sC-tZt"/>
|
|
||||||
<constraint firstItem="hX1-uy-i87" firstAttribute="trailing" secondItem="w0P-Jb-Y3X" secondAttribute="trailing" constant="20" id="zYy-da-HnI"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
</view>
|
||||||
<tabBarItem key="tabBarItem" title="Debug" image="hammer.fill" catalog="system" selectedImage="hammer.fill" id="QZ2-fJ-hLg"/>
|
<tabBarItem key="tabBarItem" title="Item" id="QZ2-fJ-hLg"/>
|
||||||
<connections>
|
|
||||||
<outlet property="userModeButton" destination="Zrg-H8-O73" id="9sz-f3-9pJ"/>
|
|
||||||
<outlet property="wvmInput" destination="xm9-jG-GRO" id="3x2-fp-i3W"/>
|
|
||||||
<outlet property="wvmStepper" destination="Em8-2h-OLJ" id="uJ6-sU-PZQ"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="mZu-oh-Lmb" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="mZu-oh-Lmb" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1652" y="869"/>
|
<point key="canvasLocation" x="952" y="869"/>
|
||||||
</scene>
|
|
||||||
<!--Stopwatch-->
|
|
||||||
<scene sceneID="HUO-7o-NzS">
|
|
||||||
<objects>
|
|
||||||
<viewController id="PYt-Lf-JNp" customClass="StopwatchViewController" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="wac-VZ-OTJ">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="Apb-MM-jue">
|
|
||||||
<rect key="frame" x="20" y="224.5" width="374" height="588.5"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="4j4-zF-LWF">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="374" height="70"/>
|
|
||||||
<subviews>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Jse-AL-cOs">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="374" height="30"/>
|
|
||||||
<color key="backgroundColor" name="AccentColor"/>
|
|
||||||
<state key="normal" title="Start">
|
|
||||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
</state>
|
|
||||||
<userDefinedRuntimeAttributes>
|
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="layer.cornerRadius" value="10"/>
|
|
||||||
</userDefinedRuntimeAttributes>
|
|
||||||
<connections>
|
|
||||||
<action selector="start:" destination="PYt-Lf-JNp" eventType="touchUpInside" id="ucf-fN-SgP"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Q7i-3U-NbI">
|
|
||||||
<rect key="frame" x="0.0" y="40" width="374" height="30"/>
|
|
||||||
<color key="backgroundColor" name="AccentColor"/>
|
|
||||||
<state key="normal" title="Reset">
|
|
||||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
</state>
|
|
||||||
<userDefinedRuntimeAttributes>
|
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="layer.cornerRadius" value="10"/>
|
|
||||||
</userDefinedRuntimeAttributes>
|
|
||||||
<connections>
|
|
||||||
<action selector="lapOrReset:" destination="PYt-Lf-JNp" eventType="touchUpInside" id="3ff-of-rJl"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="O7B-Tj-vT0">
|
|
||||||
<rect key="frame" x="0.0" y="78" width="374" height="510.5"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="lapCell" id="2Je-Lw-ZAb">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="374" height="43.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2Je-Lw-ZAb" id="HNi-3j-MIV">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="PYt-Lf-JNp" id="HRZ-MF-SqM"/>
|
|
||||||
<outlet property="delegate" destination="PYt-Lf-JNp" id="uCp-Op-uuX"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stopwatch" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tq0-EC-Sco">
|
|
||||||
<rect key="frame" x="20" y="54" width="374" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Measure How Long Something Takes" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8n2-Ve-Po9">
|
|
||||||
<rect key="frame" x="20" y="74.5" width="374" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="55a-Ig-Fri" customClass="StopwatchText" customModule="GetGoing" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="51" y="109" width="312.5" height="95.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="thin" pointSize="80"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<viewLayoutGuide key="safeArea" id="0Lp-Fy-R0s"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="55a-Ig-Fri" firstAttribute="top" secondItem="8n2-Ve-Po9" secondAttribute="bottom" constant="20" id="0wf-3m-bdN"/>
|
|
||||||
<constraint firstItem="0Lp-Fy-R0s" firstAttribute="bottom" secondItem="Apb-MM-jue" secondAttribute="bottom" id="4Rv-Vr-MaH"/>
|
|
||||||
<constraint firstItem="8n2-Ve-Po9" firstAttribute="trailing" secondItem="Apb-MM-jue" secondAttribute="trailing" id="9wh-TF-Ner"/>
|
|
||||||
<constraint firstItem="Apb-MM-jue" firstAttribute="top" secondItem="55a-Ig-Fri" secondAttribute="bottom" constant="20" id="BWt-OI-Yx3"/>
|
|
||||||
<constraint firstItem="0Lp-Fy-R0s" firstAttribute="trailing" secondItem="Apb-MM-jue" secondAttribute="trailing" constant="20" id="ENw-M0-fRs"/>
|
|
||||||
<constraint firstItem="Tq0-EC-Sco" firstAttribute="trailing" secondItem="8n2-Ve-Po9" secondAttribute="trailing" id="OZA-d4-Gz3"/>
|
|
||||||
<constraint firstItem="Apb-MM-jue" firstAttribute="leading" secondItem="0Lp-Fy-R0s" secondAttribute="leading" constant="20" id="l5v-Y0-GXP"/>
|
|
||||||
<constraint firstItem="Tq0-EC-Sco" firstAttribute="leading" secondItem="8n2-Ve-Po9" secondAttribute="leading" id="rbK-gY-FkN"/>
|
|
||||||
<constraint firstItem="55a-Ig-Fri" firstAttribute="centerX" secondItem="wac-VZ-OTJ" secondAttribute="centerX" id="v5F-QZ-Aet"/>
|
|
||||||
<constraint firstItem="8n2-Ve-Po9" firstAttribute="leading" secondItem="Apb-MM-jue" secondAttribute="leading" id="vvf-Gl-biw"/>
|
|
||||||
<constraint firstItem="Tq0-EC-Sco" firstAttribute="top" secondItem="0Lp-Fy-R0s" secondAttribute="top" constant="10" id="xRI-eT-4UM"/>
|
|
||||||
<constraint firstItem="8n2-Ve-Po9" firstAttribute="top" secondItem="Tq0-EC-Sco" secondAttribute="bottom" id="zMA-9E-p8y"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<tabBarItem key="tabBarItem" title="Stopwatch" image="stopwatch" catalog="system" selectedImage="stopwatch" id="hNc-UI-vIQ"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="resetButton" destination="Q7i-3U-NbI" id="gYT-DB-XpP"/>
|
|
||||||
<outlet property="startButton" destination="Jse-AL-cOs" id="iq2-xE-Nir"/>
|
|
||||||
<outlet property="tableView" destination="O7B-Tj-vT0" id="kfu-vG-sgE"/>
|
|
||||||
<outlet property="timeLabel" destination="55a-Ig-Fri" id="8eb-Ow-UF1"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="ZlX-5W-Pjn" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="192.75362318840581" y="868.52678571428567"/>
|
|
||||||
</scene>
|
</scene>
|
||||||
<!--Add Alarm View Controller-->
|
<!--Add Alarm View Controller-->
|
||||||
<scene sceneID="sJi-AZ-FUw">
|
<scene sceneID="sJi-AZ-FUw">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="Mki-dC-5Kc" customClass="AddAlarmViewController" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="Mki-dC-5Kc" customClass="AddAlarmViewController" customModule="ProjectClock" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="YRJ-pi-UhZ">
|
<view key="view" contentMode="scaleToFill" id="YRJ-pi-UhZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
@@ -455,9 +404,6 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
||||||
<state key="normal" image="checkmark" catalog="system"/>
|
<state key="normal" image="checkmark" catalog="system"/>
|
||||||
<connections>
|
|
||||||
<action selector="addAlarmButton:" destination="Mki-dC-5Kc" eventType="touchUpInside" id="AiG-Cc-DlR"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6d8-Gi-Ipa">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6d8-Gi-Ipa">
|
||||||
<rect key="frame" x="20" y="20" width="30" height="30"/>
|
<rect key="frame" x="20" y="20" width="30" height="30"/>
|
||||||
@@ -467,9 +413,6 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
||||||
<state key="normal" image="trash.fill" catalog="system"/>
|
<state key="normal" image="trash.fill" catalog="system"/>
|
||||||
<connections>
|
|
||||||
<action selector="cancelAlarmButton:" destination="Mki-dC-5Kc" eventType="touchUpInside" id="4zo-hG-Mnk"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Going off in 23 hr 59 min" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xqs-n0-H9G">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Going off in 23 hr 59 min" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xqs-n0-H9G">
|
||||||
<rect key="frame" x="20" y="40.5" width="374" height="14.5"/>
|
<rect key="frame" x="20" y="40.5" width="374" height="14.5"/>
|
||||||
@@ -491,14 +434,11 @@
|
|||||||
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="N7f-vi-SFU">
|
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="N7f-vi-SFU">
|
||||||
<rect key="frame" x="0.0" y="-15.5" width="374" height="811"/>
|
<rect key="frame" x="0.0" y="-15.5" width="374" height="811"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<datePicker multipleTouchEnabled="YES" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" datePickerMode="time" minuteInterval="1" style="wheels" translatesAutoresizingMaskIntoConstraints="NO" id="qSt-1V-2DQ">
|
<datePicker multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" datePickerMode="time" minuteInterval="1" style="wheels" translatesAutoresizingMaskIntoConstraints="NO" id="qSt-1V-2DQ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="374" height="220"/>
|
<rect key="frame" x="0.0" y="0.0" width="374" height="220"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="220" id="VCm-rY-H4H"/>
|
<constraint firstAttribute="height" constant="220" id="VCm-rY-H4H"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
|
||||||
<action selector="alarmTimeUpdated:" destination="Mki-dC-5Kc" eventType="valueChanged" id="4e5-2Z-7gj"/>
|
|
||||||
</connections>
|
|
||||||
</datePicker>
|
</datePicker>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Alarm Ringtone" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O5c-Ga-0xh">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Alarm Ringtone" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O5c-Ga-0xh">
|
||||||
<rect key="frame" x="0.0" y="228" width="374" height="20.5"/>
|
<rect key="frame" x="0.0" y="228" width="374" height="20.5"/>
|
||||||
@@ -507,55 +447,41 @@
|
|||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="bCI-jj-foN">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="bCI-jj-foN">
|
||||||
<rect key="frame" x="0.0" y="258.5" width="374" height="150"/>
|
<rect key="frame" x="0.0" y="258.5" width="374" height="60"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<pickerView tag="2" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JbI-Jp-vvx" customClass="RingtonesDataSource" customModule="GetGoing" customModuleProvider="target">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cZ8-yN-fp6">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="374" height="150"/>
|
<rect key="frame" x="0.0" y="0.0" width="374" height="30"/>
|
||||||
<constraints>
|
<state key="normal" title="Pick From iOS Default Ringtones"/>
|
||||||
<constraint firstAttribute="height" constant="150" id="OgV-hH-xcq"/>
|
</button>
|
||||||
</constraints>
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="A3Z-Qp-XmN">
|
||||||
</pickerView>
|
<rect key="frame" x="0.0" y="30" width="374" height="30"/>
|
||||||
|
<state key="normal" title="Pick From Sound Library"/>
|
||||||
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Wakeup Verification Method" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E4B-9M-iJd">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Wakeup Verification Method" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E4B-9M-iJd">
|
||||||
<rect key="frame" x="0.0" y="428.5" width="374" height="20.5"/>
|
<rect key="frame" x="0.0" y="338.5" width="374" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<pickerView tag="1" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oae-b2-SoF" customClass="WVMDataSource" customModule="GetGoing" customModuleProvider="target">
|
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oae-b2-SoF" customClass="WVMDataSource" customModule="ProjectClock" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="457" width="374" height="150"/>
|
<rect key="frame" x="0.0" y="367" width="374" height="150"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="150" id="C0G-jE-CPx"/>
|
<constraint firstAttribute="height" constant="150" id="C0G-jE-CPx"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</pickerView>
|
</pickerView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Others" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eZX-HE-BKB">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Others" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eZX-HE-BKB">
|
||||||
<rect key="frame" x="0.0" y="615" width="374" height="20.5"/>
|
<rect key="frame" x="0.0" y="525" width="374" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="e8v-cM-bxf">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="e8v-cM-bxf">
|
||||||
<rect key="frame" x="20" y="643.5" width="334" height="116"/>
|
<rect key="frame" x="20" y="553.5" width="334" height="72"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="gdH-Ok-RIH">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="334" height="34"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Alarm Name:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5LP-Zk-4Sh">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="98.5" height="34"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Alarm" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="bJg-zp-Aan">
|
|
||||||
<rect key="frame" x="118.5" y="0.0" width="215.5" height="34"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<textInputTraits key="textInputTraits"/>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="iAX-G9-qxh">
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="iAX-G9-qxh">
|
||||||
<rect key="frame" x="0.0" y="44" width="334" height="31"/>
|
<rect key="frame" x="0.0" y="0.0" width="334" height="31"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Repeat on Weekdays" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="voA-xY-em1">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Repeat on Weekdays" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="voA-xY-em1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="265" height="31"/>
|
<rect key="frame" x="0.0" y="0.0" width="265" height="31"/>
|
||||||
@@ -565,14 +491,11 @@
|
|||||||
</label>
|
</label>
|
||||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5oN-BL-Xtu">
|
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5oN-BL-Xtu">
|
||||||
<rect key="frame" x="285" y="0.0" width="51" height="31"/>
|
<rect key="frame" x="285" y="0.0" width="51" height="31"/>
|
||||||
<connections>
|
|
||||||
<action selector="alarmTimeUpdated:" destination="Mki-dC-5Kc" eventType="valueChanged" id="KrO-lK-2lG"/>
|
|
||||||
</connections>
|
|
||||||
</switch>
|
</switch>
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="A0Y-z2-p4h">
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="A0Y-z2-p4h">
|
||||||
<rect key="frame" x="0.0" y="85" width="334" height="31"/>
|
<rect key="frame" x="0.0" y="41" width="334" height="31"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Repeat on Weekends" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gcr-Xt-GY4">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Repeat on Weekends" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gcr-Xt-GY4">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="265" height="31"/>
|
<rect key="frame" x="0.0" y="0.0" width="265" height="31"/>
|
||||||
@@ -582,9 +505,6 @@
|
|||||||
</label>
|
</label>
|
||||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WPM-Fh-sRB">
|
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WPM-Fh-sRB">
|
||||||
<rect key="frame" x="285" y="0.0" width="51" height="31"/>
|
<rect key="frame" x="285" y="0.0" width="51" height="31"/>
|
||||||
<connections>
|
|
||||||
<action selector="alarmTimeUpdated:" destination="Mki-dC-5Kc" eventType="valueChanged" id="FGC-Oc-gFj"/>
|
|
||||||
</connections>
|
|
||||||
</switch>
|
</switch>
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
@@ -649,186 +569,20 @@
|
|||||||
</view>
|
</view>
|
||||||
<navigationItem key="navigationItem" id="Ydw-dw-vLC"/>
|
<navigationItem key="navigationItem" id="Ydw-dw-vLC"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="alarmNameTextField" destination="bJg-zp-Aan" id="Itt-3v-GJB"/>
|
|
||||||
<outlet property="repeatWeekdaysSwitch" destination="5oN-BL-Xtu" id="gqb-l3-1jZ"/>
|
|
||||||
<outlet property="repeatWeekendsSwitch" destination="WPM-Fh-sRB" id="LB7-zW-jpC"/>
|
|
||||||
<outlet property="ringtonePicker" destination="JbI-Jp-vvx" id="mPO-Ej-DNU"/>
|
|
||||||
<outlet property="scrollView" destination="ybc-8d-6pJ" id="m1B-ff-zeC"/>
|
<outlet property="scrollView" destination="ybc-8d-6pJ" id="m1B-ff-zeC"/>
|
||||||
<outlet property="scrollViewInner" destination="N7f-vi-SFU" id="LQW-6T-f6x"/>
|
<outlet property="scrollViewInner" destination="N7f-vi-SFU" id="LQW-6T-f6x"/>
|
||||||
<outlet property="timePicker" destination="qSt-1V-2DQ" id="aDM-jE-0OP"/>
|
<outlet property="timePicker" destination="qSt-1V-2DQ" id="aDM-jE-0OP"/>
|
||||||
<outlet property="timeTillAlarmLabel" destination="Xqs-n0-H9G" id="fg6-18-oAG"/>
|
|
||||||
<outlet property="viewTitle" destination="97A-aJ-4ms" id="MZL-qA-2AV"/>
|
|
||||||
<outlet property="wvmPicker" destination="oae-b2-SoF" id="ADd-1r-iNM"/>
|
<outlet property="wvmPicker" destination="oae-b2-SoF" id="ADd-1r-iNM"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="0OD-e8-Pfh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="0OD-e8-Pfh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-494.20289855072468" y="1575.6696428571429"/>
|
<point key="canvasLocation" x="-494" y="1576"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Alarm Activation View Controller-->
|
<!--Tab Bar Controller-->
|
||||||
<scene sceneID="21H-AI-pzd">
|
|
||||||
<objects>
|
|
||||||
<viewController id="hDW-11-g9U" customClass="AlarmActivationViewController" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="BZn-UX-vps">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="842"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="17:40" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vmg-Oj-8tB">
|
|
||||||
<rect key="frame" x="20" y="20" width="374" height="137.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="thin" pointSize="115"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Jan 17, Sunday" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="42Q-0v-ZS3">
|
|
||||||
<rect key="frame" x="20" y="152.5" width="374" height="25.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="21"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OMQ-qK-fej" userLabel="factorView">
|
|
||||||
<rect key="frame" x="20" y="358" width="374" height="126.5"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Solve: " lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CZ0-R6-FDq">
|
|
||||||
<rect key="frame" x="20" y="20" width="334" height="32.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Input One Answer" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eTb-g7-8x3">
|
|
||||||
<rect key="frame" x="20" y="72.5" width="334" height="34"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<textInputTraits key="textInputTraits" keyboardType="numberPad"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="checkBinomialSolution:" destination="hDW-11-g9U" eventType="editingChanged" id="nuZ-mN-UR3"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="CZ0-R6-FDq" firstAttribute="top" secondItem="OMQ-qK-fej" secondAttribute="top" constant="20" id="4Pl-nG-fJl"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="eTb-g7-8x3" secondAttribute="bottom" constant="20" id="7Kx-WE-DEo"/>
|
|
||||||
<constraint firstItem="eTb-g7-8x3" firstAttribute="leading" secondItem="OMQ-qK-fej" secondAttribute="leading" constant="20" id="RAE-gz-B20"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="eTb-g7-8x3" secondAttribute="trailing" constant="20" id="cKs-ik-No4"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="CZ0-R6-FDq" secondAttribute="trailing" constant="20" id="piD-rs-vaT"/>
|
|
||||||
<constraint firstItem="CZ0-R6-FDq" firstAttribute="leading" secondItem="OMQ-qK-fej" secondAttribute="leading" constant="20" id="vSh-fq-ZxW"/>
|
|
||||||
<constraint firstItem="eTb-g7-8x3" firstAttribute="top" secondItem="CZ0-R6-FDq" secondAttribute="bottom" constant="20" id="yME-j6-vSk"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zph-rd-rPI" userLabel="shakeView">
|
|
||||||
<rect key="frame" x="20" y="391" width="374" height="60.5"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Shake your phone to dismiss" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nK2-jK-GsP">
|
|
||||||
<rect key="frame" x="20" y="20" width="334" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="nK2-jK-GsP" secondAttribute="trailing" constant="20" id="Pao-Rp-2Y5"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="nK2-jK-GsP" secondAttribute="bottom" constant="20" id="b7B-x1-rDl"/>
|
|
||||||
<constraint firstItem="nK2-jK-GsP" firstAttribute="leading" secondItem="Zph-rd-rPI" secondAttribute="leading" constant="20" id="ngJ-SU-kAf"/>
|
|
||||||
<constraint firstItem="nK2-jK-GsP" firstAttribute="top" secondItem="Zph-rd-rPI" secondAttribute="top" constant="20" id="yVq-ID-HNc"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NmX-iJ-1D0" userLabel="rpsView">
|
|
||||||
<rect key="frame" x="20" y="338.5" width="374" height="165"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="213-rS-oYj">
|
|
||||||
<rect key="frame" x="20" y="103" width="334" height="42"/>
|
|
||||||
<subviews>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ndj-R4-AKk">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="111.5" height="42"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="25"/>
|
|
||||||
<state key="normal" title="✊"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="rpsChoice:" destination="hDW-11-g9U" eventType="touchUpInside" id="w4O-MC-ohr"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button opaque="NO" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Iiv-Co-j8G">
|
|
||||||
<rect key="frame" x="111.5" y="0.0" width="111" height="42"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="25"/>
|
|
||||||
<state key="normal" title="✋"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="rpsChoice:" destination="hDW-11-g9U" eventType="touchUpInside" id="9mT-0M-Taq"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button opaque="NO" tag="2" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="U4y-ls-yuK">
|
|
||||||
<rect key="frame" x="222.5" y="0.0" width="111.5" height="42"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="25"/>
|
|
||||||
<state key="normal" title="✌"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="rpsChoice:" destination="hDW-11-g9U" eventType="touchUpInside" id="Dnx-vm-mRk"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="What do you choose?" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PBY-xU-AE1">
|
|
||||||
<rect key="frame" x="20" y="62.5" width="334" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Rock Paper Scissors" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HEP-oh-VSY">
|
|
||||||
<rect key="frame" x="20" y="20" width="334" height="32.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="27"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="PBY-xU-AE1" secondAttribute="trailing" constant="20" id="1as-87-SzK"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="HEP-oh-VSY" secondAttribute="trailing" constant="20" id="2I1-pc-Tqk"/>
|
|
||||||
<constraint firstItem="PBY-xU-AE1" firstAttribute="top" secondItem="HEP-oh-VSY" secondAttribute="bottom" constant="10" id="6t4-qZ-fxr"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="213-rS-oYj" secondAttribute="trailing" constant="20" id="QKl-2D-O3G"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="213-rS-oYj" secondAttribute="bottom" constant="20" id="ZSk-dm-n4J"/>
|
|
||||||
<constraint firstItem="HEP-oh-VSY" firstAttribute="leading" secondItem="NmX-iJ-1D0" secondAttribute="leading" constant="20" id="ij7-rF-uS8"/>
|
|
||||||
<constraint firstItem="PBY-xU-AE1" firstAttribute="leading" secondItem="NmX-iJ-1D0" secondAttribute="leading" constant="20" id="rQc-Ss-ITL"/>
|
|
||||||
<constraint firstItem="213-rS-oYj" firstAttribute="leading" secondItem="NmX-iJ-1D0" secondAttribute="leading" constant="20" id="sjM-gl-BmA"/>
|
|
||||||
<constraint firstItem="213-rS-oYj" firstAttribute="top" secondItem="PBY-xU-AE1" secondAttribute="bottom" constant="20" id="vNn-C6-Y65"/>
|
|
||||||
<constraint firstItem="HEP-oh-VSY" firstAttribute="top" secondItem="NmX-iJ-1D0" secondAttribute="top" constant="20" id="vmt-aJ-pbh"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
</subviews>
|
|
||||||
<viewLayoutGuide key="safeArea" id="hS6-fS-yWp"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="vmg-Oj-8tB" firstAttribute="top" secondItem="hS6-fS-yWp" secondAttribute="top" constant="20" id="5lz-9m-i9F"/>
|
|
||||||
<constraint firstItem="vmg-Oj-8tB" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="7fm-Y8-FaE"/>
|
|
||||||
<constraint firstItem="NmX-iJ-1D0" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="8M3-oo-Hx8"/>
|
|
||||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="Zph-rd-rPI" secondAttribute="trailing" constant="20" id="9PX-vo-26Q"/>
|
|
||||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="NmX-iJ-1D0" secondAttribute="trailing" constant="20" id="GHr-G7-Fe4"/>
|
|
||||||
<constraint firstItem="42Q-0v-ZS3" firstAttribute="top" secondItem="vmg-Oj-8tB" secondAttribute="bottom" constant="-5" id="GNM-uI-sQt"/>
|
|
||||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="42Q-0v-ZS3" secondAttribute="trailing" constant="20" id="L1F-Bv-r5M"/>
|
|
||||||
<constraint firstItem="OMQ-qK-fej" firstAttribute="centerY" secondItem="BZn-UX-vps" secondAttribute="centerY" id="MvA-2F-Gdv"/>
|
|
||||||
<constraint firstItem="42Q-0v-ZS3" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="Nbf-jB-Uq5"/>
|
|
||||||
<constraint firstItem="NmX-iJ-1D0" firstAttribute="centerY" secondItem="BZn-UX-vps" secondAttribute="centerY" id="QOI-JZ-KRZ"/>
|
|
||||||
<constraint firstItem="Zph-rd-rPI" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="b5R-HH-etH"/>
|
|
||||||
<constraint firstItem="OMQ-qK-fej" firstAttribute="leading" secondItem="hS6-fS-yWp" secondAttribute="leading" constant="20" id="mKi-lK-CZ3"/>
|
|
||||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="OMQ-qK-fej" secondAttribute="trailing" constant="20" id="stB-3o-46B"/>
|
|
||||||
<constraint firstItem="Zph-rd-rPI" firstAttribute="centerY" secondItem="BZn-UX-vps" secondAttribute="centerY" id="ua1-TZ-IR3"/>
|
|
||||||
<constraint firstItem="hS6-fS-yWp" firstAttribute="trailing" secondItem="vmg-Oj-8tB" secondAttribute="trailing" constant="20" id="vVE-ID-dgj"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dateLabel" destination="42Q-0v-ZS3" id="t7y-cH-391"/>
|
|
||||||
<outlet property="puzzleAnswerInput" destination="eTb-g7-8x3" id="xIh-TJ-vhq"/>
|
|
||||||
<outlet property="puzzleQuestionLabel" destination="CZ0-R6-FDq" id="gon-N6-XTD"/>
|
|
||||||
<outlet property="puzzleView" destination="OMQ-qK-fej" id="KLT-oe-RJX"/>
|
|
||||||
<outlet property="rpsResult" destination="HEP-oh-VSY" id="W2G-eZ-mzI"/>
|
|
||||||
<outlet property="rpsView" destination="NmX-iJ-1D0" id="Rhg-Hm-2Xx"/>
|
|
||||||
<outlet property="shakeView" destination="Zph-rd-rPI" id="aJ0-W7-cAV"/>
|
|
||||||
<outlet property="timeLabel" destination="vmg-Oj-8tB" id="FJJ-No-dwF"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="78q-sr-CNz" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="2389.8550724637685" y="1101.5625"/>
|
|
||||||
</scene>
|
|
||||||
<!--Alarm Activator-->
|
|
||||||
<scene sceneID="c7a-VN-D68">
|
<scene sceneID="c7a-VN-D68">
|
||||||
<objects>
|
<objects>
|
||||||
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="s8U-C8-W2S" customClass="AlarmActivator" customModule="GetGoing" customModuleProvider="target" sceneMemberID="viewController">
|
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="s8U-C8-W2S" sceneMemberID="viewController">
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="203-Gr-Mvz">
|
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="203-Gr-Mvz">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
|
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
|
||||||
@@ -836,9 +590,7 @@
|
|||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</tabBar>
|
</tabBar>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="hDW-11-g9U" kind="showDetail" identifier="activate-alarm" destinationCreationSelector="sendAlarm:" id="z9o-Jh-fnM"/>
|
|
||||||
<segue destination="Bqt-du-DT2" kind="relationship" relationship="viewControllers" id="ZTh-ke-ZIV"/>
|
<segue destination="Bqt-du-DT2" kind="relationship" relationship="viewControllers" id="ZTh-ke-ZIV"/>
|
||||||
<segue destination="PYt-Lf-JNp" kind="relationship" relationship="viewControllers" id="uMq-nh-i7c"/>
|
|
||||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="viewControllers" destinationCreationSelector="Account" id="NN6-JO-Hmi"/>
|
<segue destination="BYZ-38-t0r" kind="relationship" relationship="viewControllers" destinationCreationSelector="Account" id="NN6-JO-Hmi"/>
|
||||||
<segue destination="r8W-6e-Hn2" kind="relationship" relationship="viewControllers" destinationCreationSelector="Testing" id="SLn-vq-UnH"/>
|
<segue destination="r8W-6e-Hn2" kind="relationship" relationship="viewControllers" destinationCreationSelector="Testing" id="SLn-vq-UnH"/>
|
||||||
</connections>
|
</connections>
|
||||||
@@ -847,6 +599,8 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-1406" y="1102"/>
|
<point key="canvasLocation" x="-1406" y="1102"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
<!--LoginVC-->
|
<!--LoginVC-->
|
||||||
<scene sceneID="erw-PT-N27">
|
<scene sceneID="erw-PT-N27">
|
||||||
<objects>
|
<objects>
|
||||||
@@ -1410,30 +1164,25 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="895.6521739130435" y="2270.7589285714284"/>
|
<point key="canvasLocation" x="895.6521739130435" y="2270.7589285714284"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
>>>>>>> Stashed changes
|
||||||
</scenes>
|
</scenes>
|
||||||
<inferredMetricsTieBreakers>
|
|
||||||
<segue reference="WYD-ju-WX0"/>
|
|
||||||
</inferredMetricsTieBreakers>
|
|
||||||
<resources>
|
<resources>
|
||||||
<image name="alarm.fill" catalog="system" width="124" height="128"/>
|
<image name="alarm.fill" catalog="system" width="124" height="128"/>
|
||||||
<image name="checkmark" catalog="system" width="128" height="114"/>
|
<image name="checkmark" catalog="system" width="128" height="114"/>
|
||||||
<image name="hammer.fill" catalog="system" width="128" height="117"/>
|
|
||||||
<image name="person.crop.circle" catalog="system" width="128" height="121"/>
|
<image name="person.crop.circle" catalog="system" width="128" height="121"/>
|
||||||
<image name="person.crop.circle.badge.checkmark" catalog="system" width="128" height="113"/>
|
|
||||||
<image name="stopwatch" catalog="system" width="121" height="128"/>
|
|
||||||
<image name="trash.fill" catalog="system" width="121" height="128"/>
|
<image name="trash.fill" catalog="system" width="121" height="128"/>
|
||||||
<namedColor name="AccentColor">
|
<namedColor name="AccentColor">
|
||||||
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</namedColor>
|
</namedColor>
|
||||||
|
<systemColor name="labelColor">
|
||||||
|
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</systemColor>
|
||||||
<systemColor name="secondaryLabelColor">
|
<systemColor name="secondaryLabelColor">
|
||||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemBackgroundColor">
|
<systemColor name="systemBackgroundColor">
|
||||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemOrangeColor">
|
|
||||||
<color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
</systemColor>
|
|
||||||
<systemColor name="systemRedColor">
|
<systemColor name="systemRedColor">
|
||||||
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// TestingViewController.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Aaron Saporito on 1/13/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import UserNotifications
|
|
||||||
|
|
||||||
class DebugViewController: EndEditingOnReturn
|
|
||||||
{
|
|
||||||
@IBOutlet weak var userModeButton: UIButton!
|
|
||||||
var darkMode = false
|
|
||||||
|
|
||||||
@IBOutlet weak var wvmInput: UITextField!
|
|
||||||
@IBOutlet weak var wvmStepper: UIStepper!
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
wvmStepper.maximumValue = Double(wvms.count - 1)
|
|
||||||
|
|
||||||
// End editing on return
|
|
||||||
wvmInput.delegate = self
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sends a test notification
|
|
||||||
@IBAction func sendNotification(_ sender: Any)
|
|
||||||
{
|
|
||||||
Notification.scheduleNotification(alarm: Alarms.fromLocal().listEnabled[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func addAlarm(_ sender: Any)
|
|
||||||
{
|
|
||||||
let (h, m, _) = Date().getHMS()
|
|
||||||
let alarm = Alarm(hour: h, minute: m, text: "Test alarm - \(h * m)", wakeMethod: wvms[Int(wvmStepper.value)], repeats: [true, true, true, true, true, true, true], lastActivate: Date().added(.minute, -1))
|
|
||||||
|
|
||||||
Alarms.fromLocal().apply { $0.list.append(alarm) }.localSave()
|
|
||||||
Notification.scheduleNotification(alarm: alarm)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func deleteAlarm(_ sender: Any)
|
|
||||||
{
|
|
||||||
Alarms.fromLocal().apply { $0.list.removeAll() }.localSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func wvmStepperChange(_ sender: Any)
|
|
||||||
{
|
|
||||||
wvmInput.text = String(Int(wvmStepper.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func switchViewingMode(_ sender: Any) {
|
|
||||||
if !darkMode {
|
|
||||||
view.window?.overrideUserInterfaceStyle = .dark
|
|
||||||
userModeButton.setTitle("Switch to Light Mode", for: .normal)
|
|
||||||
darkMode = true
|
|
||||||
} else {
|
|
||||||
view.window?.overrideUserInterfaceStyle = .light
|
|
||||||
userModeButton.setTitle("Switch to Dark Mode", for: .normal)
|
|
||||||
darkMode = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,19 +2,6 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSExceptionDomains</key>
|
|
||||||
<dict>
|
|
||||||
<key>localhost</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSIncludesSubdomains</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// ProjectClock
|
||||||
|
//
|
||||||
|
// Created by Hykilpikonna on 1/8/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct WVM
|
||||||
|
{
|
||||||
|
let name: String
|
||||||
|
let desc: String
|
||||||
|
}
|
||||||
|
|
||||||
|
let wvms = [
|
||||||
|
WVM(name: "Walk", desc: "Walk a few steps"),
|
||||||
|
WVM(name: "Jump", desc: "Make a few jumps"),
|
||||||
|
WVM(name: "Puzzle", desc: "Complete a simple puzzle"),
|
||||||
|
WVM(name: "Smash", desc: "It'll never truns off"),
|
||||||
|
]
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
//
|
|
||||||
// MathExpressions.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Puzzles to complete for task (math or RPS)
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CoreMotion
|
|
||||||
|
|
||||||
/**
|
|
||||||
Math element for problem generation (Credit: https://stackoverflow.com/a/43132311/7346633)
|
|
||||||
*/
|
|
||||||
enum MathElement : CustomStringConvertible {
|
|
||||||
case Integer(value: Int)
|
|
||||||
case Percentage(value: Int)
|
|
||||||
case Expression(expression: MathExpression)
|
|
||||||
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .Integer(let value): return "\(value)"
|
|
||||||
case .Percentage(let percentage): return "\(percentage)%"
|
|
||||||
case .Expression(let expr): return expr.description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nsExpressionFormatString : String {
|
|
||||||
switch self {
|
|
||||||
case .Integer(let value): return "\(value).0"
|
|
||||||
case .Percentage(let percentage): return "\(Double(percentage) / 100)"
|
|
||||||
case .Expression(let expr): return "(\(expr.description))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Math operator for problem generation (Credit: https://stackoverflow.com/a/43132311/7346633)
|
|
||||||
*/
|
|
||||||
enum MathOperator : String {
|
|
||||||
case plus = "+"
|
|
||||||
case minus = "-"
|
|
||||||
case multiply = "*"
|
|
||||||
case power = "**"
|
|
||||||
|
|
||||||
static func random() -> MathOperator {
|
|
||||||
let allMathOperators: [MathOperator] = [.plus, .minus, .multiply, .power]
|
|
||||||
let index = Int(arc4random_uniform(UInt32(allMathOperators.count)))
|
|
||||||
|
|
||||||
return allMathOperators[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Math expressions for problem generation (Credit: https://stackoverflow.com/a/43132311/7346633)
|
|
||||||
*/
|
|
||||||
class MathExpression : CustomStringConvertible {
|
|
||||||
var lhs: MathElement
|
|
||||||
var rhs: MathElement
|
|
||||||
var op: MathOperator
|
|
||||||
|
|
||||||
init(lhs: MathElement, rhs: MathElement, op: MathOperator) {
|
|
||||||
self.lhs = lhs
|
|
||||||
self.rhs = rhs
|
|
||||||
self.op = op
|
|
||||||
}
|
|
||||||
|
|
||||||
var description: String {
|
|
||||||
var leftString = ""
|
|
||||||
var rightString = ""
|
|
||||||
|
|
||||||
if case .Expression(_) = lhs {
|
|
||||||
leftString = "(\(lhs))"
|
|
||||||
} else {
|
|
||||||
leftString = lhs.description
|
|
||||||
}
|
|
||||||
if case .Expression(_) = rhs {
|
|
||||||
rightString = "(\(rhs))"
|
|
||||||
} else {
|
|
||||||
rightString = rhs.description
|
|
||||||
}
|
|
||||||
|
|
||||||
return "\(leftString) \(self.op.rawValue) \(rightString)"
|
|
||||||
}
|
|
||||||
|
|
||||||
var result: Int {
|
|
||||||
let format = "\(lhs.nsExpressionFormatString) \(op.rawValue) \(rhs.nsExpressionFormatString)"
|
|
||||||
let expr = NSExpression(format: format)
|
|
||||||
let result = expr.expressionValue(with: nil, context: nil)
|
|
||||||
return Int(round(result as! Double))
|
|
||||||
}
|
|
||||||
|
|
||||||
static func random() -> MathExpression {
|
|
||||||
let op: MathOperator = .random()
|
|
||||||
let lhs = MathElement.Integer(value: Int(arc4random_uniform(10)))
|
|
||||||
let rhs = MathElement.Integer(value: Int(arc4random_uniform(op == .power ? 3 : 10)))
|
|
||||||
|
|
||||||
return MathExpression(lhs: lhs, rhs: rhs, op: op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Generate simple problem - 2 expressions
|
|
||||||
*/
|
|
||||||
class MathExpProblem
|
|
||||||
{
|
|
||||||
let prob: String
|
|
||||||
let ans: Int
|
|
||||||
|
|
||||||
init(size: Int)
|
|
||||||
{
|
|
||||||
var expressions: [String] = []
|
|
||||||
var answer = 0
|
|
||||||
for _ in 1...size
|
|
||||||
{
|
|
||||||
let exp = MathExpression.random()
|
|
||||||
expressions.append(exp.description)
|
|
||||||
answer += exp.result
|
|
||||||
}
|
|
||||||
prob = expressions.joined(separator: " + ")
|
|
||||||
ans = answer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Generate quadratic factorization problem
|
|
||||||
*/
|
|
||||||
class QuadraticProb {
|
|
||||||
// Generates the roots
|
|
||||||
let root1 = Int.random(in: 1...10)
|
|
||||||
let root2 = Int.random(in: 1...10)
|
|
||||||
|
|
||||||
/**
|
|
||||||
Generate problem description
|
|
||||||
*/
|
|
||||||
func getProblem() -> String {
|
|
||||||
//a is 1
|
|
||||||
let b = root1 + root2 //bx
|
|
||||||
let c = root1 * root2 //x
|
|
||||||
|
|
||||||
return "x^2 + \(b)x + \(c)"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Finds the roots of the quadratic **NOTE**: the return type is [Int], not a String
|
|
||||||
*/
|
|
||||||
func getAnswer() -> [Int] {
|
|
||||||
return [root1, root2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Rock paper scissors
|
|
||||||
*/
|
|
||||||
class RPS
|
|
||||||
{
|
|
||||||
static let choices: [Choice] = [.rock, .paper, .scissors]
|
|
||||||
|
|
||||||
enum Choice: String
|
|
||||||
{
|
|
||||||
case rock = "Rock"
|
|
||||||
case paper = "Paper"
|
|
||||||
case scissors = "Scissors"
|
|
||||||
}
|
|
||||||
|
|
||||||
static func playRPS(you: Choice, computer: Choice) -> Bool
|
|
||||||
{
|
|
||||||
return you == .rock && computer == .scissors ||
|
|
||||||
you == .paper && computer == .rock ||
|
|
||||||
you == .scissors && computer == .paper
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -6,12 +6,15 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import AVFoundation
|
|
||||||
|
|
||||||
struct Family: Codable
|
struct User: Decodable
|
||||||
{
|
{
|
||||||
var fid: Int
|
var id: Int
|
||||||
var name: String
|
var name: String
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
var email: String
|
||||||
|
var pass: String
|
||||||
|
=======
|
||||||
var members: String
|
var members: String
|
||||||
// And a hidden field: admin pin
|
// And a hidden field: admin pin
|
||||||
|
|
||||||
@@ -33,19 +36,18 @@ struct Family: Codable
|
|||||||
|
|
||||||
struct WVM: Codable
|
struct WVM: Codable
|
||||||
{
|
{
|
||||||
let index: Int
|
|
||||||
let name: String
|
let name: String
|
||||||
let desc: String
|
let desc: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let wvms = [
|
let wvms = [
|
||||||
WVM(index: 0, name: "Shake", desc: "Shake your phone... aggresively!"),
|
WVM(name: "Shake", desc: "Shake your phone... aggresively!"),
|
||||||
WVM(index: 1, name: "Math 1", desc: "Easy math expression"),
|
WVM(name: "Math 1", desc: "Easy math expression"),
|
||||||
WVM(index: 2, name: "Math 2", desc: "Medium math expression"),
|
WVM(name: "Math 2", desc: "Medium math expression"),
|
||||||
WVM(index: 3, name: "Math 3", desc: "Hard math expression"),
|
WVM(name: "Math 3", desc: "Hard math expression"),
|
||||||
WVM(index: 4, name: "Factor", desc: "Factor a binomial"),
|
WVM(name: "Factor", desc: "Factor a binomial"),
|
||||||
WVM(index: 5, name: "RPS", desc: "Win a game of rock paper scissors"),
|
WVM(name: "RPS", desc: "Win a game of rock paper scissors"),
|
||||||
//WVM(name: "Smash", desc: "It'll never turn off"),
|
//WVM(name: "Smash", desc: "It'll never turn off"),
|
||||||
//WVM(name: "Walk", desc: "Walk a few steps"),
|
//WVM(name: "Walk", desc: "Walk a few steps"),
|
||||||
//WVM(name: "Jump", desc: "Make a few jumps")
|
//WVM(name: "Jump", desc: "Make a few jumps")
|
||||||
@@ -85,6 +87,7 @@ class Alarm: Codable, Equatable
|
|||||||
var wakeMethod: WVM
|
var wakeMethod: WVM
|
||||||
var alarmTone: SystemSoundID
|
var alarmTone: SystemSoundID
|
||||||
var notificationID: String
|
var notificationID: String
|
||||||
|
var toneName: String
|
||||||
|
|
||||||
/// What days does it repeat (Sun, Mon, Tue, Wed, Thu, Fri, Sat)
|
/// What days does it repeat (Sun, Mon, Tue, Wed, Thu, Fri, Sat)
|
||||||
var repeats: [Bool]
|
var repeats: [Bool]
|
||||||
@@ -111,6 +114,7 @@ class Alarm: Codable, Equatable
|
|||||||
self.lastActivate = lastActivate
|
self.lastActivate = lastActivate
|
||||||
self.alarmTone = alarmTone
|
self.alarmTone = alarmTone
|
||||||
self.notificationID = "notification.id.\(Int.random(in: 1...Int.max))"
|
self.notificationID = "notification.id.\(Int.random(in: 1...Int.max))"
|
||||||
|
self.toneName = toneName
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does it automatically disable after activating once
|
/// Does it automatically disable after activating once
|
||||||
@@ -173,4 +177,5 @@ class Alarms: Codable
|
|||||||
let now = Date()
|
let now = Date()
|
||||||
return listEnabled.filter { guard let n = $0.nextActivate else { return false }; return n < now }
|
return listEnabled.filter { guard let n = $0.nextActivate else { return false }; return n < now }
|
||||||
}
|
}
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,27 +7,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Base URL of the HTTP server
|
/// Base URL of the HTTP server
|
||||||
let baseUrl = "https://alarm-clock-api.hydev.org"
|
let baseUrl = "http://localhost:8080/api" // TODO: Production settings
|
||||||
|
let JSON = JSONDecoder()
|
||||||
/// Json class
|
|
||||||
class JSON
|
|
||||||
{
|
|
||||||
static let decoder = JSONDecoder()
|
|
||||||
static let encoder = JSONEncoder()
|
|
||||||
|
|
||||||
static func stringify<T: Encodable>(_ o: T) -> String?
|
|
||||||
{
|
|
||||||
guard let jsonData = try? encoder.encode(o) else { return nil }
|
|
||||||
return String(data: jsonData, encoding: String.Encoding.utf8)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func parse<T: Decodable>(_ type: T.Type, _ j: String) -> T?
|
|
||||||
{
|
|
||||||
return try? decoder.decode(type, from: j.data(using: .utf8)!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Local storage
|
|
||||||
let localStorage = UserDefaults(suiteName: "group.org.hydev.alarm.clock")!
|
let localStorage = UserDefaults(suiteName: "group.org.hydev.alarm.clock")!
|
||||||
|
|
||||||
/// API class
|
/// API class
|
||||||
@@ -43,32 +24,21 @@ class APIs
|
|||||||
Register the user in the database.
|
Register the user in the database.
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
- username: The user's unique username
|
- name: The user's name (this is not username because it doesn't have to be unique)
|
||||||
- password: Password hash
|
- email: The user's email (this does have to be unique)
|
||||||
|
- pass: Password (initial hash)
|
||||||
|
|
||||||
## Returns
|
## Returns
|
||||||
Success or error
|
Success or error
|
||||||
*/
|
*/
|
||||||
static let register = API<String>(loc: "/user/register")
|
static let register = API<String>(loc: "/user/register")
|
||||||
|
|
||||||
/**
|
|
||||||
Verify password and login.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
- username: The user's unique username
|
|
||||||
- password: Password hash
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Success or error
|
|
||||||
*/
|
|
||||||
static let login = API<String>(loc: "/user/login")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Delete a user from the database.
|
Delete a user from the database.
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
- username: The user's unique username
|
- email: The user's email
|
||||||
- password: Password hash
|
- pass: Password (initial hash)
|
||||||
|
|
||||||
## Returns
|
## Returns
|
||||||
Success or error
|
Success or error
|
||||||
@@ -78,120 +48,43 @@ class APIs
|
|||||||
/**
|
/**
|
||||||
Upload curent config to the cloud.
|
Upload curent config to the cloud.
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
## Parameters
|
||||||
- config: The config json
|
- config: The config json
|
||||||
|
|
||||||
## Returns
|
## Returns
|
||||||
Success or error
|
Success or error
|
||||||
*/
|
*/
|
||||||
static let uploadConfig = API<String>(loc: "/user/backup/upload")
|
static let uploadConfig = API<String>(loc: "/backup/upload")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Download the config from the cloud.
|
Download the config from the cloud.
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
## Parameters
|
||||||
None
|
None
|
||||||
|
|
||||||
## Returns
|
## Returns
|
||||||
Config Json
|
Config Json
|
||||||
*/
|
*/
|
||||||
static let downloadConfig = API<String>(loc: "/user/backup/download")
|
static let downloadConfig = API<String>(loc: "/backup/download")
|
||||||
|
|
||||||
/**
|
|
||||||
Get family info for this account
|
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
|
||||||
None
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Family object
|
|
||||||
*/
|
|
||||||
static let familyGet = API<Family>(loc: "/family/get")
|
|
||||||
|
|
||||||
/**
|
|
||||||
Create a family
|
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
|
||||||
- name: Family name
|
|
||||||
- pin: Admin pin
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Family object
|
|
||||||
*/
|
|
||||||
static let familyCreate = API<Family>(loc: "/family/create")
|
|
||||||
|
|
||||||
/**
|
|
||||||
Change a family's admin pin
|
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
|
||||||
- fid: Family ID
|
|
||||||
- oldPin: Original admin pin
|
|
||||||
- newPin: New admin pin
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Updated family object
|
|
||||||
*/
|
|
||||||
static let familyChangePin = API<Family>(loc: "/family/update_pin")
|
|
||||||
|
|
||||||
/**
|
|
||||||
Family-related action
|
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
|
||||||
- fid: Family ID
|
|
||||||
- pin: Admin pin
|
|
||||||
- action: Join / Leave / Delete
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Family object
|
|
||||||
*/
|
|
||||||
static let familyAction = API<Family>(loc: "/family/action")
|
|
||||||
|
|
||||||
/**
|
|
||||||
Get updates about alarms that other family members added
|
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
|
||||||
None
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Alarm updates
|
|
||||||
*/
|
|
||||||
static let familyAlarmUpdates = API<String>(loc: "/family/get_alarm_updates")
|
|
||||||
|
|
||||||
/**
|
|
||||||
Add alarm to a family member
|
|
||||||
|
|
||||||
## Parameters (Besides from username and password)
|
|
||||||
- fid: Family ID
|
|
||||||
- pin: Admin pin
|
|
||||||
- to: Family member's username
|
|
||||||
- alarm: Alarm json
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
Success message
|
|
||||||
*/
|
|
||||||
static let familyAddAlarm = API<String>(loc: "/family/add_alarm")
|
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Build a URLRequest with the node path and params
|
Build a URL with the node path and params
|
||||||
|
|
||||||
- Parameter api: API Node (Eg. APIs.register)
|
- Parameter api: API Node (Eg. APIs.register)
|
||||||
- Parameter params: Parameters to send to the server (Check the documentation of the API node to see which parameters you need)
|
- Parameter params: Parameters to send to the server (Check the documentation of the API node to see which parameters you need)
|
||||||
- Returns: URLRequest
|
- Returns: URL
|
||||||
*/
|
*/
|
||||||
func createRequest(_ node: String, _ params: [String: String]? = [:]) -> URLRequest
|
func createUrl(_ node: String, _ params: [String: String]? = [:]) -> URL
|
||||||
{
|
{
|
||||||
// Create URL and request
|
var url = URLComponents(string: baseUrl + node)
|
||||||
let url = URLComponents(string: baseUrl + node)
|
if let params = params
|
||||||
var request = URLRequest(url: url!.url!)
|
{
|
||||||
request.httpMethod = "POST"
|
url?.queryItems = params.map { URLQueryItem(name: $0, value: $1) }
|
||||||
|
}
|
||||||
// Put parameters inside headers
|
return url!.url!
|
||||||
params?.forEach { request.setValue($1, forHTTPHeaderField: $0) }
|
|
||||||
|
|
||||||
return request
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -208,13 +101,14 @@ func send<T: Decodable>(_ api: API<T>, _ params: [String: String]? = [:], _ succ
|
|||||||
var params = params
|
var params = params
|
||||||
if params != nil
|
if params != nil
|
||||||
{
|
{
|
||||||
if params!["username"] == nil { params!["username"] = localStorage.string(forKey: "name") }
|
if params!["email"] == nil { params!["email"] = localStorage.string(forKey: "email") }
|
||||||
if params!["password"] == nil { params!["password"] = localStorage.string(forKey: "pass") }
|
if params!["pass"] == nil { params!["pass"] = localStorage.string(forKey: "pass") }
|
||||||
if params!["fid"] == nil, let f = Family.fromLocal() { params!["fid"] = String(f.fid) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let url = createUrl(api.loc, params)
|
||||||
|
|
||||||
// Create task
|
// Create task
|
||||||
let task = URLSession.shared.dataTask(with: createRequest(api.loc, params)) { (raw, response, error) in
|
let task = URLSession.shared.dataTask(with: url) { (raw, response, error) in
|
||||||
|
|
||||||
// Check if raw data exists
|
// Check if raw data exists
|
||||||
guard let response = response as? HTTPURLResponse, let raw = raw else { err("Data doesn't exist"); return }
|
guard let response = response as? HTTPURLResponse, let raw = raw else { err("Data doesn't exist"); return }
|
||||||
@@ -222,11 +116,8 @@ func send<T: Decodable>(_ api: API<T>, _ params: [String: String]? = [:], _ succ
|
|||||||
// If success
|
// If success
|
||||||
if (200...299).contains(response.statusCode)
|
if (200...299).contains(response.statusCode)
|
||||||
{
|
{
|
||||||
// If the desired type is string, it doesn't have to parse json.
|
|
||||||
if T.self == String.self, let msg = String(data: raw, encoding: .utf8) { success(msg as! T); return }
|
|
||||||
|
|
||||||
// Parse JSON
|
// Parse JSON
|
||||||
guard let obj = try? JSON.decoder.decode(T.self, from: raw) else { err("JSON cannot be parsed"); return }
|
guard let obj = try? JSON.decode(T.self, from: raw) else { err("JSON cannot be parsed"); return }
|
||||||
|
|
||||||
// Call callback
|
// Call callback
|
||||||
success(obj)
|
success(obj)
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
//
|
|
||||||
// Notification.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Aaron Saporito on 1/24/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UserNotifications
|
|
||||||
|
|
||||||
class Notification {
|
|
||||||
|
|
||||||
//Removes the notifiation
|
|
||||||
static func removeNotification(alarm: Alarm) {
|
|
||||||
UNUserNotificationCenter.current().getPendingNotificationRequests { (notificationRequests) in
|
|
||||||
var identifiers: [String] = []
|
|
||||||
for notification:UNNotificationRequest in notificationRequests {
|
|
||||||
if notification.identifier == alarm.notificationID {
|
|
||||||
identifiers.append(notification.identifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for duplicates and adds any missing notifications
|
|
||||||
static func bulkScheduleNotifications(alarms: [Alarm]) {
|
|
||||||
var ids: [String] = []
|
|
||||||
|
|
||||||
UNUserNotificationCenter.current().getPendingNotificationRequests { (notificationRequests) in
|
|
||||||
for notification:UNNotificationRequest in notificationRequests {
|
|
||||||
ids.append(notification.identifier)
|
|
||||||
}
|
|
||||||
for alarm in alarms {
|
|
||||||
if !ids.contains(alarm.notificationID) {
|
|
||||||
scheduleNotification(alarm: alarm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func scheduleNotification(alarm: Alarm) {
|
|
||||||
let content = UNMutableNotificationContent()
|
|
||||||
|
|
||||||
//Date formatting to string
|
|
||||||
let today = Date()
|
|
||||||
let formatter1 = DateFormatter()
|
|
||||||
formatter1.dateFormat = "MMM d, h:mm"
|
|
||||||
|
|
||||||
//Notification content
|
|
||||||
content.title = alarm.text
|
|
||||||
content.subtitle = formatter1.string(from: today)
|
|
||||||
content.body = "Wake method: \(alarm.wakeMethod.name)"
|
|
||||||
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "notif.caf"))
|
|
||||||
|
|
||||||
// Notification image content
|
|
||||||
let imageName = "AlarmLogo1"
|
|
||||||
guard let imageURL = Bundle.main.url(forResource: imageName, withExtension: "png") else { return }
|
|
||||||
let attachment = try! UNNotificationAttachment(identifier: imageName, url: imageURL, options: .none)
|
|
||||||
content.attachments = [attachment]
|
|
||||||
|
|
||||||
// Scheduels alarm notification for proper time
|
|
||||||
let interval = alarm.nextActivate!.timeIntervalSince(Date())
|
|
||||||
guard interval > 0 else { return }
|
|
||||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: alarm.nextActivate!.timeIntervalSince(Date()), repeats: false)
|
|
||||||
let request = UNNotificationRequest(identifier: alarm.notificationID, content: content, trigger: trigger)
|
|
||||||
|
|
||||||
// Sends notification
|
|
||||||
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// NotificationLogic.swift
|
||||||
|
// ProjectClock
|
||||||
|
//
|
||||||
|
// Created by Aaron Saporito on 1/13/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreMotion
|
||||||
|
import UserNotifications
|
||||||
|
|
||||||
|
let motionManager = CMMotionManager()
|
||||||
|
|
||||||
|
func getAccelerometer() {
|
||||||
|
motionManager.startAccelerometerUpdates()
|
||||||
|
//print(motionManager.accelerometerData)
|
||||||
|
if let accelerometerData = motionManager.accelerometerData {
|
||||||
|
print("Acclerometer: \(accelerometerData)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func walkAction() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func jumpAction() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func puzzleAction() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func smashAction() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// SceneDelegate.swift
|
||||||
|
// ProjectClock
|
||||||
|
//
|
||||||
|
// Created by Hykilpikonna on 1/6/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
|
var window: UIWindow?
|
||||||
|
|
||||||
|
|
||||||
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||||
|
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||||
|
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||||
|
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||||
|
guard let _ = (scene as? UIWindowScene) else { return }
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidDisconnect(_ scene: UIScene) {
|
||||||
|
// Called as the scene is being released by the system.
|
||||||
|
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||||
|
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||||
|
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||||
|
// Called when the scene has moved from an inactive state to an active state.
|
||||||
|
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillResignActive(_ scene: UIScene) {
|
||||||
|
// Called when the scene will move from an active state to an inactive state.
|
||||||
|
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
|
// Called as the scene transitions from the background to the foreground.
|
||||||
|
// Use this method to undo the changes made on entering the background.
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||||
|
// Called as the scene transitions from the foreground to the background.
|
||||||
|
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||||
|
// to restore the scene back to its current state.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
//
|
|
||||||
// StopwatchViewController.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Dallon Archibald on 1/23/21.
|
|
||||||
// Reference: https://youtu.be/H691qFRpaWA
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/**
|
|
||||||
Stopwatch feature
|
|
||||||
*/
|
|
||||||
class StopwatchViewController: UIViewController
|
|
||||||
{
|
|
||||||
// UI Components
|
|
||||||
@IBOutlet weak var timeLabel: UILabel!
|
|
||||||
@IBOutlet weak var startButton: UIButton!
|
|
||||||
@IBOutlet weak var resetButton: UIButton!
|
|
||||||
@IBOutlet weak var tableView: UITableView!
|
|
||||||
|
|
||||||
// Time Components
|
|
||||||
var hours = 0
|
|
||||||
var minutes = 0
|
|
||||||
var seconds = 0
|
|
||||||
var started = false
|
|
||||||
|
|
||||||
var lappedTimes: [String] = []
|
|
||||||
var timer = Timer()
|
|
||||||
|
|
||||||
/**
|
|
||||||
Start/stop stopwatch
|
|
||||||
*/
|
|
||||||
@IBAction func start(_ sender: UIButton)
|
|
||||||
{
|
|
||||||
if !started
|
|
||||||
{
|
|
||||||
// Start timer
|
|
||||||
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(count), userInfo: nil, repeats: true)
|
|
||||||
started = true
|
|
||||||
startButton.setTitle("Stop", for: .normal)
|
|
||||||
resetButton.setTitle("Lap", for: .normal)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Stop timer
|
|
||||||
timer.invalidate()
|
|
||||||
started = false
|
|
||||||
startButton.setTitle("Start", for: .normal)
|
|
||||||
resetButton.setTitle("Reset", for: .normal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc fileprivate func count()
|
|
||||||
{
|
|
||||||
// Add time (If it goes longer than 24 hours, the hour count should go to 25)
|
|
||||||
seconds += 1
|
|
||||||
|
|
||||||
if seconds == 60
|
|
||||||
{
|
|
||||||
minutes += 1
|
|
||||||
seconds = 0
|
|
||||||
}
|
|
||||||
if minutes == 60
|
|
||||||
{
|
|
||||||
hours += 1
|
|
||||||
minutes = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set label text
|
|
||||||
timeLabel.text = String(format: "%02i:%02i:%02i", hours, minutes, seconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Lap/reset button
|
|
||||||
*/
|
|
||||||
@IBAction func lapOrReset(_ sender: UIButton)
|
|
||||||
{
|
|
||||||
if started
|
|
||||||
{
|
|
||||||
// Insert lap
|
|
||||||
lappedTimes.insert(timeLabel.text!, at: 0)
|
|
||||||
tableView.reloadData()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reset
|
|
||||||
seconds = 0
|
|
||||||
minutes = 0
|
|
||||||
seconds = 0
|
|
||||||
lappedTimes = []
|
|
||||||
timeLabel.text = "00:00:00"
|
|
||||||
tableView.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Table data source
|
|
||||||
*/
|
|
||||||
extension StopwatchViewController: UITableViewDelegate, UITableViewDataSource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
Define row count
|
|
||||||
*/
|
|
||||||
func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int
|
|
||||||
{
|
|
||||||
return lappedTimes.count
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Set cell at i
|
|
||||||
*/
|
|
||||||
func tableView(_ view: UITableView, cellForRowAt i: IndexPath) -> UITableViewCell
|
|
||||||
{
|
|
||||||
let cell = view.dequeueReusableCell(withIdentifier: "lapCell", for: i)
|
|
||||||
cell.textLabel?.text = lappedTimes[i.row]
|
|
||||||
cell.selectionStyle = .none
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Swipe left to delete cells at i
|
|
||||||
*/
|
|
||||||
func tableView(_ view: UITableView, commit: UITableViewCell.EditingStyle, forRowAt i: IndexPath)
|
|
||||||
{
|
|
||||||
if commit == .delete
|
|
||||||
{
|
|
||||||
lappedTimes.remove(at: i.row)
|
|
||||||
view.deleteRows(at: [i], with: .automatic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Class to set relative font size for the stopwatch
|
|
||||||
*/
|
|
||||||
class StopwatchText: UILabel
|
|
||||||
{
|
|
||||||
@IBInspectable var iPhoneFontSize: CGFloat = 0
|
|
||||||
{
|
|
||||||
didSet
|
|
||||||
{
|
|
||||||
overrideFontSize(iPhoneFontSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func overrideFontSize(_ fontSize: CGFloat)
|
|
||||||
{
|
|
||||||
let size = UIScreen.main.bounds.size
|
|
||||||
let width = UIDevice.current.orientation.isPortrait ? size.width : size.height
|
|
||||||
|
|
||||||
// ViewWidth-based font size
|
|
||||||
font = font.withSize(0.22 * width)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,36 +8,66 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
class DebugViewController: UIViewController
|
class TestingViewController: UIViewController {
|
||||||
{
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad() {
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
// Request notification permission
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) {
|
||||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
|
(granted, error) in
|
||||||
if success {
|
if granted {
|
||||||
print("All set!")
|
print("Authorized Notifications")
|
||||||
} else if let error = error {
|
} else {
|
||||||
print(error.localizedDescription)
|
print("Error: No notification access")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do any additional setup after loading the view.
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func getAccel(_ sender: Any) {
|
||||||
|
getAccelerometer()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Sends a test notification
|
//Sends a test notification
|
||||||
@IBAction func sendNotification(_ sender: Any)
|
@IBAction func sendNotification(_ sender: Any) {
|
||||||
{
|
let alarm = Alarm(alarmTime: Date(), text: "Good morning!", wakeMethod: WVM(name: "walking", desc: "Walk"))
|
||||||
Notification(alarm: Alarms.fromLocal().listEnabled[0]).scheduleNotification()
|
|
||||||
|
let content = UNMutableNotificationContent()
|
||||||
|
|
||||||
|
//Date formatting to string
|
||||||
|
let today = Date()
|
||||||
|
let formatter1 = DateFormatter()
|
||||||
|
formatter1.dateStyle = .long
|
||||||
|
|
||||||
|
//Notification content
|
||||||
|
content.title = alarm.text
|
||||||
|
content.subtitle = formatter1.string(from: today)
|
||||||
|
content.body = "Wake method: \(alarm.wakeMethod.name)"
|
||||||
|
|
||||||
|
// Notification image content
|
||||||
|
let imageName = "clock"
|
||||||
|
guard let imageURL = Bundle.main.url(forResource: imageName, withExtension: "png") else { return }
|
||||||
|
let attachment = try! UNNotificationAttachment(identifier: imageName, url: imageURL, options: .none)
|
||||||
|
content.attachments = [attachment]
|
||||||
|
|
||||||
|
|
||||||
|
// Readies notification to be sent
|
||||||
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
|
||||||
|
let request = UNNotificationRequest(identifier: "notification.id.01", content: content, trigger: trigger)
|
||||||
|
|
||||||
|
// Sends notification
|
||||||
|
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func addAlarm(_ sender: Any)
|
/*
|
||||||
{
|
// MARK: - Navigation
|
||||||
let (h, m, _) = Date().getHMS()
|
|
||||||
Alarms.fromLocal().apply { $0.list.append(Alarm(hour: h, minute: m, text: "Test alarm - \(h * m)", wakeMethod: wvms[1], repeats: [true, true, true, true, true, true, true], lastActivate: Date().added(.minute, -1))) }.localSave()
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func deleteAlarm(_ sender: Any)
|
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||||
{
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
Alarms.fromLocal().apply { $0.list.removeAll() }.localSave()
|
// Get the new view controller using segue.destination.
|
||||||
|
// Pass the selected object to the new view controller.
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,283 +0,0 @@
|
|||||||
//
|
|
||||||
// Utils.swift
|
|
||||||
// ProjectClock
|
|
||||||
//
|
|
||||||
// Created by Hykilpikonna on 1/17/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CryptoKit
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/// Date manipulations
|
|
||||||
extension Date
|
|
||||||
{
|
|
||||||
/// Add toString to Date
|
|
||||||
func str(_ format: String = "yyyy-MM-dd hh:mm:ss") -> String
|
|
||||||
{
|
|
||||||
let f = DateFormatter()
|
|
||||||
f.dateFormat = format
|
|
||||||
return f.string(from: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructor from components
|
|
||||||
static func create(_ year: Int, _ month: Int, _ day: Int, _ hour: Int, _ minute: Int) -> Date
|
|
||||||
{
|
|
||||||
var c = DateComponents()
|
|
||||||
c.year = year
|
|
||||||
c.month = month
|
|
||||||
c.day = day
|
|
||||||
c.hour = hour
|
|
||||||
c.minute = minute
|
|
||||||
let cal = Calendar(identifier: .gregorian)
|
|
||||||
return cal.date(from: c)!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get year, month, day
|
|
||||||
func getYMD() -> (y: Int, m: Int, d: Int)
|
|
||||||
{
|
|
||||||
let calendar = Calendar.current
|
|
||||||
let comp = calendar.dateComponents([.year, .month, .day], from: self)
|
|
||||||
return (comp.year!, comp.month!, comp.day!)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get hour, minute, seconds
|
|
||||||
func getHMS() -> (h: Int, m: Int, s: Int)
|
|
||||||
{
|
|
||||||
let calendar = Calendar.current
|
|
||||||
let comp = calendar.dateComponents([.hour, .minute, .second], from: self)
|
|
||||||
return (comp.hour!, comp.minute!, comp.second!)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get another component
|
|
||||||
func get(_ c: Calendar.Component) -> Int
|
|
||||||
{
|
|
||||||
let calendar = Calendar.current
|
|
||||||
let comp = calendar.dateComponents([c], from: self)
|
|
||||||
return comp.value(for: c)!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a new modified date
|
|
||||||
func added(_ c: Calendar.Component, _ v: Int) -> Date
|
|
||||||
{
|
|
||||||
return Calendar.current.date(byAdding: c, value: v, to: self)!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TimeInterval
|
|
||||||
{
|
|
||||||
var seconds: Int { return Int(self) % 60 }
|
|
||||||
var minutes: Int { return (Int(self) / 60) % 60 }
|
|
||||||
var hours: Int { return (Int(self) / 3600) % 24 }
|
|
||||||
var days: Int { return Int(self) / (3600 * 24) }
|
|
||||||
|
|
||||||
/// Add toString to time interval
|
|
||||||
func str() -> String
|
|
||||||
{
|
|
||||||
if days != 0 { return "\(days)d \(hours)h \(minutes)m \(seconds)s" }
|
|
||||||
else if hours != 0 { return "\(hours)h \(minutes)m \(seconds)s" }
|
|
||||||
else if days != 0 && hours == 0 { return "\(days)d \(minutes)m \(seconds)s"}
|
|
||||||
else if minutes != 0 { return "\(minutes)m \(seconds)s" }
|
|
||||||
else { return "\(seconds)s" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Apply like Kotlin
|
|
||||||
protocol HasApply {}
|
|
||||||
extension HasApply
|
|
||||||
{
|
|
||||||
@discardableResult
|
|
||||||
func apply(_ c: (Self) -> ()) -> Self
|
|
||||||
{
|
|
||||||
c(self)
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extension Alarm: HasApply {}
|
|
||||||
extension Alarms: HasApply {}
|
|
||||||
|
|
||||||
|
|
||||||
/// Hashing
|
|
||||||
extension Digest
|
|
||||||
{
|
|
||||||
var bytes: [UInt8] { Array(makeIterator()) }
|
|
||||||
var b64: String { Data(bytes).base64EncodedString() }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String
|
|
||||||
{
|
|
||||||
var sha256: String { SHA256.hash(data: self.data(using: .utf8)!).b64 }
|
|
||||||
var csv: [String] { components(separatedBy: ";") }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// UI Extensions
|
|
||||||
extension UIViewController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
Send an alert
|
|
||||||
|
|
||||||
- Parameter title: Title of the alert
|
|
||||||
- Parameter message: Body message of the alert
|
|
||||||
- Parameter okayable: Whether the alert can be okayed
|
|
||||||
*/
|
|
||||||
@discardableResult
|
|
||||||
func alert(_ title: String, _ message: String, okayable: Bool = false, _ completion: (() -> Void)? = nil) -> UIAlertController
|
|
||||||
{
|
|
||||||
// Create alert
|
|
||||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
||||||
|
|
||||||
// Add okay button if it's okayable
|
|
||||||
if okayable { alert.addAction(UIAlertAction(title: "OK", style: .default) { it in if let c = completion { c() } }) }
|
|
||||||
|
|
||||||
// Display alert
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
|
||||||
return alert
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A message is an okayable alert
|
|
||||||
@discardableResult
|
|
||||||
func msg(_ title: String, _ message: String, _ completion: (() -> Void)? = nil) -> UIAlertController
|
|
||||||
{
|
|
||||||
alert(title, message, okayable: true, completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// More convenient dismiss function
|
|
||||||
func dismiss(_ completion: (() -> Void)? = nil) { ui { self.dismiss(animated: false, completion: completion) } }
|
|
||||||
|
|
||||||
/**
|
|
||||||
Send a http request even more conveniently
|
|
||||||
*/
|
|
||||||
func sendReq<T: Decodable>(_ api: API<T>, title: String, errors: [String: String] = [:], params: [String: String]? = [:], _ success: @escaping (T) -> Void, err: ((String) -> Void)? = nil)
|
|
||||||
{
|
|
||||||
// Send request
|
|
||||||
let a = alert(title, "Please Wait")
|
|
||||||
send(api, params) { it in a.dismiss { success(it) } }
|
|
||||||
err:
|
|
||||||
{
|
|
||||||
// Call callback error function
|
|
||||||
if let err = err { err($0); return }
|
|
||||||
|
|
||||||
// Display error message
|
|
||||||
print("===== Error: \($0) =====")
|
|
||||||
let message = errors[$0.trimmingCharacters(in: .whitespaces)]
|
|
||||||
?? "Maybe the server is on fire, just wait a few hours. (Error: \($0))"
|
|
||||||
a.dismiss { self.msg("An error occurred", message) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Asks the user to enter a pin
|
|
||||||
*/
|
|
||||||
func enterPin(_ title: String = "Enter Pin", _ message: String = "Please enter your family pin.", _ then: @escaping (String) -> Void)
|
|
||||||
{
|
|
||||||
// Create alert
|
|
||||||
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
|
|
||||||
|
|
||||||
// Add next button
|
|
||||||
alert.addAction(UIAlertAction(title: "Next", style: UIAlertAction.Style.default) { it in
|
|
||||||
let t = alert.textFields![0] as UITextField
|
|
||||||
then(t.text!)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add pin text field
|
|
||||||
alert.addTextField(configurationHandler: { (t: UITextField!) in
|
|
||||||
t.placeholder = "Enter Pin"
|
|
||||||
t.isSecureTextEntry = true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Present alert
|
|
||||||
self.present(alert, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UIView
|
|
||||||
{
|
|
||||||
func hide(_ hidden: Bool = true) { isHidden = hidden }
|
|
||||||
func show(_ shown: Bool = true) { hide(!shown) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Regex Matching (Credit: https://www.hackingwithswift.com/articles/108/how-to-use-regular-expressions-in-swift)
|
|
||||||
*/
|
|
||||||
extension NSRegularExpression
|
|
||||||
{
|
|
||||||
convenience init(_ pattern: String)
|
|
||||||
{
|
|
||||||
do { try self.init(pattern: pattern) }
|
|
||||||
catch { preconditionFailure("Illegal regular expression: \(pattern).") }
|
|
||||||
}
|
|
||||||
|
|
||||||
func matches(_ string: String) -> Bool
|
|
||||||
{
|
|
||||||
let range = NSRange(location: 0, length: string.utf16.count)
|
|
||||||
return firstMatch(in: string, options: [], range: range) != nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
String convenience functions
|
|
||||||
*/
|
|
||||||
extension String
|
|
||||||
{
|
|
||||||
static func ~= (lhs: String, rhs: String) -> Bool
|
|
||||||
{
|
|
||||||
guard let regex = try? NSRegularExpression(pattern: rhs) else { return false }
|
|
||||||
let range = NSRange(location: 0, length: lhs.utf16.count)
|
|
||||||
return regex.firstMatch(in: lhs, options: [], range: range) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Better subscripting from: https://stackoverflow.com/a/46627527
|
|
||||||
subscript (bounds: CountableClosedRange<Int>) -> String
|
|
||||||
{
|
|
||||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
|
||||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
|
||||||
return String(self[start...end])
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript (bounds: CountableRange<Int>) -> String
|
|
||||||
{
|
|
||||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
|
||||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
|
||||||
return String(self[start..<end])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// More convenient ui update closure
|
|
||||||
func ui(closure: @escaping () -> Void) { DispatchQueue.main.async { closure() } }
|
|
||||||
|
|
||||||
/**
|
|
||||||
More convenient UserDefaults access (Credit: https://gist.github.com/Otbivnoe/04b8bd7984fba0cb58ca7f136fd95582)
|
|
||||||
*/
|
|
||||||
extension UserDefaults
|
|
||||||
{
|
|
||||||
subscript<T>(key: String) -> T?
|
|
||||||
{
|
|
||||||
get { return value(forKey: key) as? T }
|
|
||||||
set { set(newValue, forKey: key) }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript<T: RawRepresentable>(key: String) -> T?
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if let rawValue = value(forKey: key) as? T.RawValue { return T(rawValue: rawValue) }
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
set { self[key] = newValue?.rawValue }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EndEditingOnReturn: UIViewController, UITextFieldDelegate
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
End editing on return
|
|
||||||
*/
|
|
||||||
func textFieldShouldReturn(_ scoreText: UITextField) -> Bool
|
|
||||||
{
|
|
||||||
self.view.endEditing(true)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||