Look and feel.

This commit is contained in:
Stuart Breckenridge 2020-12-07 19:22:35 +08:00
parent 841da2d570
commit c8b5caba60
No known key found for this signature in database
GPG Key ID: 1F11FD62007DC331
9 changed files with 342 additions and 92 deletions

View File

@ -25,6 +25,8 @@ class AddFeedlyViewModel: ObservableObject, OAuthAccountAuthorizationOperationDe
addAccount.delegate = self addAccount.delegate = self
#if os(macOS) #if os(macOS)
addAccount.presentationAnchor = NSApplication.shared.windows.last addAccount.presentationAnchor = NSApplication.shared.windows.last
#else
addAccount.presentationAnchor = UIApplication.shared.windows.last
#endif #endif
MainThreadOperationQueue.shared.add(addAccount) MainThreadOperationQueue.shared.add(addAccount)
} }

View File

@ -18,7 +18,9 @@ struct AddCloudKitAccountView: View {
#if os(macOS) #if os(macOS)
macBody macBody
#else #else
iosBody NavigationView {
iosBody
}
#endif #endif
} }
@ -26,29 +28,28 @@ struct AddCloudKitAccountView: View {
#if os(iOS) #if os(iOS)
var iosBody: some View { var iosBody: some View {
List { List {
Section(header: formHeader, content: { Section(header: formHeader, footer: formFooter, content: {
Button(action: { Button(action: {
_ = AccountManager.shared.createAccount(type: .cloudKit) _ = AccountManager.shared.createAccount(type: .cloudKit)
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}, label: { }, label: {
Text("Add") HStack {
}) Spacer()
Text("Add Account")
Spacer()
}
}).disabled(AccountManager.shared.activeAccounts.filter({ $0.type == .cloudKit }).count > 0)
}) })
}.navigationBarItems(leading: }.navigationBarItems(leading:
Button(action: { Button(action: {
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}, label: { }, label: {
Text("Dismiss") Text("Cancel")
})
, trailing:
Button(action: {
_ = AccountManager.shared.createAccount(type: .cloudKit)
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Add")
}) })
) )
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text(AccountType.cloudKit.localizedAccountName()))
.listStyle(InsetGroupedListStyle())
} }
#endif #endif
@ -102,20 +103,27 @@ struct AddCloudKitAccountView: View {
var formHeader: some View { var formHeader: some View {
HStack { HStack {
Spacer()
VStack(alignment: .center) { VStack(alignment: .center) {
AccountType.cloudKit.image() AccountType.cloudKit.image()
.resizable() .resizable()
.frame(width: 50, height: 50) .frame(width: 50, height: 50)
Text("Sign in to your iCloud account.")
.font(.headline)
Text("This account syncs across your Mac and iOS devices using your iCloud account.")
.foregroundColor(.secondary)
.font(.callout)
.lineLimit(2)
.padding(.top, 4)
} }
} Spacer()
}.padding(.vertical)
}
var formFooter: some View {
HStack {
Spacer()
VStack(spacing: 8) {
Text("This account syncs across your Mac and iOS devices using your iCloud account.").foregroundColor(.secondary)
}
.multilineTextAlignment(.center)
.font(.caption)
Spacer()
}.padding(.vertical)
} }
} }

View File

