feat: begin work on user control & update server
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
"nm": "Blink Animation",
|
||||
"ddd": 0,
|
||||
"assets": [],
|
||||
"date": "2026-01-09T00:00:00Z",
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
@@ -16,11 +17,38 @@
|
||||
"nm": "Eye Container",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": {"a": 0, "k": 100},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"p": {"a": 0, "k": [100, 100, 0]},
|
||||
"a": {"a": 0, "k": [0, 0, 0]},
|
||||
"s": {"a": 0, "k": [100, 100, 100]}
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100,
|
||||
100
|
||||
]
|
||||
}
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
@@ -29,37 +57,130 @@
|
||||
"it": [
|
||||
{
|
||||
"ty": "el",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [80, 80], "h": 1},
|
||||
{"t": 15, "s": [80, 80], "h": 1},
|
||||
{"t": 20, "s": [80, 10], "h": 1},
|
||||
{"t": 25, "s": [80, 80], "h": 1},
|
||||
{"t": 35, "s": [80, 80], "h": 1},
|
||||
{"t": 40, "s": [80, 10], "h": 1},
|
||||
{"t": 45, "s": [80, 80], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
80,
|
||||
80
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 15,
|
||||
"s": [
|
||||
80,
|
||||
80
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 20,
|
||||
"s": [
|
||||
80,
|
||||
10
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 25,
|
||||
"s": [
|
||||
80,
|
||||
80
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 35,
|
||||
"s": [
|
||||
80,
|
||||
80
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 40,
|
||||
"s": [
|
||||
80,
|
||||
10
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 45,
|
||||
"s": [
|
||||
80,
|
||||
80
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"nm": "Eye Ellipse"
|
||||
},
|
||||
{
|
||||
"ty": "st",
|
||||
"c": {"a": 0, "k": [0, 0.478, 1, 1]},
|
||||
"o": {"a": 0, "k": 100},
|
||||
"w": {"a": 0, "k": 8},
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0.478,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
},
|
||||
"w": {
|
||||
"a": 0,
|
||||
"k": 8
|
||||
},
|
||||
"lc": 2,
|
||||
"lj": 2,
|
||||
"nm": "Stroke"
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"a": {"a": 0, "k": [0, 0]},
|
||||
"s": {"a": 0, "k": [100, 100]},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"o": {"a": 0, "k": 100}
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
}
|
||||
}
|
||||
],
|
||||
"nm": "Eye Shape"
|
||||
@@ -77,11 +198,38 @@
|
||||
"nm": "Pupil",
|
||||
"sr": 1,
|
||||
"ks": {
|
||||
"o": {"a": 0, "k": 100},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"p": {"a": 0, "k": [100, 100, 0]},
|
||||
"a": {"a": 0, "k": [0, 0, 0]},
|
||||
"s": {"a": 0, "k": [100, 100, 100]}
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100,
|
||||
100
|
||||
]
|
||||
}
|
||||
},
|
||||
"ao": 0,
|
||||
"shapes": [
|
||||
@@ -90,31 +238,105 @@
|
||||
"it": [
|
||||
{
|
||||
"ty": "el",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"s": {"a": 0, "k": [25, 25]},
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
25,
|
||||
25
|
||||
]
|
||||
},
|
||||
"nm": "Pupil Ellipse"
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {"a": 0, "k": [0, 0.478, 1, 1]},
|
||||
"o": {"a": 0, "k": 100},
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0.478,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
},
|
||||
"r": 1,
|
||||
"nm": "Fill"
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"a": {"a": 0, "k": [0, 0]},
|
||||
"s": {"a": 0, "k": [100, 100]},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"o": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [100], "h": 1},
|
||||
{"t": 20, "s": [0], "h": 1},
|
||||
{"t": 25, "s": [100], "h": 1},
|
||||
{"t": 40, "s": [0], "h": 1},
|
||||
{"t": 45, "s": [100], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 20,
|
||||
"s": [
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 25,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 40,
|
||||
"s": [
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 45,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
"nm": "Posture Arrow Animation",
|
||||
"ddd": 0,
|
||||
"assets": [],
|
||||
"date": "2026-01-09T00:00:00Z",
|
||||
"layers": [
|
||||
{
|
||||
"ddd": 0,
|
||||
@@ -19,29 +20,119 @@
|
||||
"o": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [0], "h": 1},
|
||||
{"t": 10, "s": [100], "h": 1},
|
||||
{"t": 60, "s": [100], "h": 1},
|
||||
{"t": 70, "s": [0], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 10,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 60,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 70,
|
||||
"s": [
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [100, 200, 0], "h": 1},
|
||||
{"t": 60, "s": [100, 200, 0], "h": 1},
|
||||
{"t": 90, "s": [100, -100, 0], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
100,
|
||||
200,
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 60,
|
||||
"s": [
|
||||
100,
|
||||
200,
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 90,
|
||||
"s": [
|
||||
100,
|
||||
-100,
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {"a": 0, "k": [0, 0, 0]},
|
||||
"s": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [0, 0, 100], "h": 1},
|
||||
{"t": 10, "s": [100, 100, 100], "h": 1},
|
||||
{"t": 60, "s": [100, 100, 100], "h": 1},
|
||||
{"t": 70, "s": [50, 50, 100], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
0,
|
||||
0,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 10,
|
||||
"s": [
|
||||
100,
|
||||
100,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 60,
|
||||
"s": [
|
||||
100,
|
||||
100,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 70,
|
||||
"s": [
|
||||
50,
|
||||
50,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -55,16 +146,95 @@
|
||||
"ks": {
|
||||
"a": 0,
|
||||
"k": {
|
||||
"i": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
|
||||
"o": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
|
||||
"i": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
"o": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
"v": [
|
||||
[0, -40],
|
||||
[-30, -10],
|
||||
[-12, -10],
|
||||
[-12, 40],
|
||||
[12, 40],
|
||||
[12, -10],
|
||||
[30, -10]
|
||||
[
|
||||
0,
|
||||
-40
|
||||
],
|
||||
[
|
||||
-30,
|
||||
-10
|
||||
],
|
||||
[
|
||||
-12,
|
||||
-10
|
||||
],
|
||||
[
|
||||
-12,
|
||||
40
|
||||
],
|
||||
[
|
||||
12,
|
||||
40
|
||||
],
|
||||
[
|
||||
12,
|
||||
-10
|
||||
],
|
||||
[
|
||||
30,
|
||||
-10
|
||||
]
|
||||
],
|
||||
"c": true
|
||||
}
|
||||
@@ -73,18 +243,53 @@
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {"a": 0, "k": [0, 0, 0, 1]},
|
||||
"o": {"a": 0, "k": 100},
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
},
|
||||
"r": 1,
|
||||
"nm": "Fill"
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"a": {"a": 0, "k": [0, 0]},
|
||||
"s": {"a": 0, "k": [100, 100]},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"o": {"a": 0, "k": 100}
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
}
|
||||
}
|
||||
],
|
||||
"nm": "Arrow Shape"
|
||||
@@ -105,29 +310,119 @@
|
||||
"o": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [0], "h": 1},
|
||||
{"t": 10, "s": [100], "h": 1},
|
||||
{"t": 60, "s": [100], "h": 1},
|
||||
{"t": 70, "s": [0], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 10,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 60,
|
||||
"s": [
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 70,
|
||||
"s": [
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"p": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [100, 200, 0], "h": 1},
|
||||
{"t": 60, "s": [100, 200, 0], "h": 1},
|
||||
{"t": 90, "s": [100, -100, 0], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
100,
|
||||
200,
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 60,
|
||||
"s": [
|
||||
100,
|
||||
200,
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 90,
|
||||
"s": [
|
||||
100,
|
||||
-100,
|
||||
0
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {"a": 0, "k": [0, 0, 0]},
|
||||
"s": {
|
||||
"a": 1,
|
||||
"k": [
|
||||
{"t": 0, "s": [0, 0, 100], "h": 1},
|
||||
{"t": 10, "s": [100, 100, 100], "h": 1},
|
||||
{"t": 60, "s": [100, 100, 100], "h": 1},
|
||||
{"t": 70, "s": [50, 50, 100], "h": 1}
|
||||
{
|
||||
"t": 0,
|
||||
"s": [
|
||||
0,
|
||||
0,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 10,
|
||||
"s": [
|
||||
100,
|
||||
100,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 60,
|
||||
"s": [
|
||||
100,
|
||||
100,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"t": 70,
|
||||
"s": [
|
||||
50,
|
||||
50,
|
||||
100
|
||||
],
|
||||
"h": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -138,24 +433,71 @@
|
||||
"it": [
|
||||
{
|
||||
"ty": "el",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"s": {"a": 0, "k": [120, 120]},
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
120,
|
||||
120
|
||||
]
|
||||
},
|
||||
"nm": "Circle Ellipse"
|
||||
},
|
||||
{
|
||||
"ty": "fl",
|
||||
"c": {"a": 0, "k": [0.9, 0.9, 0.9, 1]},
|
||||
"o": {"a": 0, "k": 30},
|
||||
"c": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0.9,
|
||||
0.9,
|
||||
0.9,
|
||||
1
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 30
|
||||
},
|
||||
"r": 1,
|
||||
"nm": "Fill"
|
||||
},
|
||||
{
|
||||
"ty": "tr",
|
||||
"p": {"a": 0, "k": [0, 0]},
|
||||
"a": {"a": 0, "k": [0, 0]},
|
||||
"s": {"a": 0, "k": [100, 100]},
|
||||
"r": {"a": 0, "k": 0},
|
||||
"o": {"a": 0, "k": 100}
|
||||
"p": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"a": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"a": 0,
|
||||
"k": [
|
||||
100,
|
||||
100
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"a": 0,
|
||||
"k": 0
|
||||
},
|
||||
"o": {
|
||||
"a": 0,
|
||||
"k": 100
|
||||
}
|
||||
}
|
||||
],
|
||||
"nm": "Circle"
|
||||
|
||||
@@ -7,11 +7,23 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Centralized Configuration System
|
||||
|
||||
/// Unified configuration class that manages all app settings in a centralized way
|
||||
struct AppSettings: Codable, Equatable {
|
||||
// Timer configurations
|
||||
var lookAwayTimer: TimerConfiguration
|
||||
var lookAwayCountdownSeconds: Int
|
||||
var blinkTimer: TimerConfiguration
|
||||
var postureTimer: TimerConfiguration
|
||||
|
||||
// User-defined timers (up to 3)
|
||||
var userTimers: [UserTimer]
|
||||
|
||||
// UI and display settings
|
||||
var subtleReminderSizePercentage: Double // 2-35% of screen width
|
||||
|
||||
// App state and behavior
|
||||
var hasCompletedOnboarding: Bool
|
||||
var launchAtLogin: Bool
|
||||
var playSounds: Bool
|
||||
@@ -20,8 +32,10 @@ struct AppSettings: Codable, Equatable {
|
||||
AppSettings(
|
||||
lookAwayTimer: TimerConfiguration(enabled: true, intervalSeconds: 20 * 60),
|
||||
lookAwayCountdownSeconds: 20,
|
||||
blinkTimer: TimerConfiguration(enabled: true, intervalSeconds: 5 * 60),
|
||||
blinkTimer: TimerConfiguration(enabled: false, intervalSeconds: 7 * 60),
|
||||
postureTimer: TimerConfiguration(enabled: true, intervalSeconds: 30 * 60),
|
||||
userTimers: [],
|
||||
subtleReminderSizePercentage: 5.0,
|
||||
hasCompletedOnboarding: false,
|
||||
launchAtLogin: false,
|
||||
playSounds: true
|
||||
@@ -33,6 +47,8 @@ struct AppSettings: Codable, Equatable {
|
||||
lhs.lookAwayCountdownSeconds == rhs.lookAwayCountdownSeconds &&
|
||||
lhs.blinkTimer == rhs.blinkTimer &&
|
||||
lhs.postureTimer == rhs.postureTimer &&
|
||||
lhs.userTimers == rhs.userTimers &&
|
||||
lhs.subtleReminderSizePercentage == rhs.subtleReminderSizePercentage &&
|
||||
lhs.hasCompletedOnboarding == rhs.hasCompletedOnboarding &&
|
||||
lhs.launchAtLogin == rhs.launchAtLogin &&
|
||||
lhs.playSounds == rhs.playSounds
|
||||
|
||||
51
Gaze/Models/UserTimer.swift
Normal file
51
Gaze/Models/UserTimer.swift
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// UserTimer.swift
|
||||
// Gaze
|
||||
//
|
||||
// Created by Mike Freno on 1/9/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Represents a user-defined timer with customizable properties
|
||||
struct UserTimer: Codable, Equatable {
|
||||
let id: String
|
||||
var type: UserTimerType
|
||||
var timeOnScreenSeconds: Int
|
||||
var message: String?
|
||||
|
||||
init(
|
||||
id: String = UUID().uuidString,
|
||||
type: UserTimerType = .subtle,
|
||||
timeOnScreenSeconds: Int = 30,
|
||||
message: String? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.type = type
|
||||
self.timeOnScreenSeconds = timeOnScreenSeconds
|
||||
self.message = message
|
||||
}
|
||||
|
||||
static func == (lhs: UserTimer, rhs: UserTimer) -> Bool {
|
||||
lhs.id == rhs.id && lhs.type == rhs.type
|
||||
&& lhs.timeOnScreenSeconds == rhs.timeOnScreenSeconds && lhs.message == rhs.message
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of user timer - subtle or overlay
|
||||
enum UserTimerType: String, Codable, CaseIterable, Identifiable {
|
||||
case subtle
|
||||
case overlay
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .subtle:
|
||||
return "Subtle"
|
||||
case .overlay:
|
||||
return "Overlay"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
Gaze/Services/AnimationService.swift
Normal file
68
Gaze/Services/AnimationService.swift
Normal file
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// AnimationService.swift
|
||||
// Gaze
|
||||
//
|
||||
// Created by Mike Freno on 1/9/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
class AnimationService {
|
||||
static let shared = AnimationService()
|
||||
|
||||
private init() {}
|
||||
|
||||
struct RemoteAnimation: Codable {
|
||||
let name: String
|
||||
let version: String
|
||||
let date: String // ISO 8601 formatted date string
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name, version, date
|
||||
}
|
||||
}
|
||||
|
||||
struct RemoteAnimationsResponse: Codable {
|
||||
let animations: [RemoteAnimation]
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
func fetchRemoteAnimations() async throws -> [RemoteAnimation] {
|
||||
guard let url = URL(string: "https://freno.me/api/Gaze/animations") else {
|
||||
throw URLError(.badURL)
|
||||
}
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(from: url)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
|
||||
guard 200...299 ~= httpResponse.statusCode else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
let remoteAnimations = try decoder.decode(RemoteAnimationsResponse.self, from: data)
|
||||
return remoteAnimations.animations
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
func updateLocalAnimationsIfNeeded(remoteAnimations: [RemoteAnimation]) async throws {
|
||||
// For now, just validate the API response structure.
|
||||
// In a real implementation, this would:
|
||||
// 1. Compare dates of local vs remote animations
|
||||
// 2. Update local files if newer versions exist
|
||||
// 3. Tag local files with date fields in ISO 8601 format
|
||||
|
||||
for animation in remoteAnimations {
|
||||
print("Remote animation: \(animation.name) - \(animation.version) - \(animation.date)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
GazeTests/Services/AnimationServiceTests.swift
Normal file
17
GazeTests/Services/AnimationServiceTests.swift
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// AnimationServiceTests.swift
|
||||
// GazeTests
|
||||
//
|
||||
// Created by Mike Freno on 1/9/26.
|
||||
//
|
||||
|
||||
@testable import Gaze
|
||||
|
||||
final class AnimationServiceTests {
|
||||
// Test cases can be added here as needed
|
||||
|
||||
func testRemoteAnimationDecoding() {
|
||||
// This will be implemented when we have a testable implementation
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user