add fixture-backed admin and ios scaffold
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct HermesApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
RootView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import SwiftUI
|
||||
|
||||
struct RootView: View {
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 16) {
|
||||
Text("Hermes")
|
||||
.font(.largeTitle.bold())
|
||||
Text("Native study app scaffold")
|
||||
.font(.body)
|
||||
.foregroundStyle(.secondary)
|
||||
OnboardingView()
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Hermes")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Foundation
|
||||
|
||||
protocol AnalyticsTracking {
|
||||
func track(_ event: String, attributes: [String: String])
|
||||
}
|
||||
|
||||
final class HermesAnalyticsClient: AnalyticsTracking {
|
||||
func track(_ event: String, attributes: [String: String]) {
|
||||
// Scaffold implementation.
|
||||
_ = event
|
||||
_ = attributes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import SwiftUI
|
||||
|
||||
enum HermesTheme {
|
||||
static let background = Color.black
|
||||
static let surface = Color(red: 0.12, green: 0.12, blue: 0.14)
|
||||
static let accent = Color(red: 0.87, green: 0.74, blue: 0.34)
|
||||
static let textPrimary = Color.white
|
||||
static let textSecondary = Color.white.opacity(0.72)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
struct GestureHandlers {
|
||||
func handleTap() {}
|
||||
func handleSwipeDown() {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
final class HapticsController {
|
||||
func selectionAccepted() {}
|
||||
func marketLocked() {}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import Foundation
|
||||
|
||||
final class LocalizationStore {
|
||||
private let bundle: Bundle
|
||||
|
||||
init(bundle: Bundle = .main) {
|
||||
self.bundle = bundle
|
||||
}
|
||||
|
||||
func string(for key: String, locale: String) -> String {
|
||||
guard let path = bundle.path(forResource: locale, ofType: "lproj"),
|
||||
let localizedBundle = Bundle(path: path) else {
|
||||
return bundle.localizedString(forKey: key, value: nil, table: nil)
|
||||
}
|
||||
|
||||
return localizedBundle.localizedString(forKey: key, value: nil, table: nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
final class PlayerCoordinator: ObservableObject {
|
||||
@Published var isPlaying = false
|
||||
@Published var playbackPositionMs: Int = 0
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import Foundation
|
||||
|
||||
struct APIEnvironment {
|
||||
let baseURL: URL
|
||||
}
|
||||
|
||||
struct HermesAPIClient {
|
||||
let environment: APIEnvironment
|
||||
let session: URLSession
|
||||
|
||||
init(environment: APIEnvironment, session: URLSession = .shared) {
|
||||
self.environment = environment
|
||||
self.session = session
|
||||
}
|
||||
|
||||
func get(path: String) async throws -> (Data, HTTPURLResponse) {
|
||||
let url = environment.baseURL.appendingPathComponent(path)
|
||||
let (data, response) = try await session.data(from: url)
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw URLError(.badServerResponse)
|
||||
}
|
||||
return (data, httpResponse)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import SwiftUI
|
||||
|
||||
struct FeedView: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Next round")
|
||||
.font(.headline)
|
||||
Text("A new clip is ready for review.")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding()
|
||||
.background(HermesTheme.surface)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import SwiftUI
|
||||
|
||||
struct OnboardingView: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Study intro")
|
||||
.font(.headline)
|
||||
Text("Watch the clip, make your choice before lock, then see the reveal.")
|
||||
.foregroundStyle(.secondary)
|
||||
Button("Start session") {}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding()
|
||||
.background(HermesTheme.surface)
|
||||
.foregroundStyle(HermesTheme.textPrimary)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ResultView: View {
|
||||
var body: some View {
|
||||
Text("Result scaffold")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct RevealView: View {
|
||||
var body: some View {
|
||||
Text("Reveal scaffold")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import SwiftUI
|
||||
|
||||
struct RoundView: View {
|
||||
var body: some View {
|
||||
Text("Round scaffold")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SelectionView: View {
|
||||
var body: some View {
|
||||
Text("Selection scaffold")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SessionView: View {
|
||||
var body: some View {
|
||||
Text("Session scaffold")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
var body: some View {
|
||||
Text("Settings scaffold")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
"app.name" = "Hermes";
|
||||
"onboarding.title" = "Study intro";
|
||||
"onboarding.start_session" = "Start session";
|
||||
"feed.next_round_title" = "Next round";
|
||||
@@ -0,0 +1,4 @@
|
||||
"app.name" = "Hermes";
|
||||
"onboarding.title" = "Studieintro";
|
||||
"onboarding.start_session" = "Starta session";
|
||||
"feed.next_round_title" = "Nästa runda";
|
||||
Reference in New Issue
Block a user