@ -22,7 +22,9 @@ struct AddFeedWranglerAccountView: View {
#if os(macOS) #if os(macOS)
macBody macBody
#else #else
iosBody NavigationView {
iosBody
}
#endif #endif
} }
@ -30,10 +32,8 @@ struct AddFeedWranglerAccountView: View {
#if os(iOS) #if os(iOS)
var iosBody: some View { var iosBody: some View {
List { List {
Section(header: formHeader, footer: ProgressView() Section(header: formHeader, content: {
.scaleEffect(CGSize(width: 0.5, height: 0.5)) TextField("Email", text: $model.username)
.hidden(!model.isAuthenticating) , content: {
TextField("me@email.com", text: $model.username)
if model.showPassword == false { if model.showPassword == false {
ZStack { ZStack {
HStack { HStack {
@ -60,20 +60,39 @@ struct AddFeedWranglerAccountView: View {
} }
} }
} }
}) })
}.navigationBarItems(leading:
Section(footer: formFooter, content: {
Button(action: {
model.authenticateFeedWrangler()
}, label: {
HStack {
Spacer()
Text("Sign In")
Spacer()
}
}).disabled(model.username.isEmpty || model.password.isEmpty)
})
}
.navigationBarItems(leading:
Button(action: { Button(action: {
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}, label: { }, label: {
Text("Dismiss") Text("Cancel")
}) }))
, trailing: .listStyle(InsetGroupedListStyle())
Button(action: { .navigationBarTitleDisplayMode(.inline)
model.authenticateFeedWrangler() .navigationTitle(Text("Feed Wrangler"))
}, label: { .alert(isPresented: $model.showError, content: {
Text("Add") Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel(Text("Dismiss")))
}).disabled(model.username.isEmpty || model.password.isEmpty) })
) .onReceive(model.$canDismiss, perform: { value in
if value == true {
presentationMode.wrappedValue.dismiss()
}
})
} }
#endif #endif
@ -158,20 +177,34 @@ struct AddFeedWranglerAccountView: View {
var formHeader: some View { var formHeader: some View {
HStack { HStack {
Spacer()
VStack(alignment: .center) { VStack(alignment: .center) {
AccountType.newsBlur.image() AccountType.feedWrangler.image()
.resizable() .resizable()
.frame(width: 50, height: 50) .frame(width: 50, height: 50)
Text("Sign in to your Feed Wrangler account.")
.font(.headline)
Text("This account syncs across your subscriptions across devices.")
.foregroundColor(.secondary)
.font(.callout)
.lineLimit(2)
.padding(.top, 4)
} }
} Spacer()
}.padding(.vertical)
}
var formFooter: some View {
HStack {
Spacer()
VStack(spacing: 8) {
Text("Sign in to your Feed Wrangler account and sync your subscriptions across your devices. Your username and password and password will be encrypted and stored in Keychain.").foregroundColor(.secondary)
Text("Don't have a Feed Wrangler account?").foregroundColor(.secondary)
Button(action: {
model.presentSignUpOption(.feedWrangler)
}, label: {
Text("Sign Up Here").foregroundColor(.blue).multilineTextAlignment(.center)
})
ProgressView().hidden(!model.isAuthenticating)
}
.multilineTextAlignment(.center)
.font(.caption2)
Spacer()
}.padding(.vertical)
} }
} }

View File

@ -74,7 +74,7 @@ struct AddFeedbinAccountView: View {
Button(action: { Button(action: {
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}, label: { }, label: {
Text("Dismiss") Text("Cancel")
})) }))
.listStyle(InsetGroupedListStyle()) .listStyle(InsetGroupedListStyle())
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)

View File

@ -32,7 +32,29 @@ struct AddFeedlyAccountView: View {
#if os(iOS) #if os(iOS)
var iosBody: some View { var iosBody: some View {
Text("TBC") List {
Section(header: formHeader, footer: formFooter, content: {
Button(action: {
model.authenticateFeedly()
}, label: {
HStack {
Spacer()
Text("Add Account")
Spacer()
}
})
})
}.navigationBarItems(leading:
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
})
)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text(AccountType.feedly.localizedAccountName()))
.listStyle(InsetGroupedListStyle())
} }
#endif #endif
@ -90,6 +112,35 @@ struct AddFeedlyAccountView: View {
} }
#endif #endif
var formHeader: some View {
HStack {
Spacer()
VStack(alignment: .center) {
AccountType.feedly.image()
.resizable()
.frame(width: 50, height: 50)
}
Spacer()
}.padding(.vertical)
}
var formFooter: some View {
HStack {
Spacer()
VStack(spacing: 8) {
Text("Sign in to your Feedly account and sync your subscriptions across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon't have an Feedly account?").foregroundColor(.secondary)
Button(action: {
model.presentSignUpOption(.feedly)
}, label: {
Text("Sign Up Here").foregroundColor(.blue).multilineTextAlignment(.center)
})
}
.multilineTextAlignment(.center)
.font(.caption)
Spacer()
}.padding(.vertical)
}
} }

View File

