Start adding tab view, improved Pinned header

This commit is contained in:
Jackson Harper
2023-06-30 21:09:24 -07:00
parent 4cb05acfe0
commit 32d583e05c
17 changed files with 284 additions and 367 deletions

View File

@ -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)

View File

@ -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
}
}
}
}
}

View File

@ -14,7 +14,8 @@ public struct PrimaryContentView: View {
if UIDevice.isIPad {
splitView
} else {
HomeView()
// HomeView()
LibraryTabView()
}
#elseif os(macOS)
splitView

View File

@ -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"
}
}

View File

@ -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) }

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -1,15 +0,0 @@
{
"images" : [
{
"filename" : "sun-horizon.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -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

View File

@ -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())
// }
// }