mv-experiment #1

Merged
fmodf merged 88 commits from mv-experiment into develop 2024-09-03 15:13:59 +00:00
13 changed files with 202 additions and 158 deletions
Showing only changes of commit 2e053afea9 - Show all commits

View file

@ -1,30 +0,0 @@
import Combine
import Foundation
@MainActor
final class NavigationStore: ObservableObject {
enum Flow: Equatable {
enum Entering: Equatable {
case welcome
case login
case registration
}
enum Main: Equatable {
enum Contacts: Equatable {
case list
case add
}
case contacts(Contacts)
case conversations
case settings
}
case start
case entering(Entering)
case main(Main)
}
@Published var flow: Flow = .start
}

View file

@ -5,7 +5,6 @@ import SwiftUI
@MainActor
struct ConversationsClassic: App {
private var clientsStore = ClientsStore()
private var navigationStore = NavigationStore()
init() {
// There's a bug on iOS 17 where sheet may not load with large title, even if modifiers are set, which causes some tests to fail
@ -15,7 +14,7 @@ struct ConversationsClassic: App {
var body: some Scene {
WindowGroup {
AppRootView()
RootView()
.environmentObject(clientsStore)
}
}

View file

@ -19,6 +19,10 @@
"Login.btn" = "Continue";
"Login.error" = "Check internet connection, and make sure that JID and password are correct";
// MARK: Tabs
"Tabs.Name.contacts" = "Contacts";
"Tabs.Name.conversations" = "Chats";
"Tabs.Name.settings" = "Settings";

View file

@ -1,47 +0,0 @@
import SwiftUI
struct AppRootView: View {
@EnvironmentObject var navigation: NavigationStore
var body: some View {
Group {
switch navigation.flow {
case .start:
StartScreen()
case .entering(let kind):
switch kind {
case .welcome:
WelcomeScreen()
case .login:
LoginScreen()
case .registration:
RegistrationScreen()
}
case .main(let main):
switch main {
case .contacts(let kind):
switch kind {
case .list:
ContactsScreen()
case .add:
ContactsScreen()
.fullScreenCover(isPresented: .constant(true)) {
AddContactOrChannelScreen()
}
}
case .conversations:
EmptyView()
case .settings:
EmptyView()
}
}
}
}
}

View file

@ -3,7 +3,7 @@ import Martin
import SwiftUI
struct LoginScreen: View {
@EnvironmentObject var navigation: NavigationStore
@Environment(\.router) var router
@EnvironmentObject var clientsStore: ClientsStore
enum Field {
@ -88,9 +88,7 @@ struct LoginScreen: View {
.disabled(!loginInputValid)
Button {
withAnimation {
navigation.flow = .entering(.welcome)
}
router.dismissScreen()
} label: {
Text("\(Image(systemName: "chevron.left")) \(L10n.Global.back)")
.foregroundColor(.Material.Elements.active)
@ -129,9 +127,6 @@ struct LoginScreen: View {
clientsStore.addNewClient(client)
isLoading = false
isError = false
if navigation.flow == .entering(.login) {
navigation.flow = .main(.contacts(.list))
}
case .failure:
isLoading = false

View file

@ -1,15 +1,13 @@
import SwiftUI
struct RegistrationScreen: View {
@EnvironmentObject var navigation: NavigationStore
@Environment(\.router) var router
public var body: some View {
ZStack {
Color.Material.Background.light
Button {
withAnimation {
navigation.flow = .entering(.welcome)
}
router.dismissScreen()
} label: {
VStack {
Text("Not yet implemented")

View file

@ -1,9 +1,9 @@
import SwiftUI
struct WelcomeScreen: View {
@EnvironmentObject var navigation: NavigationStore
@Environment(\.router) var router
public var body: some View {
var body: some View {
ZStack {
// background
Color.Material.Background.light
@ -33,16 +33,18 @@ struct WelcomeScreen: View {
// buttons
VStack(spacing: 16) {
Button {
withAnimation {
navigation.flow = .entering(.login)
router.showScreen(.push) { _ in
LoginScreen()
.navigationBarBackButtonHidden(true)
}
} label: {
Text(L10n.Start.Btn.login)
}
.buttonStyle(SecondaryButtonStyle())
Button {
withAnimation {
navigation.flow = .entering(.registration)
router.showScreen(.push) { _ in
RegistrationScreen()
.navigationBarBackButtonHidden(true)
}
} label: {
Text(L10n.Start.Btn.register)

View file

@ -1,7 +1,6 @@
import SwiftUI
struct AddContactOrChannelScreen: View {
@EnvironmentObject var navigation: NavigationStore
// @EnvironmentObject var store: AppStore
// enum Field {
@ -32,9 +31,9 @@ struct AddContactOrChannelScreen: View {
leftButton: .init(
image: Image(systemName: "xmark"),
action: {
withAnimation {
navigation.flow = .main(.contacts(.list))
}
// withAnimation {
// navigation.flow = .main(.contacts(.list))
// }
// isPresented = false
}
),

View file

@ -1,7 +1,6 @@
import SwiftUI
struct ContactsScreen: View {
@EnvironmentObject var navigation: NavigationStore
@EnvironmentObject var clientsStore: ClientsStore
@StateObject var rostersStore = RostersStore(clientsPublisher: ClientsStore.shared.$clients)
// @State private var addPanelPresented = false
@ -23,9 +22,9 @@ struct ContactsScreen: View {
rightButton: .init(
image: Image(systemName: "plus"),
action: {
withAnimation {
navigation.flow = .main(.contacts(.add))
}
// withAnimation {
// navigation.flow = .main(.contacts(.add))
// }
// addPanelPresented = true
}
)
@ -48,9 +47,6 @@ struct ContactsScreen: View {
} else {
Spacer()
}
// Tab bar
SharedTabBar()
}
}
// .task {

View file

@ -0,0 +1,113 @@
import Foundation
import SwiftfulRouting
import SwiftUI
private enum Tab {
case conversations
case contacts
case settings
}
struct MainTabScreen: View {
@State private var selectedTab: Tab = .conversations
var body: some View {
ZStack {
// Background color
Color.Material.Background.light
.ignoresSafeArea()
// Content
VStack(spacing: 0) {
switch selectedTab {
case .conversations:
Color.red
// ConversationsScreen()
case .contacts:
RouterView { _ in
ContactsScreen()
}
case .settings:
Color.green
// SettingsScreen()
}
// Tab bar
TabBar(selectedTab: $selectedTab)
}
}
}
}
private struct TabBar: View {
@Binding var selectedTab: Tab
var body: some View {
VStack(spacing: 0) {
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 0.2)
.foregroundColor(.Material.Shape.separator)
HStack(spacing: 0) {
TabBarButton(buttonType: .contacts, selectedTab: $selectedTab)
TabBarButton(buttonType: .conversations, selectedTab: $selectedTab)
TabBarButton(buttonType: .settings, selectedTab: $selectedTab)
}
.background(Color.Material.Background.dark)
}
.frame(height: 50)
}
}
private struct TabBarButton: View {
let buttonType: Tab
@Binding var selectedTab: Tab
var body: some View {
ZStack {
VStack(spacing: 2) {
buttonImg
.foregroundColor(buttonType == selectedTab ? .Material.Elements.active : .Material.Elements.inactive)
.font(.system(size: 24, weight: .light))
.symbolRenderingMode(.hierarchical)
Text(buttonTitle)
.font(.sub1)
.foregroundColor(buttonType == selectedTab ? .Material.Text.main : .Material.Elements.inactive)
}
Rectangle()
.foregroundColor(.white.opacity(0.01))
.onTapGesture {
selectedTab = buttonType
}
}
}
var buttonImg: Image {
switch buttonType {
case .contacts:
return Image(systemName: "person.2.fill")
case .conversations:
return Image(systemName: "bubble.left.fill")
case .settings:
return Image(systemName: "gearshape.fill")
}
}
var buttonTitle: String {
switch buttonType {
case .contacts:
return L10n.Tabs.Name.contacts
case .conversations:
return L10n.Tabs.Name.conversations
case .settings:
return L10n.Tabs.Name.settings
}
}
}

View file

@ -0,0 +1,27 @@
import SwiftfulRouting
import SwiftUI
struct RootView: View {
@EnvironmentObject var clientsStore: ClientsStore
var body: some View {
Group {
if clientsStore.ready {
if clientsStore.clients.isEmpty {
RouterView { _ in
WelcomeScreen()
}
} else {
RouterView { _ in
MainTabScreen()
}
}
} else {
StartScreen()
}
}
.task {
clientsStore.startFetching()
}
}
}

View file

@ -7,72 +7,72 @@ struct SharedTabBar: View {
.frame(maxWidth: .infinity)
.frame(height: 0.2)
.foregroundColor(.Material.Shape.separator)
HStack(spacing: 0) {
SharedTabBarButton(buttonFlow: .main(.contacts(.list)))
SharedTabBarButton(buttonFlow: .main(.conversations))
SharedTabBarButton(buttonFlow: .main(.settings))
}
.background(Color.Material.Background.dark)
// HStack(spacing: 0) {
// SharedTabBarButton(buttonFlow: .main(.contacts(.list)))
// SharedTabBarButton(buttonFlow: .main(.conversations))
// SharedTabBarButton(buttonFlow: .main(.settings))
// }
// .background(Color.Material.Background.dark)
}
.frame(height: 50)
}
}
private struct SharedTabBarButton: View {
@EnvironmentObject var navigation: NavigationStore
let buttonFlow: NavigationStore.Flow
// let buttonFlow: NavigationStore.Flow
var body: some View {
ZStack {
VStack(spacing: 2) {
buttonImg
.foregroundColor(buttonFlow == navigation.flow ? .Material.Elements.active : .Material.Elements.inactive)
// .foregroundColor(buttonFlow == navigation.flow ? .Material.Elements.active : .Material.Elements.inactive)
.font(.system(size: 24, weight: .light))
.symbolRenderingMode(.hierarchical)
Text(buttonTitle)
.font(.sub1)
.foregroundColor(buttonFlow == navigation.flow ? .Material.Text.main : .Material.Elements.inactive)
// .foregroundColor(buttonFlow == navigation.flow ? .Material.Text.main : .Material.Elements.inactive)
}
Rectangle()
.foregroundColor(.white.opacity(0.01))
.onTapGesture {
withAnimation {
navigation.flow = buttonFlow
}
// withAnimation {
// navigation.flow = buttonFlow
// }
}
}
}
var buttonImg: Image {
switch buttonFlow {
case .main(.contacts):
return Image(systemName: "person.2.fill")
// switch buttonFlow {
// case .main(.contacts):
// return Image(systemName: "person.2.fill")
//
// case .main(.conversations):
// return Image(systemName: "bubble.left.fill")
//
// case .main(.settings):
// return Image(systemName: "gearshape.fill")
case .main(.conversations):
return Image(systemName: "bubble.left.fill")
case .main(.settings):
return Image(systemName: "gearshape.fill")
default:
return Image(systemName: "questionmark.circle")
}
// default:
// return Image(systemName: "questionmark.circle")
// }
Image(systemName: "questionmark.circle")
}
var buttonTitle: String {
switch buttonFlow {
case .main(.contacts(.list)):
return "Contacts"
case .main(.conversations):
return "Chats"
case .main(.settings):
return "Settings"
default:
return "Unknown"
}
""
// switch buttonFlow {
// case .main(.contacts(.list)):
// return "Contacts"
//
// case .main(.conversations):
// return "Chats"
//
// case .main(.settings):
// return "Settings"
//
// default:
// return "Unknown"
// }
}
}

View file

@ -2,7 +2,6 @@ import SwiftUI
struct StartScreen: View {
@EnvironmentObject var clientsStore: ClientsStore
@EnvironmentObject var navigation: NavigationStore
var body: some View {
ZStack {
@ -13,16 +12,5 @@ struct StartScreen: View {
.frame(width: 200, height: 200)
}
.ignoresSafeArea()
.onAppear {
clientsStore.startFetching()
}
.onChange(of: clientsStore.ready) { ready in
if ready {
let flow: NavigationStore.Flow = clientsStore.clients.isEmpty ? .entering(.welcome) : .main(.conversations)
withAnimation {
navigation.flow = flow
}
}
}
}
}