@ -19,7 +19,9 @@ struct AddLocalAccountView: View {
#if os(macOS) #if os(macOS)
macBody macBody
#else #else
iosBody NavigationView {
iosBody
}
#endif #endif
} }
@ -29,22 +31,30 @@ struct AddLocalAccountView: View {
Section(header: formHeader, content: { Section(header: formHeader, content: {
TextField("Account Name", text: $newAccountName) TextField("Account Name", text: $newAccountName)
}) })
Section(footer: formFooter, content: {
Button(action: {
let newAccount = AccountManager.shared.createAccount(type: .onMyMac)
newAccount.name = newAccountName
presentationMode.wrappedValue.dismiss()
}, label: {
HStack {
Spacer()
Text("Add Account")
Spacer()
}
})
})
}.navigationBarItems(leading: }.navigationBarItems(leading:
Button(action: { Button(action: {
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}, label: { }, label: {
Text("Dismiss") Text("Cancel")
})
, trailing:
Button(action: {
let newAccount = AccountManager.shared.createAccount(type: .onMyMac)
newAccount.name = newAccountName
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Add")
}) })
) )
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text(AccountType.onMyMac.localizedAccountName()))
.listStyle(InsetGroupedListStyle())
} }
#endif #endif
@ -98,17 +108,27 @@ struct AddLocalAccountView: View {
var formHeader: some View { var formHeader: some View {
HStack { HStack {
Spacer()
VStack(alignment: .center) { VStack(alignment: .center) {
AccountType.onMyMac.image() AccountType.onMyMac.image()
.resizable() .resizable()
.frame(width: 50, height: 50) .frame(width: 50, height: 50)
Text("Create a local account on your Mac.")
.font(.headline)
Text("Local accounts store their data on your Mac. They do not sync across your devices.")
.font(.callout)
.foregroundColor(.secondary)
} }
} Spacer()
}.padding(.vertical)
}
var formFooter: some View {
HStack {
Spacer()
VStack(spacing: 8) {
Text("Local accounts do not sync your subscriptions across devices.").foregroundColor(.secondary)
}
.multilineTextAlignment(.center)
.font(.caption)
Spacer()
}.padding(.vertical)
} }
} }

View File

@ -21,17 +21,17 @@ struct AddNewsBlurAccountView: View {
#if os(macOS) #if os(macOS)
macBody macBody
#else #else
iosBody NavigationView {
iosBody
}
#endif #endif
} }
#if os(iOS) #if os(iOS)
var iosBody: some View { var iosBody: some View {
List { List {
Section(header: formHeader, footer: ProgressView() Section(header: formHeader, content: {
.scaleEffect(CGSize(width: 0.5, height: 0.5)) TextField("Email", text: $model.username)
.hidden(!model.isAuthenticating) , content: {
TextField("me@email.com", text: $model.username)
if model.showPassword == false { if model.showPassword == false {
ZStack { ZStack {
HStack { HStack {
@ -58,20 +58,39 @@ struct AddNewsBlurAccountView: View {
} }
} }
} }
}) })
}.navigationBarItems(leading:
Section(footer: formFooter, content: {
Button(action: {
model.authenticateNewsBlur()
}, label: {
HStack {
Spacer()
Text("Sign In")
Spacer()
}
}).disabled(model.username.isEmpty || model.password.isEmpty)
})
}
.navigationBarItems(leading:
Button(action: { Button(action: {
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}, label: { }, label: {
Text("Dismiss") Text("Cancel")
}) }))
, trailing: .listStyle(InsetGroupedListStyle())
Button(action: { .navigationBarTitleDisplayMode(.inline)
model.authenticateNewsBlur() .navigationTitle(Text("NewsBlur"))
}, label: { .alert(isPresented: $model.showError, content: {
Text("Add") Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel(Text("Dismiss")))
}).disabled(model.username.isEmpty || model.password.isEmpty) })
) .onReceive(model.$canDismiss, perform: { value in
if value == true {
presentationMode.wrappedValue.dismiss()
}
})
} }
#endif #endif
@ -155,20 +174,34 @@ struct AddNewsBlurAccountView: View {
var formHeader: some View { var formHeader: some View {
HStack { HStack {
Spacer()
VStack(alignment: .center) { VStack(alignment: .center) {
AccountType.newsBlur.image() AccountType.newsBlur.image()
.resizable() .resizable()
.frame(width: 50, height: 50) .frame(width: 50, height: 50)
Text("Sign in to your NewsBlur account.")
.font(.headline)
Text("This account syncs across your subscriptions across devices.")
.foregroundColor(.secondary)
.font(.callout)
.lineLimit(2)
.padding(.top, 4)
} }
} Spacer()
}.padding(.vertical)
}
var formFooter: some View {
HStack {
Spacer()
VStack(spacing: 8) {
Text("Sign in to your NewsBlur account and sync your subscriptions across your devices. Your username and password and password will be encrypted and stored in Keychain.").foregroundColor(.secondary)
Text("Don't have a NewsBlur account?").foregroundColor(.secondary)
Button(action: {
model.presentSignUpOption(.newsBlur)
}, label: {
Text("Sign Up Here").foregroundColor(.blue).multilineTextAlignment(.center)
})
ProgressView().hidden(!model.isAuthenticating)
}
.multilineTextAlignment(.center)
.font(.caption2)
Spacer()
}.padding(.vertical)
} }
} }

