mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-22 16:31:00 +00:00
Transition profile to List container
This commit is contained in:
parent
ad7ca63c3b
commit
a959ea3606
3 changed files with 73 additions and 92 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
||||
switch viewModel.tabState {
|
||||
case .statuses:
|
||||
if viewModel.selectedTab == .statuses {
|
||||
pinnedPostsView
|
||||
}
|
||||
StatusesListView(fetcher: viewModel, isEmbdedInList: false)
|
||||
case .followedTags:
|
||||
tagsListView
|
||||
case .lists:
|
||||
listsListView
|
||||
}
|
||||
.listRowInsets(.init())
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue