Skip to content

Commit

Permalink
Add ability to reorder Watchlist in manual sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
ealymbaev committed May 21, 2024
1 parent 0148e2c commit a93b275
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ extension WatchlistManager {
coinUidsSubject.eraseToAnyPublisher()
}

func set(coinUids: [String]) {
self.coinUids = coinUids
}

func add(coinUid: String) {
guard !coinUids.contains(coinUid) else {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct MarketCoinsView: View {
}
}
.sheet(item: $presentedFullCoin) { fullCoin in
CoinPageViewNew(coinUid: fullCoin.coin.uid)
CoinPageViewNew(coinUid: fullCoin.coin.uid).ignoresSafeArea()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ struct MarketWatchlistView: View {

@State private var sortBySelectorPresented = false
@State private var timePeriodSelectorPresented = false

@State private var presentedFullCoin: FullCoin?

@State private var editMode: EditMode = .inactive

var body: some View {
ThemeView {
switch viewModel.state {
Expand All @@ -36,7 +37,7 @@ struct MarketWatchlistView: View {
}
}
.sheet(item: $presentedFullCoin) { fullCoin in
CoinPageViewNew(coinUid: fullCoin.coin.uid)
CoinPageViewNew(coinUid: fullCoin.coin.uid).ignoresSafeArea()
}
}

Expand All @@ -51,6 +52,20 @@ struct MarketWatchlistView: View {
.buttonStyle(SecondaryButtonStyle(style: .default, rightAccessory: .dropDown))
.disabled(disabled)

if viewModel.sortBy == .manual {
Button(action: {
if editMode == .active {
editMode = .inactive
} else {
editMode = .active
}
}) {
Image("edit2_20").renderingMode(.template)
}
.buttonStyle(SecondaryCircleButtonStyle(style: .default, isActive: editMode == .active))
.disabled(disabled)
}

Button(action: {
timePeriodSelectorPresented = true
}) {
Expand Down Expand Up @@ -107,7 +122,12 @@ struct MarketWatchlistView: View {
}

@ViewBuilder private func list(marketInfos: [MarketInfo], signals: [String: TechnicalAdvice.Advice]) -> some View {
ThemeList(items: marketInfos) { marketInfo in
ThemeList(
items: marketInfos,
onMove: viewModel.sortBy == .manual ? { source, destination in
viewModel.move(source: source, destination: destination)
} : nil
) { marketInfo in
let coin = marketInfo.fullCoin.coin

ClickableRow(action: {
Expand All @@ -133,9 +153,14 @@ struct MarketWatchlistView: View {
}
}
.themeListStyle(.transparent)
.environment(\.editMode, $editMode)
.refreshable {
await viewModel.refresh()
}
.animation(.default, value: editMode)
.onChange(of: viewModel.sortBy) { _ in
editMode = .inactive
}
}

@ViewBuilder private func loadingList() -> some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ class MarketWatchlistViewModel: ObservableObject {
}

private func syncCoinUids() {
coinUids = watchlistManager.coinUids
let coinUids = watchlistManager.coinUids

guard coinUids != self.coinUids else {
return
}

self.coinUids = coinUids

if case let .loaded(marketInfos, signals) = internalState {
let newMarketInfos = marketInfos.filter { marketInfo in
Expand Down Expand Up @@ -144,6 +150,23 @@ extension MarketWatchlistViewModel {
func remove(coinUid: String) {
watchlistManager.remove(coinUid: coinUid)
}

func move(source: IndexSet, destination: Int) {
guard case let .loaded(marketInfos, signals) = internalState else {
return
}

var newCoinUids = coinUids
var newMarketInfos = marketInfos

newCoinUids.move(fromOffsets: source, toOffset: destination)
newMarketInfos.move(fromOffsets: source, toOffset: destination)

coinUids = newCoinUids
internalState = .loaded(marketInfos: newMarketInfos, signals: signals)

watchlistManager.set(coinUids: coinUids)
}
}

extension MarketWatchlistViewModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension BitcoinSendHandler: ISendHandler {
let satoshiPerByte = transactionSettings?.satoshiPerByte
var feeData: BitcoinFeeData?
var transactionError: Error?
var params = params.copy()
let params = params.copy()

if let satoshiPerByte {
params.feeRate = satoshiPerByte
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import SwiftUI

struct SecondaryCircleButtonStyle: ButtonStyle {
let style: Style
private let style: Style
private let isActive: Bool

@Environment(\.isEnabled) private var isEnabled

init(style: Style = .default, isActive: Bool = false) {
self.style = style
self.isActive = isActive
}

func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.margin4)
.foregroundColor(style.foregroundColor(isEnabled: isEnabled, isPressed: configuration.isPressed))
.background(style.backgroundColor(isEnabled: isEnabled, isPressed: configuration.isPressed))
.foregroundColor(style.foregroundColor(isEnabled: isEnabled, isActive: isActive, isPressed: configuration.isPressed))
.background(style.backgroundColor(isEnabled: isEnabled, isActive: isActive, isPressed: configuration.isPressed))
.clipShape(Circle())
.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
}
Expand All @@ -19,17 +25,18 @@ struct SecondaryCircleButtonStyle: ButtonStyle {
case transparent
case red

func foregroundColor(isEnabled: Bool, isPressed: Bool) -> Color {
func foregroundColor(isEnabled: Bool, isActive: Bool, isPressed: Bool) -> Color {
switch self {
case .default: return isEnabled ? (isPressed ? .themeGray : .themeLeah) : .themeGray50
case .default: return isEnabled ? (isActive ? .themeDark : (isPressed ? .themeGray : .themeLeah)) : .themeGray50
case .transparent: return isEnabled ? (isPressed ? .themeGray50 : .themeGray) : .themeGray50
case .red: return isEnabled ? (isPressed ? .themeRed50 : .themeLucian) : .themeGray50
}
}

func backgroundColor(isEnabled _: Bool, isPressed: Bool) -> Color {
func backgroundColor(isEnabled _: Bool, isActive: Bool, isPressed: Bool) -> Color {
switch self {
case .default, .red: return isPressed ? .themeSteel10 : .themeSteel20
case .default: return isActive ? (isPressed ? .themeYellow50 : .themeYellow) : (isPressed ? .themeSteel10 : .themeSteel20)
case .red: return isPressed ? .themeSteel10 : .themeSteel20
case .transparent: return .clear
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import SwiftUI

struct ThemeList<Content: View, Item: Hashable>: View {
let items: [Item]
@ViewBuilder let itemContent: (Item) -> Content
private let items: [Item]
private let onMove: ((IndexSet, Int) -> Void)?
private let itemContent: (Item) -> Content

@Environment(\.themeListStyle) var themeListStyle

init(items: [Item], onMove: ((IndexSet, Int) -> Void)? = nil, @ViewBuilder itemContent: @escaping (Item) -> Content) {
self.items = items
self.onMove = onMove
self.itemContent = itemContent
}

var body: some View {
switch themeListStyle {
case .lawrence, .bordered, .transparentInline, .borderedLawrence:
Expand All @@ -26,6 +33,7 @@ struct ThemeList<Content: View, Item: Hashable>: View {
.listRowInsets(EdgeInsets())
.listRowSeparator(.hidden)
}
.onMove(perform: onMove)

Spacer()
.frame(height: .margin16)
Expand Down

0 comments on commit a93b275

Please sign in to comment.