Start adding tab view, improved Pinned header
This commit is contained in:
@ -197,21 +197,21 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
.formSheet(isPresented: $viewModel.snoozePresented) {
|
||||
SnoozeView(
|
||||
snoozePresented: $viewModel.snoozePresented,
|
||||
itemToSnoozeID: $viewModel.itemToSnoozeID
|
||||
) { snoozeParams in
|
||||
Task {
|
||||
await viewModel.snoozeUntil(
|
||||
dataService: dataService,
|
||||
linkId: snoozeParams.feedItemId,
|
||||
until: snoozeParams.snoozeUntilDate,
|
||||
successMessage: snoozeParams.successMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// .formSheet(isPresented: $viewModel.snoozePresented) {
|
||||
// SnoozeView(
|
||||
// snoozePresented: $viewModel.snoozePresented,
|
||||
// itemToSnoozeID: $viewModel.itemToSnoozeID
|
||||
// ) { snoozeParams in
|
||||
// Task {
|
||||
// await viewModel.snoozeUntil(
|
||||
// dataService: dataService,
|
||||
// linkId: snoozeParams.feedItemId,
|
||||
// until: snoozeParams.snoozeUntilDate,
|
||||
// successMessage: snoozeParams.successMessage
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
.fullScreenCover(isPresented: $searchPresented) {
|
||||
LibrarySearchView(homeFeedViewModel: self.viewModel)
|
||||
}
|
||||
@ -352,14 +352,6 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
itemToRemove = item
|
||||
confirmationShown = true
|
||||
}
|
||||
if FeatureFlag.enableSnooze {
|
||||
Button {
|
||||
viewModel.itemToSnoozeID = item.id
|
||||
viewModel.snoozePresented = true
|
||||
} label: {
|
||||
Label { Text(LocalText.genericSnooze) } icon: { Image.moon }
|
||||
}
|
||||
}
|
||||
if let author = item.author {
|
||||
Button(
|
||||
action: {
|
||||
@ -374,60 +366,64 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
}
|
||||
|
||||
var featureCard: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Menu(content: {
|
||||
Button(action: {
|
||||
viewModel.updateFeatureFilter(.continueReading)
|
||||
VStack {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Menu(content: {
|
||||
Button(action: {
|
||||
viewModel.updateFeatureFilter(.continueReading)
|
||||
}, label: {
|
||||
Text("Continue Reading")
|
||||
})
|
||||
Button(action: {
|
||||
viewModel.updateFeatureFilter(.pinned)
|
||||
}, label: {
|
||||
Text("Pinned")
|
||||
})
|
||||
Button(action: {
|
||||
viewModel.updateFeatureFilter(.newsletters)
|
||||
}, label: {
|
||||
Text("Newsletters")
|
||||
})
|
||||
Button(action: {
|
||||
showHideFeatureAlert = true
|
||||
}, label: {
|
||||
Text("Hide this Section")
|
||||
})
|
||||
}, label: {
|
||||
Text("Continue Reading")
|
||||
HStack(alignment: .center) {
|
||||
Text((FeaturedItemFilter(rawValue: viewModel.featureFilter) ?? .continueReading).title)
|
||||
.font(Font.system(size: 13, weight: .regular))
|
||||
Image(systemName: "chevron.down")
|
||||
.font(Font.system(size: 13, weight: .regular))
|
||||
}.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.tint(Color(hex: "#007AFF"))
|
||||
})
|
||||
Button(action: {
|
||||
viewModel.updateFeatureFilter(.pinned)
|
||||
}, label: {
|
||||
Text("Pinned")
|
||||
})
|
||||
Button(action: {
|
||||
viewModel.updateFeatureFilter(.newsletters)
|
||||
}, label: {
|
||||
Text("Newsletters")
|
||||
})
|
||||
Button(action: {
|
||||
showHideFeatureAlert = true
|
||||
}, label: {
|
||||
Text("Hide this Section")
|
||||
})
|
||||
}, label: {
|
||||
HStack(alignment: .center) {
|
||||
Text((FeaturedItemFilter(rawValue: viewModel.featureFilter) ?? .continueReading).title.uppercased())
|
||||
.font(Font.system(size: 14, weight: .regular))
|
||||
Image(systemName: "chevron.down")
|
||||
}.frame(maxWidth: .infinity, alignment: .leading)
|
||||
})
|
||||
.padding(.top, 20)
|
||||
.padding(.bottom, 0)
|
||||
.padding(.top, 15)
|
||||
|
||||
GeometryReader { geo in
|
||||
GeometryReader { geo in
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
if viewModel.featureItems.count > 0 {
|
||||
LazyHStack(alignment: .top, spacing: 10) {
|
||||
ForEach(viewModel.featureItems) { item in
|
||||
LibraryFeatureCardNavigationLink(item: item, viewModel: viewModel)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
if viewModel.featureItems.count > 0 {
|
||||
LazyHStack(alignment: .top, spacing: 10) {
|
||||
ForEach(viewModel.featureItems) { item in
|
||||
LibraryFeatureCardNavigationLink(item: item, viewModel: viewModel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text((FeaturedItemFilter(rawValue: viewModel.featureFilter) ?? .continueReading).emptyMessage)
|
||||
.font(Font.system(size: 14, weight: .regular))
|
||||
.foregroundColor(Color(hex: "#898989"))
|
||||
.frame(maxWidth: geo.size.width)
|
||||
.frame(height: 60, alignment: .topLeading)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
} else {
|
||||
Text((FeaturedItemFilter(rawValue: viewModel.featureFilter) ?? .continueReading).emptyMessage)
|
||||
.font(Font.system(size: 14, weight: .regular))
|
||||
.foregroundColor(Color(hex: "#898989"))
|
||||
.frame(maxWidth: geo.size.width)
|
||||
.frame(height: 60, alignment: .topLeading)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
Text((LinkedItemFilter(rawValue: viewModel.appliedFilter)?.displayName ?? "Inbox").uppercased())
|
||||
.font(Font.system(size: 14, weight: .regular))
|
||||
Color.thFeatureSeparator
|
||||
.frame(maxWidth: .infinity, maxHeight: 10)
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,8 +448,8 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
|
||||
if !viewModel.hideFeatureSection, viewModel.items.count > 0, viewModel.searchTerm.isEmpty, viewModel.selectedLabels.isEmpty, viewModel.negatedLabels.isEmpty {
|
||||
featureCard
|
||||
.listRowInsets(.init(top: 0, leading: 10, bottom: 10, trailing: 10))
|
||||
.modifier(AnimatingCellHeight(height: viewModel.featureItems.count > 0 ? 260 : 130))
|
||||
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
.modifier(AnimatingCellHeight(height: viewModel.featureItems.count > 0 ? 200 : 130))
|
||||
}
|
||||
|
||||
ForEach(viewModel.items) { item in
|
||||
@ -494,16 +490,16 @@ struct AnimatingCellHeight: AnimatableModifier {
|
||||
}
|
||||
).tint(.red)
|
||||
}
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
if FeatureFlag.enableSnooze {
|
||||
Button {
|
||||
viewModel.itemToSnoozeID = item.id
|
||||
viewModel.snoozePresented = true
|
||||
} label: {
|
||||
Label { Text(LocalText.genericSnooze) } icon: { Image.moon }
|
||||
}.tint(.appYellow48)
|
||||
}
|
||||
}
|
||||
// .swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
// if FeatureFlag.enableSnooze {
|
||||
// Button {
|
||||
// viewModel.itemToSnoozeID = item.id
|
||||
// viewModel.snoozePresented = true
|
||||
// } label: {
|
||||
// Label { Text(LocalText.genericSnooze) } icon: { Image.moon }
|
||||
// }.tint(.appYellow48)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
.padding(0)
|
||||
|
||||
@ -1,8 +1,46 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Jackson Harper on 6/29/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct LibraryTabView: View {
|
||||
// @EnvironmentObject var authenticator: Authenticator
|
||||
// @EnvironmentObject var dataService: DataService
|
||||
// @Binding var selectedEnvironment: AppEnvironment
|
||||
|
||||
// let appEnvironments: [AppEnvironment] = [.local, .demo, .prod]
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
HomeView()
|
||||
.tabItem {
|
||||
Label {
|
||||
Text("Subscriptions")
|
||||
} icon: {
|
||||
Image.tabSubscriptions
|
||||
}
|
||||
}
|
||||
HomeView()
|
||||
.tabItem {
|
||||
Label {
|
||||
Text("Library")
|
||||
} icon: {
|
||||
Image.tabLibrary
|
||||
}
|
||||
}
|
||||
HomeView()
|
||||
.tabItem {
|
||||
Label {
|
||||
Text("Highlights")
|
||||
} icon: {
|
||||
Image.tabHighlights
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,8 @@ public struct PrimaryContentView: View {
|
||||
if UIDevice.isIPad {
|
||||
splitView
|
||||
} else {
|
||||
HomeView()
|
||||
// HomeView()
|
||||
LibraryTabView()
|
||||
}
|
||||
#elseif os(macOS)
|
||||
splitView
|
||||
|
||||
@ -102,8 +102,12 @@ public extension FeaturedItemFilter {
|
||||
switch self {
|
||||
case .continueReading:
|
||||
return "Continue Reading"
|
||||
default:
|
||||
return rawValue
|
||||
case .recommended:
|
||||
return "Recommended"
|
||||
case .newsletters:
|
||||
return "Newsletters"
|
||||
case .pinned:
|
||||
return "Pinned"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -42,6 +42,8 @@ public extension Color {
|
||||
static var themeSolidBackground: Color { Color("_themeSolidBackground", bundle: .module) }
|
||||
static var thBorderColor: Color { Color("thBorderColor", bundle: .module) }
|
||||
|
||||
static var thFeatureSeparator: Color { Color("featureSeparator", bundle: .module) }
|
||||
|
||||
// Apple system UIColor equivalents
|
||||
#if os(iOS)
|
||||
static var systemBackground: Color { Color(.systemBackground) }
|
||||
|
||||
@ -17,7 +17,6 @@ public struct LibraryFeatureCard: View {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
imageBox
|
||||
title
|
||||
readInfo
|
||||
Spacer()
|
||||
}
|
||||
.padding(0)
|
||||
@ -32,43 +31,6 @@ public struct LibraryFeatureCard: View {
|
||||
Int(item.readingProgress) > 0
|
||||
}
|
||||
|
||||
var readingSpeed: Int64 {
|
||||
var result = UserDefaults.standard.integer(forKey: UserDefaultKey.userWordsPerMinute.rawValue)
|
||||
if result <= 0 {
|
||||
result = 235
|
||||
}
|
||||
return Int64(result)
|
||||
}
|
||||
|
||||
var estimatedReadingTime: String {
|
||||
if item.wordsCount > 0 {
|
||||
let readLen = max(1, item.wordsCount / readingSpeed)
|
||||
return "\(readLen) MIN READ • "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var readingProgress: String {
|
||||
// If there is no wordsCount don't show progress because it will make no sense
|
||||
if item.wordsCount > 0 {
|
||||
return "\(String(format: "%d", Int(item.readingProgress)))%"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var readInfo: some View {
|
||||
AnyView(HStack {
|
||||
Text("\(estimatedReadingTime)")
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.foregroundColor(Color.themeMediumGray)
|
||||
+
|
||||
Text("\(readingProgress)")
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.foregroundColor(isPartiallyRead ? Color.appGreenSuccess : Color.themeMediumGray)
|
||||
}
|
||||
.frame(maxWidth: 150, alignment: .leading))
|
||||
}
|
||||
|
||||
var imageBox: some View {
|
||||
Group {
|
||||
if let imageURL = item.imageURL {
|
||||
@ -76,7 +38,7 @@ public struct LibraryFeatureCard: View {
|
||||
switch phase {
|
||||
case .empty:
|
||||
Color.systemBackground
|
||||
.frame(width: 146, height: 90)
|
||||
.frame(width: 145, height: 90)
|
||||
.cornerRadius(5)
|
||||
case let .success(image):
|
||||
image.resizable()
|
||||
@ -110,7 +72,7 @@ public struct LibraryFeatureCard: View {
|
||||
var title: some View {
|
||||
Text(item.unwrappedTitle.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||
.multilineTextAlignment(.leading)
|
||||
.font(Font.system(size: 13, weight: .semibold))
|
||||
.font(Font.system(size: 11, weight: .medium))
|
||||
.lineSpacing(1.25)
|
||||
.foregroundColor(.appGrayTextContrast)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
@ -4,14 +4,14 @@ public extension Image {
|
||||
static var smallOmnivoreLogo: Image { Image("_smallOmnivoreLogo", bundle: .module) }
|
||||
static var omnivoreTitleLogo: Image { Image("_omnivoreTitleLogo", bundle: .module) }
|
||||
static var googleIcon: Image { Image("_googleIcon", bundle: .module) }
|
||||
static var sunHorizon: Image { Image("_sun-horizon", bundle: .module) }
|
||||
static var mountains: Image { Image("_mountains", bundle: .module) }
|
||||
static var moon: Image { Image("_moon", bundle: .module) }
|
||||
static var moonStars: Image { Image("_moon-stars", bundle: .module) }
|
||||
static var chartLineUp: Image { Image("_chart-line-up", bundle: .module) }
|
||||
static var homeTab: Image { Image("_homeTab", bundle: .module) }
|
||||
|
||||
static var homeTab: Image { Image("BookmarksSimple", bundle: .module) }
|
||||
static var homeTabSelected: Image { Image("_homeTabSelected", bundle: .module) }
|
||||
static var profileTab: Image { Image("_profileTab", bundle: .module) }
|
||||
static var profileTabSelected: Image { Image("_profileTabSelected", bundle: .module) }
|
||||
static var dotsThree: Image { Image("_dots-three", bundle: .module) }
|
||||
|
||||
static var tabSubscriptions: Image { Image("_tab_subscriptions", bundle: .module).renderingMode(.template) }
|
||||
static var tabLibrary: Image { Image("_tab_library", bundle: .module).renderingMode(.template) }
|
||||
static var tabHighlights: Image { Image("_tab_highlights", bundle: .module).renderingMode(.template) }
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 482 B |
@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "BookOpen.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 707 B |
@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "BookmarksSimple.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "HighlighterCircle.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,15 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "sun-horizon.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 92.8125 59 L 85.09375 40.5 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 43 108.8125 L 24.5 101.09375 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 213 108.8125 L 231.5 101.09375 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 163.1875 59 L 170.90625 40.5 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 240 160 L 16 160 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 208 200 L 48 200 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
<path style="fill:none;stroke-width:16;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 70.1875 160 C 63.40625 135.5625 72.6875 109.46875 93.4375 94.875 C 114.15625 80.25 141.84375 80.25 162.5625 94.875 C 183.3125 109.46875 192.59375 135.5625 185.8125 160 " transform="matrix(0.125,0,0,0.125,0,0)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
File diff suppressed because one or more lines are too long
@ -1,151 +1,151 @@
|
||||
import Models
|
||||
import SwiftUI
|
||||
|
||||
public struct SnoozeView: View {
|
||||
@Binding var snoozePresented: Bool
|
||||
@Binding var itemToSnoozeID: String?
|
||||
let snoozeAction: (SnoozeActionParams) -> Void
|
||||
// public struct SnoozeView: View {
|
||||
// @Binding var snoozePresented: Bool
|
||||
// @Binding var itemToSnoozeID: String?
|
||||
// let snoozeAction: (SnoozeActionParams) -> Void
|
||||
//
|
||||
// public init(
|
||||
// snoozePresented: Binding<Bool>,
|
||||
// itemToSnoozeID: Binding<String?>,
|
||||
// snoozeAction: @escaping (SnoozeActionParams) -> Void
|
||||
// ) {
|
||||
// self._snoozePresented = snoozePresented
|
||||
// self._itemToSnoozeID = itemToSnoozeID
|
||||
// self.snoozeAction = snoozeAction
|
||||
// }
|
||||
//
|
||||
// public var body: some View {
|
||||
// VStack {
|
||||
// Spacer()
|
||||
//
|
||||
// HStack {
|
||||
// SnoozeIconButtonView(snooze: Snooze.currentValues[0], action: { snoozeItem($0) })
|
||||
// SnoozeIconButtonView(snooze: Snooze.currentValues[1], action: { snoozeItem($0) })
|
||||
// }
|
||||
//
|
||||
// Spacer(minLength: 32)
|
||||
//
|
||||
// HStack {
|
||||
// SnoozeIconButtonView(snooze: Snooze.currentValues[2], action: { snoozeItem($0) })
|
||||
// SnoozeIconButtonView(snooze: Snooze.currentValues[3], action: { snoozeItem($0) })
|
||||
// }
|
||||
// Spacer()
|
||||
// }.padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
|
||||
// }
|
||||
//
|
||||
// private func snoozeItem(_ snooze: Snooze) {
|
||||
// if let itemID = itemToSnoozeID {
|
||||
// withAnimation(.linear(duration: 0.4)) {
|
||||
// snoozeAction(
|
||||
// SnoozeActionParams(
|
||||
// feedItemId: itemID,
|
||||
// snoozeUntilDate: snooze.until,
|
||||
// successMessage: "Snoozed until \(snooze.untilStr)"
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// itemToSnoozeID = nil
|
||||
// snoozePresented = false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public struct SnoozeActionParams {
|
||||
// public let feedItemId: String
|
||||
// public let snoozeUntilDate: Date
|
||||
// public let successMessage: String?
|
||||
// }
|
||||
//
|
||||
// private struct SnoozeIconButtonView: View {
|
||||
// let snooze: Snooze
|
||||
// let action: (_ snooze: Snooze) -> Void
|
||||
//
|
||||
// var body: some View {
|
||||
// Button(
|
||||
// action: { action(snooze) },
|
||||
// label: {
|
||||
// VStack(alignment: .center, spacing: 8) {
|
||||
// snooze.icon
|
||||
// .font(.appTitle)
|
||||
// .foregroundColor(.appYellow48)
|
||||
// Text(snooze.title)
|
||||
// .font(.appBody)
|
||||
// .foregroundColor(.appGrayText)
|
||||
// Text(snooze.untilStr)
|
||||
// .font(.appCaption)
|
||||
// .foregroundColor(.appGrayText)
|
||||
// }
|
||||
// .frame(
|
||||
// maxWidth: .infinity,
|
||||
// maxHeight: .infinity
|
||||
// )
|
||||
// }
|
||||
// )
|
||||
// .frame(height: 100)
|
||||
// }
|
||||
// }
|
||||
|
||||
public init(
|
||||
snoozePresented: Binding<Bool>,
|
||||
itemToSnoozeID: Binding<String?>,
|
||||
snoozeAction: @escaping (SnoozeActionParams) -> Void
|
||||
) {
|
||||
self._snoozePresented = snoozePresented
|
||||
self._itemToSnoozeID = itemToSnoozeID
|
||||
self.snoozeAction = snoozeAction
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
SnoozeIconButtonView(snooze: Snooze.currentValues[0], action: { snoozeItem($0) })
|
||||
SnoozeIconButtonView(snooze: Snooze.currentValues[1], action: { snoozeItem($0) })
|
||||
}
|
||||
|
||||
Spacer(minLength: 32)
|
||||
|
||||
HStack {
|
||||
SnoozeIconButtonView(snooze: Snooze.currentValues[2], action: { snoozeItem($0) })
|
||||
SnoozeIconButtonView(snooze: Snooze.currentValues[3], action: { snoozeItem($0) })
|
||||
}
|
||||
Spacer()
|
||||
}.padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
|
||||
}
|
||||
|
||||
private func snoozeItem(_ snooze: Snooze) {
|
||||
if let itemID = itemToSnoozeID {
|
||||
withAnimation(.linear(duration: 0.4)) {
|
||||
snoozeAction(
|
||||
SnoozeActionParams(
|
||||
feedItemId: itemID,
|
||||
snoozeUntilDate: snooze.until,
|
||||
successMessage: "Snoozed until \(snooze.untilStr)"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
itemToSnoozeID = nil
|
||||
snoozePresented = false
|
||||
}
|
||||
}
|
||||
|
||||
public struct SnoozeActionParams {
|
||||
public let feedItemId: String
|
||||
public let snoozeUntilDate: Date
|
||||
public let successMessage: String?
|
||||
}
|
||||
|
||||
private struct SnoozeIconButtonView: View {
|
||||
let snooze: Snooze
|
||||
let action: (_ snooze: Snooze) -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(
|
||||
action: { action(snooze) },
|
||||
label: {
|
||||
VStack(alignment: .center, spacing: 8) {
|
||||
snooze.icon
|
||||
.font(.appTitle)
|
||||
.foregroundColor(.appYellow48)
|
||||
Text(snooze.title)
|
||||
.font(.appBody)
|
||||
.foregroundColor(.appGrayText)
|
||||
Text(snooze.untilStr)
|
||||
.font(.appCaption)
|
||||
.foregroundColor(.appGrayText)
|
||||
}
|
||||
.frame(
|
||||
maxWidth: .infinity,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
}
|
||||
)
|
||||
.frame(height: 100)
|
||||
}
|
||||
}
|
||||
|
||||
struct Snooze {
|
||||
let until: Date
|
||||
let icon: Image
|
||||
let title: String
|
||||
let untilStr: String
|
||||
|
||||
init(until: Date, icon: Image, title: String, needsDay: Bool) {
|
||||
self.until = until
|
||||
self.icon = icon
|
||||
self.title = title
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = needsDay ? "EEE h:mm a" : "h:mm a"
|
||||
self.untilStr = formatter.string(from: until)
|
||||
}
|
||||
|
||||
static var currentValues: [Snooze] {
|
||||
calculateValues(for: Date(), calendar: Calendar.current)
|
||||
}
|
||||
|
||||
static func calculateValues(for now: Date, calendar: Calendar) -> [Snooze] {
|
||||
var res: [Snooze] = []
|
||||
let components = calendar.dateComponents([.year, .month, .day, .hour, .timeZone, .weekday], from: now)
|
||||
|
||||
var tonightComponent = components
|
||||
tonightComponent.hour = 20
|
||||
|
||||
var thisMorningComponent = components
|
||||
thisMorningComponent.hour = 8
|
||||
|
||||
let tonight = calendar.date(from: tonightComponent)!
|
||||
let thisMorning = calendar.date(from: thisMorningComponent)!
|
||||
|
||||
let tomorrowMorning = Calendar.current.date(byAdding: DateComponents(day: 1), to: thisMorning)
|
||||
|
||||
// Add either tonight or tomorrow night
|
||||
if now < tonight {
|
||||
res.append(Snooze(until: tonight, icon: .moonStars, title: "Tonight", needsDay: false))
|
||||
} else {
|
||||
let tomorrowNight = Calendar.current.date(byAdding: DateComponents(day: 1), to: tonight)!
|
||||
res.append(Snooze(until: tomorrowNight, icon: .moonStars, title: "Tomorrow night", needsDay: false))
|
||||
}
|
||||
|
||||
if let tomorrowMorning = tomorrowMorning {
|
||||
res.append(Snooze(until: tomorrowMorning, icon: .sunHorizon, title: "Tomorrow morning", needsDay: false))
|
||||
}
|
||||
|
||||
if let weekday = components.weekday {
|
||||
// Add this or next weekend
|
||||
if weekday < 5 {
|
||||
let thisWeekend = Calendar.current.date(byAdding: DateComponents(day: 7 - weekday), to: thisMorning)
|
||||
res.append(Snooze(until: thisWeekend!, icon: .mountains, title: "This weekend", needsDay: true))
|
||||
} else {
|
||||
let nextWeekend = Calendar.current.date(byAdding: DateComponents(day: 7 - (weekday - 5)), to: thisMorning)!
|
||||
res.append(Snooze(until: nextWeekend, icon: .mountains, title: "Next weekend", needsDay: true))
|
||||
}
|
||||
let nextWeek = Calendar.current.date(byAdding: DateComponents(day: weekday + 5), to: thisMorning)!
|
||||
res.append(Snooze(until: nextWeek, icon: .chartLineUp, title: "Next week", needsDay: true))
|
||||
}
|
||||
|
||||
return Array(res.sorted(by: { $0.until > $1.until }).reversed())
|
||||
}
|
||||
}
|
||||
// struct Snooze {
|
||||
// let until: Date
|
||||
// let icon: Image
|
||||
// let title: String
|
||||
// let untilStr: String
|
||||
//
|
||||
// init(until: Date, icon: Image, title: String, needsDay: Bool) {
|
||||
// self.until = until
|
||||
// self.icon = icon
|
||||
// self.title = title
|
||||
// let formatter = DateFormatter()
|
||||
// formatter.dateFormat = needsDay ? "EEE h:mm a" : "h:mm a"
|
||||
// self.untilStr = formatter.string(from: until)
|
||||
// }
|
||||
//
|
||||
// static var currentValues: [Snooze] {
|
||||
// calculateValues(for: Date(), calendar: Calendar.current)
|
||||
// }
|
||||
//
|
||||
// static func calculateValues(for now: Date, calendar: Calendar) -> [Snooze] {
|
||||
// var res: [Snooze] = []
|
||||
// let components = calendar.dateComponents([.year, .month, .day, .hour, .timeZone, .weekday], from: now)
|
||||
//
|
||||
// var tonightComponent = components
|
||||
// tonightComponent.hour = 20
|
||||
//
|
||||
// var thisMorningComponent = components
|
||||
// thisMorningComponent.hour = 8
|
||||
//
|
||||
// let tonight = calendar.date(from: tonightComponent)!
|
||||
// let thisMorning = calendar.date(from: thisMorningComponent)!
|
||||
//
|
||||
// let tomorrowMorning = Calendar.current.date(byAdding: DateComponents(day: 1), to: thisMorning)
|
||||
//
|
||||
// // Add either tonight or tomorrow night
|
||||
// if now < tonight {
|
||||
// res.append(Snooze(until: tonight, icon: .moonStars, title: "Tonight", needsDay: false))
|
||||
// } else {
|
||||
// let tomorrowNight = Calendar.current.date(byAdding: DateComponents(day: 1), to: tonight)!
|
||||
// res.append(Snooze(until: tomorrowNight, icon: .moonStars, title: "Tomorrow night", needsDay: false))
|
||||
// }
|
||||
//
|
||||
// if let tomorrowMorning = tomorrowMorning {
|
||||
// res.append(Snooze(until: tomorrowMorning, icon: .sunHorizon, title: "Tomorrow morning", needsDay: false))
|
||||
// }
|
||||
//
|
||||
// if let weekday = components.weekday {
|
||||
// // Add this or next weekend
|
||||
// if weekday < 5 {
|
||||
// let thisWeekend = Calendar.current.date(byAdding: DateComponents(day: 7 - weekday), to: thisMorning)
|
||||
// res.append(Snooze(until: thisWeekend!, icon: .mountains, title: "This weekend", needsDay: true))
|
||||
// } else {
|
||||
// let nextWeekend = Calendar.current.date(byAdding: DateComponents(day: 7 - (weekday - 5)), to: thisMorning)!
|
||||
// res.append(Snooze(until: nextWeekend, icon: .mountains, title: "Next weekend", needsDay: true))
|
||||
// }
|
||||
// let nextWeek = Calendar.current.date(byAdding: DateComponents(day: weekday + 5), to: thisMorning)!
|
||||
// res.append(Snooze(until: nextWeek, icon: .chartLineUp, title: "Next week", needsDay: true))
|
||||
// }
|
||||
//
|
||||
// return Array(res.sorted(by: { $0.until > $1.until }).reversed())
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user