Fixes and optimisation

This commit is contained in:
Lumaa 2024-01-10 17:45:41 +01:00
parent 0e6e63ea23
commit b524188790
4 changed files with 112 additions and 152 deletions

View File

@ -20,24 +20,12 @@ struct CompactPostView: View {
var body: some View { var body: some View {
VStack { VStack {
VStack(alignment: .leading) { statusPost(status)
if pinned { .contentShape(Rectangle())
pinnedNotice .onTapGesture {
.padding(.leading, 35) navigator.navigate(to: .post(status: status))
} }
if status.reblog != nil {
repostNotice
.padding(.leading, 30)
}
if detailed {
detailedStatusPost(status.reblog ?? status)
} else {
statusPost(status.reblog ?? status)
}
}
if !quoted { if !quoted {
Rectangle() Rectangle()
.fill(Color.gray.opacity(0.2)) .fill(Color.gray.opacity(0.2))
@ -133,13 +121,25 @@ struct CompactPostView: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
// MARK: Status main content // MARK: Status main content
VStack(alignment: .leading, spacing: 10) { VStack(alignment: .leading, spacing: 10) {
Text(status.account.username) VStack(alignment: .leading, spacing: 2) {
.font(quoted ? .callout : .body) Text(status.account.username)
.multilineTextAlignment(.leading) .font(quoted ? .callout : .body)
.bold() .multilineTextAlignment(.leading)
.onTapGesture { .bold()
navigator.navigate(to: .account(acc: status.account)) .onTapGesture {
navigator.navigate(to: .account(acc: status.account))
}
if status.inReplyToAccountId != nil {
if let user = status.mentions.first(where: { $0.id == status.inReplyToAccountId }) {
Text("status.replied-to.\(user.username)")
.multilineTextAlignment(.leading)
.lineLimit(1)
.font(.caption)
.foregroundStyle(Color(uiColor: UIColor.label).opacity(0.3))
}
} }
}
if !status.content.asRawText.isEmpty { if !status.content.asRawText.isEmpty {
TextEmoji(status.content, emojis: status.emojis, language: status.language) TextEmoji(status.content, emojis: status.emojis, language: status.language)
@ -184,21 +184,20 @@ struct CompactPostView: View {
HStack(spacing: 13) { HStack(spacing: 13) {
asyncActionButton(isLiked ? "heart.fill" : "heart") { asyncActionButton(isLiked ? "heart.fill" : "heart") {
do { do {
try await likePost()
HapticManager.playHaptics(haptics: Haptic.tap) HapticManager.playHaptics(haptics: Haptic.tap)
try await likePost()
} catch { } catch {
HapticManager.playHaptics(haptics: Haptic.error) HapticManager.playHaptics(haptics: Haptic.error)
print("Error: \(error.localizedDescription)") print("Error: \(error.localizedDescription)")
} }
} }
actionButton("bubble.right") { actionButton("bubble.right") {
print("reply") navigator.presentedSheet = .post(content: "@\(status.account.acct)", replyId: status.id)
navigator.presentedSheet = .post()
} }
asyncActionButton(isReposted ? "bolt.horizontal.fill" : "bolt.horizontal") { asyncActionButton(isReposted ? "bolt.horizontal.fill" : "bolt.horizontal") {
do { do {
try await repostPost()
HapticManager.playHaptics(haptics: Haptic.tap) HapticManager.playHaptics(haptics: Haptic.tap)
try await repostPost()
} catch { } catch {
HapticManager.playHaptics(haptics: Haptic.error) HapticManager.playHaptics(haptics: Haptic.error)
print("Error: \(error.localizedDescription)") print("Error: \(error.localizedDescription)")
@ -219,116 +218,38 @@ struct CompactPostView: View {
} }
} }
@ViewBuilder var notices: some View {
func detailedStatusPost(_ status: AnyStatus) -> some View { ZStack {
VStack { if pinned {
HStack { HStack (alignment:.center, spacing: 5) {
profilePicture Image(systemName: "pin.fill")
Text(status.account.username) Text("status.pinned")
.multilineTextAlignment(.leading)
.bold()
}
.onTapGesture {
navigator.navigate(to: .account(acc: status.account))
}
.contentShape(Rectangle())
VStack(alignment: .leading) {
// MARK: Status main content
VStack(alignment: .leading, spacing: 10) {
if !status.content.asRawText.isEmpty {
TextEmoji(status.content, emojis: status.emojis, language: status.language)
.multilineTextAlignment(.leading)
.frame(width: 300, alignment: .topLeading)
.fixedSize(horizontal: false, vertical: true)
.font(.callout)
}
if status.card != nil && status.mediaAttachments.isEmpty {
PostCardView(card: status.card!)
}
if !status.mediaAttachments.isEmpty {
ForEach(status.mediaAttachments) { attachment in
PostAttachment(attachment: attachment)
}
}
if hasQuote {
if quoteStatus != nil {
QuotePostView(status: quoteStatus!)
} else {
ProgressView()
.progressViewStyle(.circular)
}
}
} }
.padding(.leading, 20)
.multilineTextAlignment(.leading)
.lineLimit(1)
.font(.caption)
.foregroundStyle(Color(uiColor: UIColor.label).opacity(0.3))
.padding(.leading, 35)
}
//MARK: Action buttons if status.reblog != nil {
HStack(spacing: 13) { HStack (alignment:.center, spacing: 5) {
asyncActionButton(isLiked ? "heart.fill" : "heart") { Image(systemName: "bolt.horizontal")
do {
try await likePost() Text("status.reposted-by.\(status.account.username)")
HapticManager.playHaptics(haptics: Haptic.tap)
} catch {
HapticManager.playHaptics(haptics: Haptic.error)
print("Error: \(error.localizedDescription)")
}
}
actionButton("bubble.right") {
print("reply")
navigator.presentedSheet = .post()
}
asyncActionButton(isReposted ? "bolt.horizontal.fill" : "bolt.horizontal") {
do {
try await repostPost()
HapticManager.playHaptics(haptics: Haptic.tap)
} catch {
HapticManager.playHaptics(haptics: Haptic.error)
print("Error: \(error.localizedDescription)")
}
}
ShareLink(item: URL(string: status.url ?? "https://joinmastodon.org/")!) {
Image(systemName: "square.and.arrow.up")
.font(.title2)
}
.tint(Color(uiColor: UIColor.label))
} }
.padding(.top) .padding(.leading, 20)
.multilineTextAlignment(.leading)
// MARK: Status stats .lineLimit(1)
stats.padding(.top, 5) .font(.caption)
.foregroundStyle(Color(uiColor: UIColor.label).opacity(0.3))
.padding(.leading, 30)
} }
} }
} }
var pinnedNotice: some View {
HStack (alignment:.center, spacing: 5) {
Image(systemName: "pin.fill")
Text("status.pinned")
}
.padding(.leading, 20)
.multilineTextAlignment(.leading)
.lineLimit(1)
.font(.caption)
.foregroundStyle(Color(uiColor: UIColor.label).opacity(0.3))
}
var repostNotice: some View {
HStack (alignment:.center, spacing: 5) {
Image(systemName: "bolt.horizontal")
Text("status.reposted-by.\(status.account.username)")
}
.padding(.leading, 20)
.multilineTextAlignment(.leading)
.lineLimit(1)
.font(.caption)
.foregroundStyle(Color(uiColor: UIColor.label).opacity(0.3))
}
var profilePicture: some View { var profilePicture: some View {
if status.reblog != nil { if status.reblog != nil {
OnlineImage(url: status.reblog!.account.avatar, size: 50, useNuke: true) OnlineImage(url: status.reblog!.account.avatar, size: 50, useNuke: true)

View File

@ -38,7 +38,7 @@ public enum TabDestination: Identifiable {
public enum SheetDestination: Identifiable { public enum SheetDestination: Identifiable {
case welcome case welcome
case mastodonLogin(logged: Binding<Bool>) case mastodonLogin(logged: Binding<Bool>)
case post(content: String = "") case post(content: String = "", replyId: String? = nil)
public var id: String { public var id: String {
switch self { switch self {
@ -70,6 +70,7 @@ public enum RouterDestination: Hashable {
case privacy case privacy
case appearence case appearence
case account(acc: Account) case account(acc: Account)
case post(status: Status)
case about case about
} }
@ -85,6 +86,8 @@ extension View {
AppearenceView() AppearenceView()
case .account(let acc): case .account(let acc):
AccountView(account: acc, navigator: navigator) AccountView(account: acc, navigator: navigator)
case .post(let status):
PostDetailsView(status: status)
case .about: case .about:
AboutView() AboutView()
} }
@ -114,9 +117,9 @@ extension View {
} }
} else { } else {
switch destination { switch destination {
case .post(let content): case .post(let content, let replyId):
NavigationStack { NavigationStack {
PostingView(initialString: content) PostingView(initialString: content, replyId: replyId)
.tint(Color(uiColor: UIColor.label)) .tint(Color(uiColor: UIColor.label))
} }
case let .mastodonLogin(logged): case let .mastodonLogin(logged):

View File

@ -355,7 +355,14 @@
} }
}, },
"setting.experimental.activate" : { "setting.experimental.activate" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Show experimental features"
}
}
}
}, },
"setting.privacy" : { "setting.privacy" : {
"localizations" : { "localizations" : {
@ -519,6 +526,16 @@
} }
} }
}, },
"status.replied-to.%@" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Replied to @%@"
}
}
}
},
"status.replies-%lld" : { "status.replies-%lld" : {
"localizations" : { "localizations" : {
"en" : { "en" : {

View File

@ -10,6 +10,7 @@ struct PostingView: View {
@Environment(Navigator.self) private var navigator: Navigator @Environment(Navigator.self) private var navigator: Navigator
public var initialString: String = "" public var initialString: String = ""
public var replyId: String? = nil
@State private var viewModel: PostingView.ViewModel = PostingView.ViewModel() @State private var viewModel: PostingView.ViewModel = PostingView.ViewModel()
@ -43,7 +44,7 @@ struct PostingView: View {
viewModel.textView = textView viewModel.textView = textView
}) })
.placeholder(String(localized: "status.posting.placeholder")) .placeholder(String(localized: "status.posting.placeholder"))
.keyboardType(.twitter) .setKeyboardType(.twitter)
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
.font(.callout) .font(.callout)
.foregroundStyle(Color(uiColor: UIColor.label)) .foregroundStyle(Color(uiColor: UIColor.label))
@ -91,12 +92,11 @@ struct PostingView: View {
Task { Task {
if let client = accountManager.getClient() { if let client = accountManager.getClient() {
postingStatus = true postingStatus = true
let postedStatus: Status = try await client.post(endpoint: Statuses.postStatus(json: .init(status: viewModel.postText.string, visibility: visibility))) let newStatus: Status = try await client.post(endpoint: Statuses.postStatus(json: .init(status: viewModel.postText.string, visibility: visibility, inReplyToId: replyId)))
postingStatus = false postingStatus = false
HapticManager.playHaptics(haptics: Haptic.success)
dismiss() dismiss()
navigator.navigate(to: .post(status: newStatus))
// navigate to account until PostDetailsView is fully finished
navigator.navigate(to: .account(acc: accountManager.forceAccount()))
} }
} }
} label: { } label: {
@ -127,9 +127,7 @@ struct PostingView: View {
} }
} }
.onAppear { .onAppear {
let newTxt = NSMutableAttributedString(string: "abc") viewModel.append(text: initialString)
viewModel.postText.append(newTxt)
} }
} }
@ -150,7 +148,7 @@ struct PostingView: View {
actionButton("number") { actionButton("number") {
DispatchQueue.main.async { DispatchQueue.main.async {
viewModel.postText.append(NSMutableAttributedString(string: "#")) viewModel.append(text: "#")
} }
} }
} }
@ -187,21 +185,42 @@ struct PostingView: View {
.clipShape(.circle) .clipShape(.circle)
} }
public class ViewModel: NSObject, UITextPasteDelegate { @Observable public class ViewModel: NSObject {
init(postText: NSMutableAttributedString = .init(string: ""), textView: UITextView? = nil) { init(text: String = "") {
self.postText = postText self.postText = NSMutableAttributedString(string: text)
self.textView = textView
} }
@State var postText: NSMutableAttributedString { var selectedRange: NSRange {
didSet { get {
textView?.attributedText = postText guard let textView else {
return .init(location: 0, length: 0)
}
return textView.selectedRange
}
set {
textView?.selectedRange = newValue
} }
} }
var textView: UITextView? {
var postText: NSMutableAttributedString {
didSet { didSet {
textView?.pasteDelegate = self let range = selectedRange
formatText()
textView?.attributedText = postText
selectedRange = range
} }
} }
var textView: UITextView?
func append(text: String) {
let string = postText
string.mutableString.insert(text, at: selectedRange.location)
postText = string
selectedRange = NSRange(location: selectedRange.location + text.utf16.count, length: 0)
}
func formatText() {
postText.addAttributes([.foregroundColor : UIColor.label, .font: UIFont.preferredFont(forTextStyle: .callout), .backgroundColor: UIColor.clear, .underlineColor: UIColor.clear], range: NSMakeRange(0, postText.string.utf16.count))
}
} }
} }