View File

@ -22,13 +22,79 @@ struct AddReaderAPIAccountView: View {
#if os(macOS) #if os(macOS)
macBody macBody
#else #else
iosBody NavigationView {
iosBody
}
#endif #endif
} }
#if os(iOS) #if os(iOS)
var iosBody: some View { var iosBody: some View {
Text("TBC") List {
Section(header: formHeader, content: {
TextField("Email", text: $model.username)
if model.showPassword == false {
ZStack {
HStack {
SecureField("Password", text: $model.password)
Spacer()
Image(systemName: "eye.fill")
.foregroundColor(.accentColor)
.onTapGesture {
model.showPassword = true
}
}
}
}
else {
ZStack {
HStack {
TextField("Password", text: $model.password)
Spacer()
Image(systemName: "eye.slash.fill")
.foregroundColor(.accentColor)
.onTapGesture {
model.showPassword = false
}
}
}
}
if accountType == .freshRSS {
TextField("API URL", text: $model.apiUrl)
}
})
Section(footer: formFooter, content: {
Button(action: {
model.authenticateReaderAccount(accountType)
}, label: {
HStack {
Spacer()
Text("Sign In")
Spacer()
}
}).disabled(createDisabled())
})
}
.navigationBarItems(leading:
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Cancel")
}))
.listStyle(InsetGroupedListStyle())
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text(accountType.localizedAccountName()))
.alert(isPresented: $model.showError, content: {
Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel(Text("Dismiss")))
})
.onReceive(model.$canDismiss, perform: { value in
if value == true {
presentationMode.wrappedValue.dismiss()
}
})
} }
#endif #endif
@ -138,10 +204,44 @@ struct AddReaderAPIAccountView: View {
} }
return 230 return 230
} }
var formHeader: some View {
HStack {
Spacer()
VStack(alignment: .center) {
accountType.image()
.resizable()
.frame(width: 50, height: 50)
}
Spacer()
}.padding(.vertical)
}
var formFooter: some View {
HStack {
Spacer()
VStack(spacing: 8) {
Text("Sign in to your \(accountType.localizedAccountName()) account and sync your subscriptions across your devices. Your username and password and password will be encrypted and stored in Keychain.").foregroundColor(.secondary)
Text("Don't have a \(accountType.localizedAccountName()) instance?").foregroundColor(.secondary)
Button(action: {
model.presentSignUpOption(accountType)
}, label: {
Text("Sign Up Here").foregroundColor(.blue).multilineTextAlignment(.center)
})
ProgressView().hidden(!model.isAuthenticating)
}
.multilineTextAlignment(.center)
.font(.caption2)
Spacer()
}.padding(.vertical)
}
} }
struct AddReaderAPIAccountView_Previews: PreviewProvider { struct AddReaderAPIAccountView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AddReaderAPIAccountView(accountType: .freshRSS) AddReaderAPIAccountView(accountType: .freshRSS)
//AddReaderAPIAccountView(accountType: .inoreader)
} }
} }

View File

@ -59,6 +59,9 @@ extension AccountType {
func image() -> Image { func image() -> Image {
switch self { switch self {
case .onMyMac: case .onMyMac:
// If it's the multiplatform app, the asset catalog contains assets for
return Image("accountLocal") return Image("accountLocal")
case .bazQux: case .bazQux:
return Image("accountBazQux") return Image("accountBazQux")