Login and Home

This commit is contained in:
Daniel Arantes Loverde
2026-02-04 16:55:29 -03:00
parent 78daaf1927
commit 33fc111459
90 changed files with 1707 additions and 303 deletions

View File

@@ -1,177 +1,16 @@
import SwiftUI
enum ContentTab: String, Hashable {
case welcome, home, settings
}
struct ContentView: View {
@AppStorage("tab") var tab = ContentTab.welcome
@AppStorage("name") var welcomeName = "Skipper"
@AppStorage("appearance") var appearance = ""
@State var viewModel = ViewModel()
@State var root: RootFlow = DefaultTokenStore().jwt == nil ? .auth : .main
@State var selectedTab: MainTab = .home
private let tokenStore: TokenStore = DefaultTokenStore()
var body: some View {
TabView(selection: $tab) {
NavigationStack {
WelcomeView(welcomeName: $welcomeName)
}
.tabItem { Label("Welcome", systemImage: "heart.fill") }
.tag(ContentTab.welcome)
NavigationStack {
ItemListView()
.navigationTitle(Text("\(viewModel.items.count) Items"))
}
.tabItem { Label("Home", systemImage: "house.fill") }
.tag(ContentTab.home)
NavigationStack {
SettingsView(appearance: $appearance, welcomeName: $welcomeName)
.navigationTitle("Settings")
}
.tabItem { Label("Settings", systemImage: "gearshape.fill") }
.tag(ContentTab.settings)
}
.environment(viewModel)
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
}
}
struct WelcomeView : View {
@State var heartBeating = false
@Binding var welcomeName: String
var body: some View {
VStack(spacing: 0) {
Text("Hello [\(welcomeName)](https://skip.tools)!")
.padding()
Image(systemName: "heart.fill")
.foregroundStyle(.red)
.scaleEffect(heartBeating ? 1.5 : 1.0)
.animation(.easeInOut(duration: 1).repeatForever(), value: heartBeating)
.task { heartBeating = true }
}
.font(.largeTitle)
}
}
struct ItemListView : View {
@Environment(ViewModel.self) var viewModel: ViewModel
var body: some View {
List {
ForEach(viewModel.items) { item in
NavigationLink(value: item) {
Label {
Text(item.itemTitle)
} icon: {
if item.favorite {
Image(systemName: "star.fill")
.foregroundStyle(.yellow)
}
}
}
}
.onDelete { offsets in
viewModel.items.remove(atOffsets: offsets)
}
.onMove { fromOffsets, toOffset in
viewModel.items.move(fromOffsets: fromOffsets, toOffset: toOffset)
}
}
.navigationDestination(for: Item.self) { item in
ItemView(item: item)
.navigationTitle(item.itemTitle)
}
.toolbar {
ToolbarItemGroup {
Button {
withAnimation {
viewModel.items.insert(Item(), at: 0)
}
} label: {
Label("Add", systemImage: "plus")
}
}
switch root {
case .auth:
AuthFlowView(root: $root, selectedTab: $selectedTab, tokenStore: tokenStore)
case .main:
MainTabView(selectedTab: $selectedTab, root: $root, tokenStore: tokenStore)
}
}
}
struct ItemView : View {
@State var item: Item
@Environment(ViewModel.self) var viewModel: ViewModel
@Environment(\.dismiss) var dismiss
var body: some View {
Form {
TextField("Title", text: $item.title)
.textFieldStyle(.roundedBorder)
Toggle("Favorite", isOn: $item.favorite)
DatePicker("Date", selection: $item.date)
Text("Notes").font(.title3)
TextEditor(text: $item.notes)
.border(Color.secondary, width: 1.0)
}
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Save") {
viewModel.save(item: item)
dismiss()
}
.disabled(!viewModel.isUpdated(item))
}
}
}
}
struct SettingsView : View {
@Binding var appearance: String
@Binding var welcomeName: String
var body: some View {
Form {
TextField("Name", text: $welcomeName)
Picker("Appearance", selection: $appearance) {
Text("System").tag("")
Text("Light").tag("light")
Text("Dark").tag("dark")
}
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
Text("Version \(version) (\(buildNumber))")
}
HStack {
PlatformHeartView()
Text("Powered by [Skip](https://skip.tools)")
}
}
}
}
/// A view that shows a blue heart on iOS and a green heart on Android.
struct PlatformHeartView : View {
var body: some View {
#if os(Android)
ComposeView {
HeartComposer()
}
#else
Text(verbatim: "💙")
#endif
}
}
#if SKIP
/// Use a ContentComposer to integrate Compose content. This code will be transpiled to Kotlin.
struct HeartComposer : ContentComposer {
@Composable func Compose(context: ComposeContext) {
androidx.compose.material3.Text("💚", modifier: context.modifier)
}
}
#endif