Fixes and optimisation
This commit is contained in:
parent
0e6e63ea23
commit
b524188790
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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" : {
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user