Fix crash when opening Compose screen before account/instance is loaded
Prevents when opening the Compose screen with poor network connectivity
This commit is contained in:
parent
9b85090884
commit
809584cc54
|
@ -9,7 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
class MastodonController {
|
class MastodonController: ObservableObject {
|
||||||
|
|
||||||
static private(set) var all = [LocalData.UserAccountInfo: MastodonController]()
|
static private(set) var all = [LocalData.UserAccountInfo: MastodonController]()
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ class MastodonController {
|
||||||
|
|
||||||
let client: Client!
|
let client: Client!
|
||||||
|
|
||||||
var account: Account!
|
@Published private(set) var account: Account!
|
||||||
var instance: Instance!
|
@Published private(set) var instance: Instance!
|
||||||
|
|
||||||
var loggedIn: Bool {
|
var loggedIn: Bool {
|
||||||
accountInfo != nil
|
accountInfo != nil
|
||||||
|
@ -95,7 +95,9 @@ class MastodonController {
|
||||||
completion?(.failure(error))
|
completion?(.failure(error))
|
||||||
|
|
||||||
case let .success(account, _):
|
case let .success(account, _):
|
||||||
self.account = account
|
DispatchQueue.main.async {
|
||||||
|
self.account = account
|
||||||
|
}
|
||||||
self.persistentContainer.backgroundContext.perform {
|
self.persistentContainer.backgroundContext.perform {
|
||||||
if let accountMO = self.persistentContainer.account(for: account.id, in: self.persistentContainer.backgroundContext) {
|
if let accountMO = self.persistentContainer.account(for: account.id, in: self.persistentContainer.backgroundContext) {
|
||||||
accountMO.updateFrom(apiAccount: account, container: self.persistentContainer)
|
accountMO.updateFrom(apiAccount: account, container: self.persistentContainer)
|
||||||
|
@ -118,13 +120,12 @@ class MastodonController {
|
||||||
let request = Client.getInstance()
|
let request = Client.getInstance()
|
||||||
run(request) { (response) in
|
run(request) { (response) in
|
||||||
guard case let .success(instance, _) = response else { fatalError() }
|
guard case let .success(instance, _) = response else { fatalError() }
|
||||||
self.instance = instance
|
DispatchQueue.main.async {
|
||||||
completion?(instance)
|
self.instance = instance
|
||||||
|
completion?(instance)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObservableObject so that SwiftUI views can receive it through @EnvironmentObject
|
|
||||||
extension MastodonController: ObservableObject {}
|
|
||||||
|
|
|
@ -91,7 +91,9 @@ struct ComposeAttachmentsList: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var canAddAttachment: Bool {
|
private var canAddAttachment: Bool {
|
||||||
switch mastodonController.instance.instanceType {
|
switch mastodonController.instance?.instanceType {
|
||||||
|
case nil:
|
||||||
|
return false
|
||||||
case .pleroma:
|
case .pleroma:
|
||||||
return true
|
return true
|
||||||
case .mastodon:
|
case .mastodon:
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ComposeAvatarImageView: View {
|
struct ComposeAvatarImageView: View {
|
||||||
let url: URL
|
let url: URL?
|
||||||
@State var request: ImageCache.Request? = nil
|
@State var request: ImageCache.Request? = nil
|
||||||
@State var avatarImage: UIImage? = nil
|
@State var avatarImage: UIImage? = nil
|
||||||
@ObservedObject var preferences = Preferences.shared
|
@ObservedObject var preferences = Preferences.shared
|
||||||
|
@ -19,7 +19,9 @@ struct ComposeAvatarImageView: View {
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 50, height: 50)
|
.frame(width: 50, height: 50)
|
||||||
.cornerRadius(preferences.avatarStyle.cornerRadiusFraction * 50)
|
.cornerRadius(preferences.avatarStyle.cornerRadiusFraction * 50)
|
||||||
.onAppear(perform: self.loadImage)
|
.conditionally(url != nil) {
|
||||||
|
$0.onAppear(perform: self.loadImage)
|
||||||
|
}
|
||||||
.onDisappear(perform: self.cancelRequest)
|
.onDisappear(perform: self.cancelRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,24 +29,33 @@ struct ComposeAvatarImageView: View {
|
||||||
if let avatarImage = avatarImage {
|
if let avatarImage = avatarImage {
|
||||||
return Image(uiImage: avatarImage)
|
return Image(uiImage: avatarImage)
|
||||||
} else {
|
} else {
|
||||||
let imageName: String
|
return placeholderImage
|
||||||
switch preferences.avatarStyle {
|
|
||||||
case .circle:
|
|
||||||
imageName = "person.crop.circle"
|
|
||||||
case .roundRect:
|
|
||||||
imageName = "person.crop.square"
|
|
||||||
}
|
|
||||||
return Image(systemName: imageName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var placeholderImage: Image {
|
||||||
|
let imageName: String
|
||||||
|
switch preferences.avatarStyle {
|
||||||
|
case .circle:
|
||||||
|
imageName = "person.crop.circle"
|
||||||
|
case .roundRect:
|
||||||
|
imageName = "person.crop.square"
|
||||||
|
}
|
||||||
|
return Image(systemName: imageName)
|
||||||
|
}
|
||||||
|
|
||||||
private func loadImage() {
|
private func loadImage() {
|
||||||
|
guard let url = url else { return }
|
||||||
request = ImageCache.avatars.get(url) { (data) in
|
request = ImageCache.avatars.get(url) { (data) in
|
||||||
DispatchQueue.main.async {
|
if let data = data, let image = UIImage(data: data) {
|
||||||
self.request = nil
|
DispatchQueue.main.async {
|
||||||
if let data = data, let image = UIImage(data: data) {
|
self.request = nil
|
||||||
self.avatarImage = image
|
self.avatarImage = image
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.request = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,24 +12,29 @@ import Pachyderm
|
||||||
struct ComposeCurrentAccount: View {
|
struct ComposeCurrentAccount: View {
|
||||||
@EnvironmentObject var mastodonController: MastodonController
|
@EnvironmentObject var mastodonController: MastodonController
|
||||||
|
|
||||||
var account: Account {
|
var account: Account? {
|
||||||
mastodonController.account!
|
mastodonController.account
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .top) {
|
HStack(alignment: .top) {
|
||||||
ComposeAvatarImageView(url: account.avatar)
|
ComposeAvatarImageView(url: account?.avatar)
|
||||||
.accessibility(label: Text("\(account.displayName) avatar"))
|
.accessibility(label: Text(account != nil ? "\(account!.displayName) avatar" : "Avatar"))
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
if let id = account?.id,
|
||||||
AccountDisplayNameLabel(account: mastodonController.persistentContainer.account(for: account.id)!, fontSize: 20)
|
let account = mastodonController.persistentContainer.account(for: id) {
|
||||||
.lineLimit(1)
|
VStack(alignment: .leading) {
|
||||||
|
AccountDisplayNameLabel(account: account, fontSize: 20)
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
Text(verbatim: "@\(account.acct)")
|
Text(verbatim: "@\(account.acct)")
|
||||||
.font(.system(size: 17, weight: .light))
|
.font(.system(size: 17, weight: .light))
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var charactersRemaining: Int {
|
var charactersRemaining: Int {
|
||||||
let limit = mastodonController.instance.maxStatusCharacters ?? 500
|
let limit = mastodonController.instance?.maxStatusCharacters ?? 500
|
||||||
let cwCount = draft.contentWarningEnabled ? draft.contentWarning.count : 0
|
let cwCount = draft.contentWarningEnabled ? draft.contentWarning.count : 0
|
||||||
return limit - (cwCount + CharacterCounter.count(text: draft.text))
|
return limit - (cwCount + CharacterCounter.count(text: draft.text))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue