Progress flow

This commit is contained in:
Thomas Ricouard 2024-06-11 07:59:08 +02:00
parent 409680e9b8
commit a705a71c45
10 changed files with 72 additions and 17 deletions

View file

@ -5,6 +5,7 @@ import Observation
import SafariServices
import SwiftUI
import AppAccount
import WebKit
extension View {
@MainActor func withSafariRouter() -> some View {
@ -19,6 +20,7 @@ private struct SafariRouter: ViewModifier {
@Environment(UserPreferences.self) private var preferences
@Environment(RouterPath.self) private var routerPath
@Environment(AppAccountsManager.self) private var appAccount
@Environment(TipedUsers.self) private var tipedUsers
#if !os(visionOS)
@State private var safariManager = InAppSafariManager()
@ -34,8 +36,9 @@ private struct SafariRouter: ViewModifier {
.onOpenURL { url in
// Open external URL (from icecubesapp://)
guard !isSecondaryColumn else { return }
if url.lastPathComponent == "socialproxy" {
if url.absoluteString == "icecubesapp://socialproxy" {
safariManager.dismiss()
TipedUsers.shared.tipedUserCount += 1
return
}
let urlString = url.absoluteString.replacingOccurrences(of: AppInfo.scheme, with: "https://")
@ -55,8 +58,8 @@ private struct SafariRouter: ViewModifier {
}
} else if url.host() == "social-proxy.com", let accountName = appAccount.currentAccount.accountName {
let newURL = url.appending(queryItems: [
.init(name: "callback", value: "icecubesapp"),
.init(name: "id", value: accountName)
.init(name: "callback", value: "icecubesapp://socialproxy"),
.init(name: "id", value: "@\(accountName)")
])
return safariManager.open(newURL)
}
@ -116,6 +119,9 @@ private struct SafariRouter: ViewModifier {
func dismiss() {
viewController.presentedViewController?.dismiss(animated: true)
window?.resignKey()
window?.isHidden = false
window = nil
}
func setupWindow(windowScene: UIWindowScene) -> UIWindow {

View file

@ -22,6 +22,7 @@ let package = Package(
.package(name: "StatusKit", path: "../StatusKit"),
.package(name: "Env", path: "../Env"),
.package(url: "https://github.com/Dean151/ButtonKit", from: "0.1.1"),
.package(url: "https://github.com/dkk/WrappingHStack", from: "2.2.11"),
],
targets: [
.target(
@ -32,6 +33,7 @@ let package = Package(
.product(name: "StatusKit", package: "StatusKit"),
.product(name: "Env", package: "Env"),
.product(name: "ButtonKit", package: "ButtonKit"),
.product(name: "WrappingHStack", package: "WrappingHStack"),
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),

View file

@ -16,6 +16,7 @@ struct AccountDetailHeaderView: View {
@Environment(QuickLook.self) private var quickLook
@Environment(RouterPath.self) private var routerPath
@Environment(CurrentAccount.self) private var currentAccount
@Environment(TipedUsers.self) private var tipedUsers
@Environment(\.redactionReasons) private var reasons
@Environment(\.isSupporter) private var isSupporter: Bool
@ -45,6 +46,13 @@ struct AccountDetailHeaderView: View {
}
accountInfoView
}
.onChange(of: tipedUsers.tipedUserCount, { _, _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
Task {
try? await viewModel.followButtonViewModel?.refreshRelationship()
}
}
})
}
private var headerImageView: some View {
@ -211,20 +219,17 @@ struct AccountDetailHeaderView: View {
.accessibilityRespondsToUserInteraction(false)
movedToView
joinedAtView
tipView
if viewModel.isProAccount {
tipView
}
}
.accessibilityElement(children: .contain)
.accessibilitySortPriority(1)
Spacer()
if let relationship = viewModel.relationship, !viewModel.isCurrentUser {
if let followButtonViewModel = viewModel.followButtonViewModel, !viewModel.isCurrentUser {
HStack {
FollowButton(viewModel: .init(accountId: account.id,
relationship: relationship,
shouldDisplayNotify: true,
relationshipUpdated: { relationship in
viewModel.relationship = relationship
}))
FollowButton(viewModel: followButtonViewModel)
}
} else if !viewModel.isCurrentUser {
ProgressView()
@ -319,6 +324,9 @@ struct AccountDetailHeaderView: View {
private var tipView: some View {
Button {
isTipSheetPresented = true
Task {
try? await viewModel.followButtonViewModel?.follow()
}
} label: {
Text("$ Send tip")
}

View file

@ -79,6 +79,12 @@ import SwiftUI
var translation: Translation?
var isLoadingTranslation = false
var followButtonViewModel: FollowButtonViewModel?
var isProAccount: Bool {
account?.url?.host() == "social-proxy.com"
}
private(set) var account: Account?
private var tabTask: Task<Void, Never>?
@ -117,6 +123,14 @@ import SwiftUI
featuredTags = data.featuredTags
featuredTags.sort { $0.statusesCountInt > $1.statusesCountInt }
relationship = data.relationships.first
if let relationship {
followButtonViewModel = .init(accountId: accountId,
relationship: relationship,
shouldDisplayNotify: true,
relationshipUpdated: { [weak self] relationship in
self?.relationship = relationship
})
}
} catch {
if let account {
accountState = .data(account: account)

View file

@ -30,7 +30,8 @@ import SwiftUI
func follow() async throws {
guard let client else { return }
do {
relationship = try await client.post(endpoint: Accounts.follow(id: accountId, notify: false, reblogs: true))
_ = try await client.post(endpoint: Accounts.follow(id: accountId, notify: false, reblogs: true))
try await refreshRelationship()
relationshipUpdated(relationship)
} catch {
throw error
@ -40,12 +41,21 @@ import SwiftUI
func unfollow() async throws {
guard let client else { return }
do {
relationship = try await client.post(endpoint: Accounts.unfollow(id: accountId))
_ = try await client.post(endpoint: Accounts.unfollow(id: accountId))
try await refreshRelationship()
relationshipUpdated(relationship)
} catch {
throw error
}
}
func refreshRelationship() async throws {
guard let client else { return }
let relationships: [Relationship] = try await client.get(endpoint: Accounts.relationships(ids: [accountId]))
if let relationship = relationships.first {
self.relationship = relationship
}
}
func toggleNotify() async throws {
guard let client else { return }
@ -83,7 +93,7 @@ public struct FollowButton: View {
public var body: some View {
VStack(alignment: .trailing) {
AsyncButton {
if viewModel.relationship.following {
if viewModel.relationship.following || viewModel.relationship.requested {
try await viewModel.unfollow()
} else {
try await viewModel.follow()

View file

@ -3,6 +3,7 @@ import Models
import Env
import DesignSystem
import WrappingHStack
import AppAccount
@MainActor
struct TipSheetView: View {
@ -10,6 +11,8 @@ struct TipSheetView: View {
@Environment(\.dismiss) private var dismiss
@Environment(Theme.self) private var theme: Theme
@Environment(TipedUsers.self) private var tipedUSers: TipedUsers
@Environment(\.openURL) private var openURL
@Environment(AppAccountsManager.self) private var appAccount: AppAccountsManager
@State private var selectedTip: String?
@ -122,5 +125,13 @@ struct TipSheetView: View {
}
.font(.title)
.fontWeight(.bold)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if let accountName = appAccount.currentAccount.accountName,
let url = URL(string: "https://social-proxy.com/subscribe/to/\(account.username)?callback=icecubesapp://socialproxy&id=@\(accountName)") {
openURL(url)
}
}
}
}
}

View file

@ -21,7 +21,6 @@ let package = Package(
.package(name: "Env", path: "../Env"),
.package(url: "https://github.com/kean/Nuke", from: "12.4.0"),
.package(url: "https://github.com/divadretlaw/EmojiText", from: "4.0.0"),
.package(url: "https://github.com/dkk/WrappingHStack", from: "2.2.11"),
],
targets: [
.target(
@ -32,7 +31,6 @@ let package = Package(
.product(name: "NukeUI", package: "Nuke"),
.product(name: "Nuke", package: "Nuke"),
.product(name: "EmojiText", package: "EmojiText"),
.product(name: "WrappingHStack", package: "WrappingHStack"),
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),

View file

@ -5,6 +5,8 @@ import Foundation
public class TipedUsers {
public var usersIds: [String] = []
public var tipedUserCount: Int = 0
static public let shared = TipedUsers()
private init() { }

View file

@ -70,6 +70,10 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable
public var haveHeader: Bool {
header.lastPathComponent != "missing.png"
}
public var fullAccountName: String {
"\(acct)@\(url?.host() ?? "")"
}
public init(id: String, username: String, displayName: String?, avatar: URL, header: URL, acct: String, note: HTMLString, createdAt: ServerDate, followersCount: Int, followingCount: Int, statusesCount: Int, lastStatusAt: String? = nil, fields: [Account.Field], locked: Bool, emojis: [Emoji], url: URL? = nil, source: Account.Source? = nil, bot: Bool, discoverable: Bool? = nil, moved: Account? = nil) {
self.id = id

View file

@ -1,6 +1,6 @@
import Foundation
public struct Relationship: Codable {
public struct Relationship: Codable, Equatable, Identifiable {
public let id: String
public let following: Bool
public let showingReblogs: Bool