155 lines
5.8 KiB
Swift
155 lines
5.8 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: {
|
|
if #available(iOS 16.0, *) {
|
|
AddHashtagPinnedTimelineView(pinnedTimelines: $pinnedTimelines)
|
|
.edgesIgnoringSafeArea(.bottom)
|
|
} else {
|
|
AddHashtagPinnedTimelineRepresentable(pinnedTimelines: $pinnedTimelines)
|
|
.edgesIgnoringSafeArea(.bottom)
|
|
}
|
|
})
|
|
.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!)"
|
|
}
|
|
}
|
|
}
|