Compare commits
4 Commits
6df5f7fb08
...
9d9ea565f1
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 9d9ea565f1 | |
Shadowfacts | 99db842411 | |
Shadowfacts | 184fe49c0f | |
Shadowfacts | 4719342a06 |
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -1,5 +1,38 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2020.1 (9)
|
||||||
|
The marquee feature of this build is the new and improved Compose screen. It's been rewritten to use SwiftUI, is significantly more resilient to data loss, and now shows the toolbar when the main text field is not focused. It also turns out Apple is surprise-releasing iOS 14 very soon (or possibly already has, depending when you're reading this). For those who were not already on the beta train, iOS 14 brings a number of new features including a sidebar on iPadOS and lots and lots of context menus (a home screen widget is coming Soon™).
|
||||||
|
|
||||||
|
Known Issues:
|
||||||
|
- Pasting images to create attachments when composing a post is not currently supported due to an iOS bug (#109)
|
||||||
|
- Full-size previews do not display in context menus for attachments on the Compose screen due to an iOS issue (#110)
|
||||||
|
|
||||||
|
Features/Improvements:
|
||||||
|
- Rewrite Compose screen using SwiftUI
|
||||||
|
- Prevent draft posts being lost if the app crahes or is killed by the system while composing
|
||||||
|
- Show toolbar while post content is not being edited
|
||||||
|
- Save post visibility in drafts
|
||||||
|
- Move Draw Something action out of the context menu
|
||||||
|
- iOS 14: Use context menus for setting post visibility
|
||||||
|
- Show BlurHash previews for attachments on Mastodon
|
||||||
|
- Add Expand All Content Warnings preference (Preferences -> Behavior)
|
||||||
|
- Add Collapse Long Posts preference (Preferences -> Behavior)
|
||||||
|
- Improve image gallery opening animation
|
||||||
|
- Use fade in/out animations for opening/closing gallery and attachment picker when the Reduce Motion system setting is enabled
|
||||||
|
- iOS 14: Also requires the "Prefer Cross Fade" setting be enabled
|
||||||
|
- Slightly reduce default status font sizes
|
||||||
|
- Add "Direct Message" context menu action to Compose button on profile screen
|
||||||
|
- Allow viewing attachments and navigating through posts/accounts on instance public timelines
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Fix errors when uploading attachments not displaying
|
||||||
|
- Fix attachments not posting in the correct, user-specified order
|
||||||
|
- Fix accounts displaying with outdated information (avatars, display names, etc.)
|
||||||
|
- Fix Compose not showing button on profile screen
|
||||||
|
- Fix navigation title not being set on profile screen
|
||||||
|
- Fix follow notifications not showing names for users without display names set
|
||||||
|
- iPadOS 14: Fix crash when resizing app in split view mode
|
||||||
|
|
||||||
## 2020.1 (8)
|
## 2020.1 (8)
|
||||||
This is just an emergency build to fix crashes on iOS 13 when selecting attachments. The changelog of the previous build is included below.
|
This is just an emergency build to fix crashes on iOS 13 when selecting attachments. The changelog of the previous build is included below.
|
||||||
|
|
||||||
|
|
|
@ -2229,7 +2229,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 8;
|
CURRENT_PROJECT_VERSION = 9;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = Tusker/Info.plist;
|
INFOPLIST_FILE = Tusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||||
|
@ -2258,7 +2258,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Tusker/Tusker.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 8;
|
CURRENT_PROJECT_VERSION = 9;
|
||||||
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
DEVELOPMENT_TEAM = V4WK9KR9U2;
|
||||||
INFOPLIST_FILE = Tusker/Info.plist;
|
INFOPLIST_FILE = Tusker/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||||
|
|
|
@ -84,23 +84,28 @@ class MastodonController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOwnAccount(completion: ((Account) -> Void)? = nil) {
|
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
|
||||||
if account != nil {
|
if account != nil {
|
||||||
completion?(account)
|
completion?(.success(account))
|
||||||
} else {
|
} else {
|
||||||
let request = Client.getSelfAccount()
|
let request = Client.getSelfAccount()
|
||||||
run(request) { response in
|
run(request) { response in
|
||||||
guard case let .success(account, _) = response else { fatalError() }
|
switch response {
|
||||||
self.account = account
|
case let .failure(error):
|
||||||
self.persistentContainer.backgroundContext.perform {
|
completion?(.failure(error))
|
||||||
if let accountMO = self.persistentContainer.account(for: account.id, in: self.persistentContainer.backgroundContext) {
|
|
||||||
accountMO.updateFrom(apiAccount: account, container: self.persistentContainer)
|
case let .success(account, _):
|
||||||
} else {
|
self.account = account
|
||||||
// the first time the user's account is added to the store,
|
self.persistentContainer.backgroundContext.perform {
|
||||||
// increment its reference count so that it's never removed
|
if let accountMO = self.persistentContainer.account(for: account.id, in: self.persistentContainer.backgroundContext) {
|
||||||
self.persistentContainer.addOrUpdate(account: account, incrementReferenceCount: true)
|
accountMO.updateFrom(apiAccount: account, container: self.persistentContainer)
|
||||||
|
} else {
|
||||||
|
// the first time the user's account is added to the store,
|
||||||
|
// increment its reference count so that it's never removed
|
||||||
|
self.persistentContainer.addOrUpdate(account: account, incrementReferenceCount: true)
|
||||||
|
}
|
||||||
|
completion?(.success(account))
|
||||||
}
|
}
|
||||||
completion?(account)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,10 +119,29 @@ extension MastodonController {
|
||||||
func createDraft(inReplyToID: String? = nil, mentioningAcct: String? = nil) -> Draft {
|
func createDraft(inReplyToID: String? = nil, mentioningAcct: String? = nil) -> Draft {
|
||||||
var acctsToMention = [String]()
|
var acctsToMention = [String]()
|
||||||
|
|
||||||
|
var visibility = Preferences.shared.defaultPostVisibility
|
||||||
|
var contentWarning = ""
|
||||||
|
|
||||||
if let inReplyToID = inReplyToID,
|
if let inReplyToID = inReplyToID,
|
||||||
let inReplyTo = persistentContainer.status(for: inReplyToID) {
|
let inReplyTo = persistentContainer.status(for: inReplyToID) {
|
||||||
acctsToMention.append(inReplyTo.account.acct)
|
acctsToMention.append(inReplyTo.account.acct)
|
||||||
acctsToMention.append(contentsOf: inReplyTo.mentions.map(\.acct))
|
acctsToMention.append(contentsOf: inReplyTo.mentions.map(\.acct))
|
||||||
|
visibility = inReplyTo.visibility
|
||||||
|
|
||||||
|
if !inReplyTo.spoilerText.isEmpty {
|
||||||
|
switch Preferences.shared.contentWarningCopyMode {
|
||||||
|
case .doNotCopy:
|
||||||
|
break
|
||||||
|
case .asIs:
|
||||||
|
contentWarning = inReplyTo.spoilerText
|
||||||
|
case .prependRe:
|
||||||
|
if inReplyTo.spoilerText.lowercased().starts(with: "re:") {
|
||||||
|
contentWarning = inReplyTo.spoilerText
|
||||||
|
} else {
|
||||||
|
contentWarning = "re: \(inReplyTo.spoilerText)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let mentioningAcct = mentioningAcct {
|
if let mentioningAcct = mentioningAcct {
|
||||||
acctsToMention.append(mentioningAcct)
|
acctsToMention.append(mentioningAcct)
|
||||||
|
@ -136,6 +155,9 @@ extension MastodonController {
|
||||||
draft.inReplyToID = inReplyToID
|
draft.inReplyToID = inReplyToID
|
||||||
draft.text = acctsToMention.map { "@\($0) " }.joined()
|
draft.text = acctsToMention.map { "@\($0) " }.joined()
|
||||||
draft.initialText = draft.text
|
draft.initialText = draft.text
|
||||||
|
draft.visibility = visibility
|
||||||
|
draft.contentWarning = contentWarning
|
||||||
|
draft.contentWarningEnabled = !contentWarning.isEmpty
|
||||||
return draft
|
return draft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,13 +70,21 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate
|
||||||
let tempAccountInfo = LocalData.UserAccountInfo(id: "temp", instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: nil, accessToken: accessToken)
|
let tempAccountInfo = LocalData.UserAccountInfo(id: "temp", instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: nil, accessToken: accessToken)
|
||||||
mastodonController.accountInfo = tempAccountInfo
|
mastodonController.accountInfo = tempAccountInfo
|
||||||
|
|
||||||
mastodonController.getOwnAccount { (account) in
|
mastodonController.getOwnAccount { (result) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// this needs to happen on the main thread because it publishes a new value for the ObservableObject
|
switch result {
|
||||||
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
|
case let .failure(error):
|
||||||
mastodonController.accountInfo = accountInfo
|
let alert = UIAlertController(title: "Unable to Verify Credentials", message: "Your account could not be fetched at this time: \(error.localizedDescription)", preferredStyle: .alert)
|
||||||
|
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
|
||||||
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
self.present(alert, animated: true)
|
||||||
|
|
||||||
|
case let .success(account):
|
||||||
|
// this needs to happen on the main thread because it publishes a new value for the ObservableObject
|
||||||
|
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
|
||||||
|
mastodonController.accountInfo = accountInfo
|
||||||
|
|
||||||
|
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,8 @@ struct LocalAccountAvatarView: View {
|
||||||
|
|
||||||
func loadImage() {
|
func loadImage() {
|
||||||
let controller = MastodonController.getForAccount(localAccountInfo)
|
let controller = MastodonController.getForAccount(localAccountInfo)
|
||||||
controller.getOwnAccount { (account) in
|
controller.getOwnAccount { (result) in
|
||||||
|
guard case let .success(account) = result else { return }
|
||||||
_ = ImageCache.avatars.get(account.avatar) { (data) in
|
_ = ImageCache.avatars.get(account.avatar) { (data) in
|
||||||
if let data = data, let image = UIImage(data: data) {
|
if let data = data, let image = UIImage(data: data) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
|
|
@ -16,7 +16,9 @@ class MyProfileViewController: ProfileViewController {
|
||||||
title = "My Profile"
|
title = "My Profile"
|
||||||
tabBarItem.image = UIImage(systemName: "person.fill")
|
tabBarItem.image = UIImage(systemName: "person.fill")
|
||||||
|
|
||||||
mastodonController.getOwnAccount { (account) in
|
mastodonController.getOwnAccount { (result) in
|
||||||
|
guard case let .success(account) = result else { return }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.accountID = account.id
|
self.accountID = account.id
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue