Tusker/Tusker/Screens/Customize Timelines/PinnedTimelinesView.swift

149 lines
5.5 KiB
Swift

//
// PinnedTimelinesView.swift
// Tusker
//
// Created by Shadowfacts on 12/20/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
import SwiftUI
import Pachyderm
struct PinnedTimelinesView: View {
@EnvironmentObject private var mastodonController: MastodonController
@ObservedObject private var accountPreferences: AccountPreferences
@State private var isShowingAddHashtagSheet = false
@State private var isShowingAddInstanceSheet = false
// store this separately from AccountPreferences in the view, b/c the @LazilyDecoding wrapper breaks animations
@State private var pinnedTimelines: [PinnedTimeline]
init(accountPreferences: AccountPreferences) {
self.accountPreferences = accountPreferences
self.pinnedTimelines = accountPreferences.pinnedTimelines
}
var body: some View {
Section {
ForEach(pinnedTimelines, id: \.id) { timeline in
HStack {
Label {
if case .list(id: let id) = timeline,
let list = mastodonController.lists.first(where: { $0.id == id }) {
Text(list.title)
} else if case .tag(hashtag: let tag) = timeline {
Text(tag)
} else {
Text(timeline.title)
}
} icon: {
Image(uiImage: timeline.image.withRenderingMode(.alwaysTemplate))
}
Spacer()
Image(systemName: "line.3.horizontal")
.foregroundColor(Color(.lightGray))
.accessibilityHidden(true)
}
.contextMenu {
Button(role: .destructive) {
pinnedTimelines.removeAll(where: { $0.id == timeline.id })
} label: {
Label("Remove Pinned Timeline", systemImage: "trash")
}
}
}
.onMove { indices, newOffset in
pinnedTimelines.move(fromOffsets: indices, toOffset: newOffset)
}
.onDelete(perform: pinnedTimelines.count == 1 ? nil : { indices in
pinnedTimelines.remove(atOffsets: indices)
})
Menu {
ForEach([PinnedTimeline.home, .public(local: true), .public(local: false)], id: \.id) { timeline in
Button {
withAnimation {
pinnedTimelines.append(timeline)
}
} label: {
Label {
Text(timeline.title)
} icon: {
Image(uiImage: timeline.image)
}
}
.disabled(pinnedTimelines.contains(timeline))
}
Menu("List…") {
ForEach(mastodonController.lists, id: \.id) { list in
Button {
withAnimation {
pinnedTimelines.append(.list(id: list.id))
}
} label: {
Text(list.title)
}
.disabled(pinnedTimelines.contains(.list(id: list.id)))
}
}
Button {
isShowingAddHashtagSheet = true
} label: {
Label("Hashtag…", systemImage: "number")
}
Button {
isShowingAddInstanceSheet = true
} label: {
Label("Instance…", systemImage: "globe")
}
} label: {
Label("Add…", systemImage: "plus")
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
}
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
} header: {
Text("Pinned Timelines")
}
.sheet(isPresented: $isShowingAddHashtagSheet, content: {
AddHashtagPinnedTimelineView(pinnedTimelines: $pinnedTimelines)
})
.sheet(isPresented: $isShowingAddInstanceSheet, content: {
AddInstancePinnedTimelineView(pinnedTimelines: $pinnedTimelines)
.edgesIgnoringSafeArea(.bottom)
})
.onReceive(accountPreferences.publisher(for: \.pinnedTimelinesData)) { _ in
if pinnedTimelines != accountPreferences.pinnedTimelines {
pinnedTimelines = accountPreferences.pinnedTimelines
}
}
.onChange(of: pinnedTimelines) { newValue in
if accountPreferences.pinnedTimelines != newValue {
accountPreferences.pinnedTimelines = newValue
}
}
}
}
fileprivate extension PinnedTimeline {
var id: String {
switch self {
case .home:
return "home"
case .public(local: let local):
return "public:\(local)"
case .list(id: let id):
return "list:\(id)"
case .tag(hashtag: let tag):
return "tag:\(tag)"
case .instance(let url):
return "instance:\(url.host!)"
}
}
}