ios widget showing what is available at chucks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add lockscreen widgets

+109 -3
+109 -3
widget/widget.swift
··· 558 558 } 559 559 } 560 560 561 + // MARK: - Lock Screen Widgets 562 + 563 + struct AccessoryInlineView: View { 564 + let entry: ChucksEntry 565 + 566 + var body: some View { 567 + if entry.status.isOpen { 568 + if let remaining = entry.status.timeRemaining { 569 + Label("Open • \(remaining.compactCountdown) left", systemImage: entry.status.currentPhase.icon) 570 + } else { 571 + Label("Open for \(entry.status.currentPhase.shortName)", systemImage: entry.status.currentPhase.icon) 572 + } 573 + } else if let next = entry.status.nextPhase, next != .closed { 574 + if let remaining = entry.status.timeRemaining { 575 + Label("\(next.shortName) in \(remaining.compactCountdown)", systemImage: next.icon) 576 + } else { 577 + Label("\(next.shortName) soon", systemImage: next.icon) 578 + } 579 + } else { 580 + Label("Closed", systemImage: "moon.zzz.fill") 581 + } 582 + } 583 + } 584 + 585 + struct AccessoryCircularView: View { 586 + let entry: ChucksEntry 587 + 588 + var body: some View { 589 + ZStack { 590 + AccessoryWidgetBackground() 591 + VStack(spacing: 0) { 592 + Image(systemName: entry.status.isOpen ? entry.status.currentPhase.icon : (entry.status.nextPhase?.icon ?? "moon.zzz.fill")) 593 + .font(.caption) 594 + if let remaining = entry.status.timeRemaining { 595 + Text(remaining.compactCountdown) 596 + .font(.system(.body, design: .rounded, weight: .semibold)) 597 + .monospacedDigit() 598 + } 599 + } 600 + } 601 + } 602 + } 603 + 604 + struct AccessoryRectangularView: View { 605 + let entry: ChucksEntry 606 + 607 + var body: some View { 608 + VStack(alignment: .leading, spacing: 2) { 609 + HStack(spacing: 4) { 610 + Image(systemName: entry.status.isOpen ? entry.status.currentPhase.icon : (entry.status.nextPhase?.icon ?? "moon.zzz.fill")) 611 + Text(entry.status.isOpen ? "Open" : "Closed") 612 + .font(.headline) 613 + } 614 + 615 + if entry.status.isOpen { 616 + if let remaining = entry.status.timeRemaining { 617 + Text("\(entry.status.currentPhase.shortName) ends in \(remaining.compactCountdown)") 618 + .font(.caption) 619 + .foregroundStyle(.secondary) 620 + } 621 + } else if let next = entry.status.nextPhase, next != .closed { 622 + if let remaining = entry.status.timeRemaining { 623 + Text("\(next.shortName) opens in \(remaining.compactCountdown)") 624 + .font(.caption) 625 + .foregroundStyle(.secondary) 626 + } 627 + } 628 + 629 + if !entry.specials.isEmpty { 630 + Text(entry.specials.prefix(2).map { $0.name }.joined(separator: ", ")) 631 + .font(.caption2) 632 + .foregroundStyle(.secondary) 633 + .lineLimit(1) 634 + } 635 + } 636 + .frame(maxWidth: .infinity, alignment: .leading) 637 + } 638 + } 639 + 561 640 // MARK: - Widget Configuration 562 641 struct ChucksWidget: Widget { 563 642 let kind: String = "ChucksWidget" 564 - 643 + 565 644 var body: some WidgetConfiguration { 566 645 StaticConfiguration(kind: kind, provider: ChucksProvider()) { entry in 567 646 WidgetView(entry: entry) ··· 569 648 } 570 649 .configurationDisplayName("Chuck's Status") 571 650 .description("See current meal times and specials at Chuck's.") 572 - .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) 651 + .supportedFamilies([.systemSmall, .systemMedium, .systemLarge, .accessoryInline, .accessoryCircular, .accessoryRectangular]) 573 652 } 574 653 } 575 654 576 655 struct WidgetView: View { 577 656 @Environment(\.widgetFamily) var family 578 657 let entry: ChucksEntry 579 - 658 + 580 659 var body: some View { 581 660 switch family { 582 661 case .systemSmall: ··· 585 664 MediumWidgetView(entry: entry) 586 665 case .systemLarge: 587 666 LargeWidgetView(entry: entry) 667 + case .accessoryInline: 668 + AccessoryInlineView(entry: entry) 669 + case .accessoryCircular: 670 + AccessoryCircularView(entry: entry) 671 + case .accessoryRectangular: 672 + AccessoryRectangularView(entry: entry) 588 673 default: 589 674 SmallWidgetView(entry: entry) 590 675 } ··· 621 706 MenuItem(name: "Hash Browns", allergens: []) 622 707 ], venueName: "Home Cooking") 623 708 } 709 + 710 + #Preview("Lock Screen - Inline", as: .accessoryInline) { 711 + ChucksWidget() 712 + } timeline: { 713 + ChucksEntry(date: Date(), status: ChucksStatus.calculate(), specials: [], venueName: "Home Cooking") 714 + } 715 + 716 + #Preview("Lock Screen - Circular", as: .accessoryCircular) { 717 + ChucksWidget() 718 + } timeline: { 719 + ChucksEntry(date: Date(), status: ChucksStatus.calculate(), specials: [], venueName: "Home Cooking") 720 + } 721 + 722 + #Preview("Lock Screen - Rectangular", as: .accessoryRectangular) { 723 + ChucksWidget() 724 + } timeline: { 725 + ChucksEntry(date: Date(), status: ChucksStatus.calculate(), specials: [ 726 + MenuItem(name: "Scrambled Eggs", allergens: []), 727 + MenuItem(name: "Sausage Patties", allergens: []) 728 + ], venueName: "Home Cooking") 729 + }