@@ -23,7 +23,7 @@ extension WidgetLibrary {
23
23
public struct Buttons {
24
24
public enum UIButtonType {
25
25
case resetUserChoices, createNote, createTask, createRecord, createPerson, createCompany, createProject,
26
- createJob, createTerm, createDefinition, historyPrevious, settings, CLIMode
26
+ createJob, createTerm, createDefinition, historyPrevious, settings, CLIMode, CLIFilter
27
27
}
28
28
29
29
struct ResetUserChoices : View {
@@ -223,6 +223,7 @@ extension WidgetLibrary {
223
223
public var onAction : ( ( ) -> Void ) ? = { }
224
224
@State private var isHighlighted : Bool = false
225
225
@State private var selectedPage : Page = . dashboard
226
+ private var isEmpty : Bool { self . state. history. recent. count == 0 }
226
227
227
228
var body : some View {
228
229
Button {
@@ -242,9 +243,10 @@ extension WidgetLibrary {
242
243
. clipShape ( . rect( cornerRadius: 5 ) )
243
244
. padding ( [ . top, . bottom] , 10 )
244
245
}
246
+ . disabled ( self . isEmpty)
245
247
. keyboardShortcut ( KeyEquivalent . leftArrow, modifiers: [ . command] )
246
248
. buttonStyle ( . plain)
247
- . useDefaultHover ( { hover in self . isHighlighted = hover} )
249
+ . useDefaultHover ( { hover in ! self . isEmpty ? self . isHighlighted = hover : nil } )
248
250
}
249
251
}
250
252
@@ -272,25 +274,49 @@ extension WidgetLibrary {
272
274
273
275
struct CLIMode : View {
274
276
@EnvironmentObject public var state : Navigation
277
+ @AppStorage ( " general.experimental.cli " ) private var cliEnabled : Bool = false
275
278
@AppStorage ( " today.commandLineMode " ) private var commandLineMode : Bool = false
276
- @AppStorage ( " general.experimental.cli " ) private var allowCLIMode : Bool = false
277
279
public var onAction : ( ( ) -> Void ) ? = { }
278
280
@State private var isHighlighted : Bool = false
279
281
@State private var selectedPage : Page = . dashboard
280
282
281
283
var body : some View {
282
- FancyButtonv2 (
283
- text: " Command line mode " ,
284
- action: { commandLineMode. toggle ( ) ; self . onAction ? ( ) } ,
285
- icon: self . commandLineMode ? " apple.terminal.fill " : " apple.terminal " ,
286
- iconWhenHighlighted: self . commandLineMode ? " apple.terminal " : " apple.terminal.fill " ,
287
- showLabel: false ,
288
- size: . small,
289
- type: . clear,
290
- font: . title
291
- )
292
- . help ( " Enter CLI mode " )
293
- . frame ( width: 25 )
284
+ if self . cliEnabled {
285
+ FancyButtonv2 (
286
+ text: " Command line mode " ,
287
+ action: { self . commandLineMode. toggle ( ) ; self . onAction ? ( ) } ,
288
+ icon: self . commandLineMode ? " apple.terminal.fill " : " apple.terminal " ,
289
+ iconWhenHighlighted: self . commandLineMode ? " apple.terminal " : " apple.terminal.fill " ,
290
+ showLabel: false ,
291
+ size: . small,
292
+ type: . clear,
293
+ font: . title
294
+ )
295
+ . help ( self . commandLineMode ? " Exit CLI mode " : " Enter CLI mode " )
296
+ . frame ( width: 25 )
297
+ }
298
+ }
299
+ }
300
+
301
+ struct CLIFilter : View {
302
+ @EnvironmentObject public var state : Navigation
303
+ @AppStorage ( " general.experimental.cli " ) private var cliEnabled : Bool = false
304
+ @AppStorage ( " today.commandLineMode " ) private var commandLineMode : Bool = false
305
+ @AppStorage ( " today.cli.showFilter " ) private var showCLIFilter : Bool = false
306
+
307
+ var body : some View {
308
+ if self . cliEnabled && self . commandLineMode {
309
+ FancyButtonv2 (
310
+ text: " Filter " ,
311
+ action: { self . showCLIFilter. toggle ( ) } ,
312
+ icon: " line.3.horizontal.decrease " ,
313
+ bgColour: self . state. session. appPage. primaryColour. opacity ( 0.2 ) ,
314
+ showLabel: false ,
315
+ size: . small,
316
+ type: . clear
317
+ )
318
+ . mask ( Circle ( ) )
319
+ }
294
320
}
295
321
}
296
322
}
@@ -393,48 +419,173 @@ extension WidgetLibrary {
393
419
@EnvironmentObject public var state : Navigation
394
420
395
421
var body : some View {
396
- if self . state. history. recent. count > 0 {
397
- ZStack {
398
- Theme . toolbarColour
399
- LinearGradient ( colors: [ Theme . base, . clear] , startPoint: . bottom, endPoint: . top)
400
- . opacity ( 0.6 )
401
- . blendMode ( . softLight)
402
-
403
- VStack ( alignment: . leading, spacing: 0 ) {
404
- HStack {
405
- Buttons . HistoryPrevious ( )
406
- Spacer ( )
407
- ForEach ( self . state. navButtons, id: \. self) { type in
408
- switch type {
409
- case . CLIMode: Buttons . CLIMode ( )
410
- case . historyPrevious: Buttons . HistoryPrevious ( )
411
- case . resetUserChoices: Buttons . ResetUserChoices ( )
412
- case . settings: Buttons . Settings ( )
413
- case . createJob: Buttons . CreateJob ( )
414
- case . createNote: Buttons . CreateNote ( )
422
+ ZStack {
423
+ Theme . toolbarColour
424
+ LinearGradient ( colors: [ Theme . base, . clear] , startPoint: . bottom, endPoint: . top)
425
+ . opacity ( 0.6 )
426
+ . blendMode ( . softLight)
427
+
428
+ VStack ( alignment: . leading, spacing: 0 ) {
429
+ HStack {
430
+ Buttons . HistoryPrevious ( )
431
+ Spacer ( )
432
+ SimpleDateSelector ( )
433
+ Spacer ( )
434
+ ForEach ( self . state. navButtons, id: \. self) { type in
435
+ switch type {
436
+ case . CLIMode: Buttons . CLIMode ( )
437
+ case . CLIFilter: Buttons . CLIFilter ( )
438
+ case . historyPrevious: Buttons . HistoryPrevious ( )
439
+ case . resetUserChoices: Buttons . ResetUserChoices ( )
440
+ case . settings: Buttons . Settings ( )
441
+ case . createJob: Buttons . CreateJob ( )
442
+ case . createNote: Buttons . CreateNote ( )
415
443
// case .createTask: Buttons.CreateTask()
416
- case . createTerm: Buttons . CreateTerm ( )
417
- case . createPerson: Buttons . CreatePerson ( )
418
- case . createRecord: Buttons . CreateRecord ( )
419
- case . createCompany: Buttons . CreateCompany ( )
420
- case . createProject: Buttons . CreateProject ( )
421
- case . createDefinition: Buttons . CreateDefinition ( )
422
- default : EmptyView ( )
444
+ case . createTerm: Buttons . CreateTerm ( )
445
+ case . createPerson: Buttons . CreatePerson ( )
446
+ case . createRecord: Buttons . CreateRecord ( )
447
+ case . createCompany: Buttons . CreateCompany ( )
448
+ case . createProject: Buttons . CreateProject ( )
449
+ case . createDefinition: Buttons . CreateDefinition ( )
450
+ default : EmptyView ( )
451
+ }
452
+ }
453
+ }
454
+ . padding ( [ . leading, . trailing] )
455
+ Divider ( )
456
+ }
457
+ }
458
+ . frame ( height: 55 )
459
+ }
460
+ }
461
+
462
+ struct SimpleDateSelector : View {
463
+ @EnvironmentObject private var state : Navigation
464
+ @AppStorage ( " today.numPastDates " ) public var numPastDates : Int = 20
465
+ @AppStorage ( " isDatePickerPresented " ) public var isDatePickerPresented : Bool = false
466
+ @State private var isToday : Bool = false
467
+ @State private var isHighlighted : Bool = false
468
+ @State private var date : String = " "
469
+ @State private var showDateOverlay : Bool = false
470
+ @State private var sDate : Date = Date ( )
471
+
472
+ var body : some View {
473
+ HStack ( alignment: . center) {
474
+ FancyButtonv2 (
475
+ text: " Previous day " ,
476
+ action: self . actionPreviousDay,
477
+ icon: " chevron.left " ,
478
+ fgColour: . gray,
479
+ highlightColour: . white,
480
+ showLabel: false ,
481
+ size: . titleLink,
482
+ type: . titleLink
483
+ )
484
+ . help ( " Previous day " )
485
+ . frame ( height: 20 )
486
+
487
+ Button {
488
+ self . showDateOverlay. toggle ( )
489
+ } label: {
490
+ HStack ( alignment: . center) {
491
+ ZStack {
492
+ RoundedRectangle ( cornerRadius: 5 )
493
+ . strokeBorder ( self . isToday ? . yellow. opacity ( 0.6 ) : . gray, lineWidth: 1 )
494
+ . fill ( . white. opacity ( self . isHighlighted ? 0.2 : 0.1 ) )
495
+ if !self . showDateOverlay {
496
+ HStack {
497
+ Image ( systemName: " calendar " )
498
+ . foregroundStyle ( . gray)
499
+ Text ( self . date)
423
500
}
424
501
}
425
- // Buttons.Settings()
426
- // Buttons.CLIMode()
427
- // Buttons.ResetUserChoices(onActionClear: {})
428
502
}
429
- . padding ( [ . leading, . trailing] )
430
- Divider ( )
503
+ . frame ( width: 200 )
504
+ }
505
+ }
506
+ . foregroundStyle ( self . isHighlighted ? . white : self . isToday ? . yellow. opacity ( 0.6 ) : . gray)
507
+ . buttonStyle ( . plain)
508
+ . useDefaultHover ( { hover in self . isHighlighted = hover} )
509
+ . overlay {
510
+ if self . showDateOverlay {
511
+ HStack {
512
+ DatePicker ( " " , selection: $sDate)
513
+ Image ( systemName: " xmark " )
514
+ }
431
515
}
432
516
}
433
- . frame ( height: 55 )
517
+
518
+ FancyButtonv2 (
519
+ text: " Next day " ,
520
+ action: self . actionNextDay,
521
+ icon: " chevron.right " ,
522
+ fgColour: . gray,
523
+ showLabel: false ,
524
+ size: . titleLink,
525
+ type: . titleLink
526
+ )
527
+ . help ( " Next day " )
528
+ . frame ( height: 20 )
529
+ }
530
+ . padding ( 12 )
531
+ . onAppear ( perform: self . actionOnAppear)
532
+ . onChange ( of: self . state. session. date) { self . actionOnChangeDate ( ) }
533
+ . onChange ( of: self . sDate) { self . state. session. date = self . sDate }
534
+ }
535
+ }
536
+ }
537
+ }
538
+
539
+ extension WidgetLibrary . UI . SimpleDateSelector {
540
+ /// Onload handler. Sets up a timer to advance to the next day and sets view state
541
+ /// - Returns: Void
542
+ private func actionOnAppear( ) -> Void {
543
+ self . actionOnChangeDate ( )
544
+
545
+ // Auto-advance date to tomorrow when the clock strikes midnight
546
+ Timer . scheduledTimer ( withTimeInterval: 3600 , repeats: true ) { timer in
547
+ let components = Calendar . autoupdatingCurrent. dateComponents ( [ . hour] , from: Date ( ) )
548
+
549
+ if let hour = components. hour {
550
+ if hour == 24 {
551
+ self . actionNextDay ( )
434
552
}
435
553
}
436
554
}
437
555
}
556
+
557
+ /// Fires when the date is changed.
558
+ /// - Returns: Void
559
+ private func actionOnChangeDate( ) -> Void {
560
+ self . date = DateHelper . todayShort ( self . state. session. date, format: " MMMM d, yyyy " )
561
+ self . isToday = self . areSameDate ( self . state. session. date, Date ( ) )
562
+ }
563
+
564
+ /// Determine if two dates are the same
565
+ /// - Parameters:
566
+ /// - lhs: Date
567
+ /// - rhs: Date
568
+ /// - Returns: Void
569
+ private func areSameDate( _ lhs: Date , _ rhs: Date ) -> Bool {
570
+ let df = DateFormatter ( )
571
+ df. dateFormat = " MMMM d "
572
+ let fmtDate = df. string ( from: lhs)
573
+ let fmtSessionDate = df. string ( from: rhs)
574
+
575
+ return fmtDate == fmtSessionDate
576
+ }
577
+
578
+ /// Decrement the current day
579
+ /// - Returns: Void
580
+ private func actionPreviousDay( ) -> Void {
581
+ self . state. session. date -= 86400
582
+ }
583
+
584
+ /// Increment the current day
585
+ /// - Returns: Void
586
+ private func actionNextDay( ) -> Void {
587
+ self . state. session. date += 86400
588
+ }
438
589
}
439
590
440
591
extension WidgetLibrary . UI . Buttons . ResetUserChoices {
0 commit comments