Transition profile to List container

This commit is contained in:
Thomas Ricouard 2023-02-12 16:13:57 +01:00
parent ad7ca63c3b
commit a959ea3606
3 changed files with 73 additions and 92 deletions

View File

@ -7,6 +7,10 @@ import Shimmer
import SwiftUI
struct AccountDetailHeaderView: View {
enum Constants {
static let headerHeight: CGFloat = 200
}
@EnvironmentObject private var theme: Theme
@EnvironmentObject private var quickLook: QuickLook
@EnvironmentObject private var routerPath: RouterPath
@ -17,16 +21,10 @@ struct AccountDetailHeaderView: View {
let account: Account
let scrollViewProxy: ScrollViewProxy?
@Binding var scrollOffset: CGFloat
private var bannerHeight: CGFloat {
200 + (scrollOffset > 0 ? scrollOffset * 2 : 0)
}
var body: some View {
VStack(alignment: .leading) {
Rectangle()
.frame(height: 200)
.frame(height: Constants.headerHeight)
.overlay {
headerImageView
}
@ -39,7 +37,7 @@ struct AccountDetailHeaderView: View {
if reasons.contains(.placeholder) {
Rectangle()
.foregroundColor(theme.secondaryBackgroundColor)
.frame(height: bannerHeight)
.frame(height: Constants.headerHeight)
} else {
LazyImage(url: account.header) { state in
if let image = state.image {
@ -48,14 +46,14 @@ struct AccountDetailHeaderView: View {
.overlay(account.haveHeader ? .black.opacity(0.50) : .clear)
} else if state.isLoading {
theme.secondaryBackgroundColor
.frame(height: bannerHeight)
.frame(height: Constants.headerHeight)
.shimmering()
} else {
theme.secondaryBackgroundColor
.frame(height: bannerHeight)
.frame(height: Constants.headerHeight)
}
}
.frame(height: bannerHeight)
.frame(height: Constants.headerHeight)
}
if viewModel.relationship?.followedBy == true {
@ -69,8 +67,7 @@ struct AccountDetailHeaderView: View {
}
}
.background(theme.secondaryBackgroundColor)
.frame(height: bannerHeight)
.offset(y: scrollOffset > 0 ? -scrollOffset : 0)
.frame(height: Constants.headerHeight)
.contentShape(Rectangle())
.onTapGesture {
guard account.haveHeader else {
@ -102,16 +99,26 @@ struct AccountDetailHeaderView: View {
} label: {
makeCustomInfoLabel(title: "account.posts", count: account.statusesCount)
}
NavigationLink(value: RouterDestinations.following(id: account.id)) {
.buttonStyle(.borderless)
Button {
routerPath.navigate(to: .following(id: account.id))
} label: {
makeCustomInfoLabel(title: "account.following", count: account.followingCount)
}
NavigationLink(value: RouterDestinations.followers(id: account.id)) {
.buttonStyle(.borderless)
Button {
routerPath.navigate(to: .followers(id: account.id))
} label: {
makeCustomInfoLabel(
title: "account.followers",
count: account.followersCount,
needsBadge: currentAccount.account?.id == account.id && !currentAccount.followRequests.isEmpty
)
}
.buttonStyle(.borderless)
}.offset(y: 20)
}
}
@ -123,6 +130,12 @@ struct AccountDetailHeaderView: View {
VStack(alignment: .leading, spacing: 0) {
EmojiTextApp(.init(stringValue: account.safeDisplayName), emojis: account.emojis)
.font(.scaledHeadline)
.onDisappear {
print("DISPEAR")
}
.onAppear {
print("APPEAR")
}
Text("@\(account.acct)")
.font(.scaledCallout)
.foregroundColor(.gray)
@ -189,7 +202,6 @@ struct AccountDetailHeaderView_Previews: PreviewProvider {
static var previews: some View {
AccountDetailHeaderView(viewModel: .init(account: .placeholder()),
account: .placeholder(),
scrollViewProxy: nil,
scrollOffset: .constant(0))
scrollViewProxy: nil)
}
}

View File

@ -19,7 +19,6 @@ public struct AccountDetailView: View {
@EnvironmentObject private var routerPath: RouterPath
@StateObject private var viewModel: AccountDetailViewModel
@State private var scrollOffset: CGFloat = 0
@State private var isFieldsSheetDisplayed: Bool = false
@State private var isCurrentUser: Bool = false
@State private var isCreateListAlertPresented: Bool = false
@ -40,42 +39,46 @@ public struct AccountDetailView: View {
public var body: some View {
ScrollViewReader { proxy in
ScrollViewOffsetReader { offset in
self.scrollOffset = offset
} content: {
LazyVStack(alignment: .leading) {
List {
Group {
makeHeaderView(proxy: proxy)
familiarFollowers
.offset(y: -36)
featuredTagsView
.offset(y: -36)
Group {
Picker("", selection: $viewModel.selectedTab) {
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
id: \.self) { tab in
Image(systemName: tab.iconName)
.tag(tab)
}
}
.pickerStyle(.segmented)
.padding(.horizontal, .layoutPadding)
.offset(y: -20)
}
.id("status")
}
.listRowInsets(.init())
.listRowSeparator(.hidden)
.listRowBackground(theme.primaryBackgroundColor)
switch viewModel.tabState {
case .statuses:
if viewModel.selectedTab == .statuses {
pinnedPostsView
}
StatusesListView(fetcher: viewModel, isEmbdedInList: false)
case .followedTags:
tagsListView
case .lists:
listsListView
Picker("", selection: $viewModel.selectedTab) {
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
id: \.self) { tab in
Image(systemName: tab.iconName)
.tag(tab)
}
}
.pickerStyle(.segmented)
.padding(.layoutPadding)
.listRowSeparator(.hidden)
.listRowBackground(theme.primaryBackgroundColor)
.listRowInsets(.init())
.id("status")
switch viewModel.tabState {
case .statuses:
if viewModel.selectedTab == .statuses {
pinnedPostsView
.listRowInsets(.init())
.listRowSeparator(.hidden)
.listRowBackground(theme.primaryBackgroundColor)
}
StatusesListView(fetcher: viewModel)
case .followedTags:
tagsListView
case .lists:
listsListView
}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
}
@ -138,14 +141,12 @@ public struct AccountDetailView: View {
case .loading:
AccountDetailHeaderView(viewModel: viewModel,
account: .placeholder(),
scrollViewProxy: proxy,
scrollOffset: $scrollOffset)
scrollViewProxy: proxy)
.redacted(reason: .placeholder)
case let .data(account):
AccountDetailHeaderView(viewModel: viewModel,
account: account,
scrollViewProxy: proxy,
scrollOffset: $scrollOffset)
scrollViewProxy: proxy)
case let .error(error):
Text("Error: \(error.localizedDescription)")
}
@ -270,8 +271,7 @@ public struct AccountDetailView: View {
Spacer()
Image(systemName: "chevron.right")
}
.padding(.horizontal, .layoutPadding)
.padding(.vertical, 8)
.listRowBackground(theme.primaryBackgroundColor)
}
}.task {
await currentAccount.fetchFollowedTags()
@ -282,16 +282,11 @@ public struct AccountDetailView: View {
Group {
ForEach(currentAccount.sortedLists) { list in
NavigationLink(value: RouterDestinations.list(list: list)) {
HStack {
Text(list.title)
Spacer()
Image(systemName: "chevron.right")
}
.padding(.vertical, 8)
.padding(.horizontal, .layoutPadding)
Text(list.title)
.font(.scaledHeadline)
.foregroundColor(theme.labelColor)
}
.listRowBackground(theme.primaryBackgroundColor)
.contextMenu {
Button("account.list.delete", role: .destructive) {
Task {
@ -303,7 +298,9 @@ public struct AccountDetailView: View {
Button("account.list.create") {
isCreateListAlertPresented = true
}
.padding(.horizontal, .layoutPadding)
.tint(theme.tintColor)
.buttonStyle(.borderless)
.listRowBackground(theme.primaryBackgroundColor)
}
.task {
await currentAccount.fetchLists()
@ -348,18 +345,6 @@ public struct AccountDetailView: View {
@ToolbarContentBuilder
private var toolbarContent: some ToolbarContent {
ToolbarItem(placement: .principal) {
if scrollOffset < -170 {
switch viewModel.accountState {
case let .data(account):
EmojiTextApp(.init(stringValue: account.safeDisplayName), emojis: account.emojis)
.font(.scaledHeadline)
default:
EmptyView()
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
if let account = viewModel.account {
@ -544,11 +529,7 @@ public struct AccountDetailView: View {
}
}
} label: {
if scrollOffset < -5 {
Image(systemName: "ellipsis.circle")
} else {
Image(systemName: "ellipsis.circle.fill")
}
Image(systemName: "ellipsis.circle")
}
}
}

View File

@ -9,12 +9,10 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
@ObservedObject private var fetcher: Fetcher
private let isRemote: Bool
private let isEmbdedInList: Bool
public init(fetcher: Fetcher, isRemote: Bool = false, isEmbdedInList: Bool = true) {
public init(fetcher: Fetcher, isRemote: Bool = false) {
self.fetcher = fetcher
self.isRemote = isRemote
self.isEmbdedInList = isEmbdedInList
}
public var body: some View {
@ -22,12 +20,7 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
case .loading:
ForEach(Status.placeholders()) { status in
StatusRowView(viewModel: .init(status: status, isCompact: false))
.padding(.horizontal, isEmbdedInList ? 0 : .layoutPadding)
.redacted(reason: .placeholder)
if !isEmbdedInList {
Divider()
.padding(.vertical, .dividerPadding)
}
}
case .error:
ErrorView(title: "status.error.title",
@ -45,7 +38,6 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
let viewModel = StatusRowViewModel(status: status, isCompact: false, isRemote: isRemote)
if viewModel.filter?.filter.filterAction != .hide {
StatusRowView(viewModel: viewModel)
.padding(.horizontal, isEmbdedInList ? 0 : .layoutPadding)
.id(status.id)
.onAppear {
fetcher.statusDidAppear(status: status)
@ -53,10 +45,6 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
.onDisappear {
fetcher.statusDidDisappear(status: status)
}
if !isEmbdedInList {
Divider()
.padding(.vertical, .dividerPadding)
}
}
}
switch nextPageState {