2023-12-29 11:17:37 +01:00
// M a d e b y L u m a a
import Foundation
import SwiftUI
@ Observable
2024-02-16 17:26:32 +01:00
public class Navigator : ObservableObject {
2023-12-29 11:17:37 +01:00
public var path : [ RouterDestination ] = [ ]
public var presentedSheet : SheetDestination ?
2024-02-04 08:43:56 +01:00
public var presentedCover : SheetDestination ?
2023-12-29 11:17:37 +01:00
public var selectedTab : TabDestination = . timeline
public func navigate ( to : RouterDestination ) {
path . append ( to )
}
2024-01-28 17:51:50 +01:00
public func removeSettingsOfPath ( ) {
self . path = self . path . filter ( { ! RouterDestination . allSettings . contains ( $0 ) } )
}
2023-12-29 11:17:37 +01:00
}
2024-02-17 02:06:50 +01:00
public class UniversalNavigator : Navigator {
public var client : Client ?
public func handle ( url : URL ) -> OpenURLAction . Result {
guard let client = self . client else { return . systemAction }
let path : String = url . absoluteString . replacingOccurrences ( of : AppInfo . scheme , with : " " ) // r e m o v e a l l p a t h
let urlPath : URL = URL ( string : path ) !
if client . isAuth && client . hasConnection ( with : url ) {
if urlPath . lastPathComponent . starts ( with : " @ " ) {
Task {
do {
let search : SearchResults = try await client . get ( endpoint : Search . search ( query : urlPath . lastPathComponent , type : " accounts " , offset : nil , following : nil ) , forceVersion : . v2 )
let acc : Account = search . accounts . first ? ? . placeholder ( )
self . navigate ( to : . account ( acc : acc ) )
} catch {
print ( error )
}
}
}
}
return . handled
}
}
2024-02-04 08:43:56 +01:00
2023-12-29 11:17:37 +01:00
public enum TabDestination : Identifiable {
case timeline
case search
case activity
case profile
public var id : String {
switch self {
case . timeline :
return " timeline "
case . search :
return " search "
case . activity :
return " activity "
case . profile :
return " profile "
}
}
}
public enum SheetDestination : Identifiable {
case welcome
2024-01-27 08:56:22 +01:00
case shop
2024-02-18 18:39:07 +01:00
case media ( attachments : [ MediaAttachment ] , selected : MediaAttachment )
2024-01-27 08:56:22 +01:00
2023-12-29 11:17:37 +01:00
case mastodonLogin ( logged : Binding < Bool > )
2024-01-26 14:10:17 +01:00
case post ( content : String = " " , replyId : String ? = nil , editId : String ? = nil )
2024-01-21 13:08:06 +01:00
case safari ( url : URL )
2024-01-26 14:10:17 +01:00
case shareImage ( image : UIImage , status : Status )
2023-12-29 11:17:37 +01:00
public var id : String {
switch self {
case . welcome :
return " welcome "
2024-01-27 08:56:22 +01:00
case . shop :
return " shop "
2024-02-18 18:39:07 +01:00
case . media :
return " media "
2024-01-27 08:56:22 +01:00
2023-12-29 11:17:37 +01:00
case . mastodonLogin :
return " login "
case . post :
return " post "
2024-01-21 13:08:06 +01:00
case . safari :
return " safari "
2024-01-21 16:34:26 +01:00
case . shareImage :
return " shareImage "
2023-12-29 11:17:37 +01:00
}
}
public var isCover : Bool {
switch self {
case . welcome :
return true
2024-01-27 08:56:22 +01:00
case . shop :
return true
2024-02-18 18:39:07 +01:00
case . media :
return true
2023-12-29 11:17:37 +01:00
case . mastodonLogin :
return false
case . post :
return false
2024-01-21 13:08:06 +01:00
case . safari :
return false
2024-01-21 16:34:26 +01:00
case . shareImage :
return false
2023-12-29 11:17:37 +01:00
}
}
}
public enum RouterDestination : Hashable {
case settings
2024-02-12 02:42:56 +01:00
case support
2024-01-04 22:19:35 +01:00
case appearence
2023-12-29 11:17:37 +01:00
case account ( acc : Account )
2024-01-10 17:45:41 +01:00
case post ( status : Status )
2023-12-30 20:59:09 +01:00
case about
2024-02-10 02:22:39 +01:00
case contacts
2024-02-11 14:22:27 +01:00
case timeline ( timeline : TimelineFilter ? )
2023-12-29 11:17:37 +01:00
}
2024-01-28 17:51:50 +01:00
extension RouterDestination {
2024-02-12 02:42:56 +01:00
static let allSettings : [ RouterDestination ] = [ . settings , . support , . about , . appearence ]
2024-01-28 17:51:50 +01:00
}
2023-12-29 11:17:37 +01:00
extension View {
2024-01-02 14:23:36 +01:00
func withAppRouter ( _ navigator : Navigator ) -> some View {
2023-12-29 11:17:37 +01:00
navigationDestination ( for : RouterDestination . self ) { destination in
switch destination {
case . settings :
2024-01-02 14:23:36 +01:00
SettingsView ( navigator : navigator )
2024-02-12 02:42:56 +01:00
case . support :
SupportView ( )
2024-01-04 22:19:35 +01:00
case . appearence :
AppearenceView ( )
2023-12-29 11:17:37 +01:00
case . account ( let acc ) :
2024-02-04 10:44:51 +01:00
ProfileView ( account : acc )
2024-01-10 17:45:41 +01:00
case . post ( let status ) :
PostDetailsView ( status : status )
2023-12-30 20:59:09 +01:00
case . about :
AboutView ( )
2024-02-10 02:22:39 +01:00
case . contacts :
ContactsView ( )
2024-02-11 14:22:27 +01:00
case . timeline ( let timeline ) :
PostsView ( filter : timeline ? ? . home , showHero : false )
2023-12-29 11:17:37 +01:00
}
}
}
func withSheets ( sheetDestination : Binding < SheetDestination ? > ) -> some View {
sheet ( item : sheetDestination ) { destination in
2024-02-04 08:43:56 +01:00
viewSheet ( destination : destination )
2023-12-29 11:17:37 +01:00
}
}
func withCovers ( sheetDestination : Binding < SheetDestination ? > ) -> some View {
fullScreenCover ( item : sheetDestination ) { destination in
2024-02-04 08:43:56 +01:00
viewCover ( destination : destination )
2023-12-29 11:17:37 +01:00
}
}
2024-02-04 08:43:56 +01:00
private func viewCover ( destination : SheetDestination ) -> some View {
Group {
switch destination {
case . welcome :
ConnectView ( )
case . shop :
ShopView ( )
2024-02-18 18:39:07 +01:00
case . media ( let attachments , let selected ) :
AttachmentView ( attachments : attachments , selectedId : selected . id )
2024-02-04 08:43:56 +01:00
default :
EmptySheetView ( destId : destination . id )
}
}
}
private func viewSheet ( destination : SheetDestination ) -> some View {
Group {
switch destination {
case . post ( let content , let replyId , let editId ) :
NavigationStack {
PostingView ( initialString : content , replyId : replyId , editId : editId )
. tint ( Color ( uiColor : UIColor . label ) )
}
case let . mastodonLogin ( logged ) :
AddInstanceView ( logged : logged )
. tint ( Color . accentColor )
case let . safari ( url ) :
SfSafariView ( url : url )
. ignoresSafeArea ( )
case let . shareImage ( image , status ) :
ShareSheet ( image : image , status : status )
default :
EmptySheetView ( destId : destination . id )
}
}
}
2023-12-29 11:17:37 +01:00
}
2024-01-27 08:56:22 +01:00
2024-02-04 08:43:56 +01:00
// / T h i s v i e w i s v i s i b l e w h e n t h e ` v i e w R e p r e s e n t a t i o n ( d e s t i n a t i o n : S h e e t D e s t i n a t i o n ) ` d o e s n ' t s u p p o r t t h e g i v e n ` S h e e t D e s t i n a t i o n `
2024-01-27 08:56:22 +01:00
private struct EmptySheetView : View {
2024-02-04 08:43:56 +01:00
@ Environment ( \ . dismiss ) private var dismiss
var destId : String = " "
2024-01-28 17:51:50 +01:00
let str : String = . init ( localized : " about.version- \( AppInfo . appVersion ) " )
2024-01-27 08:56:22 +01:00
var body : some View {
ZStack {
2024-02-04 08:43:56 +01:00
Rectangle ( )
. fill ( Color . red . gradient )
2024-01-27 08:56:22 +01:00
. ignoresSafeArea ( )
2024-02-04 08:43:56 +01:00
VStack {
ContentUnavailableView ( String ( " Missing view for \" \( destId . isEmpty ? " [EMPTY_DEST_ID] " : destId ) \" " ) , systemImage : " exclamationmark.triangle.fill " , description : Text ( String ( " Please notify Lumaa as soon as possible! \n \n \( str ) " ) ) )
. foregroundStyle ( . white )
Button {
dismiss ( )
} label : {
Text ( String ( " Dismiss " ) )
}
. buttonStyle ( LargeButton ( filled : true ) )
}
2024-01-27 08:56:22 +01:00
}
}
}