Files
lockdown-iOS-mirror/LockdownFirewallWidget/LockdownFirewallWidget.swift

240 lines
7.8 KiB
Swift

//
// LockdownFirewallWidget.swift
// LockdownFirewallWidget
//
// Created by Oleg Dreyman on 25.09.2020.
// Copyright © 2020 Confirmed Inc. All rights reserved.
//
import WidgetKit
import SwiftUI
struct FirewallProvider: TimelineProvider {
func placeholder(in context: Context) -> FirewallEntry {
FirewallEntry(date: Date(), size: context.displaySize, isFirewallEnabled: false, dayMetricsString: "--")
}
func getSnapshot(in context: Context, completion: @escaping (FirewallEntry) -> ()) {
let entry = FirewallEntry(date: Date(), size: context.displaySize, isFirewallEnabled: getUserWantsFirewallEnabled(), dayMetricsString: getDayMetricsString(commas: true))
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [FirewallEntry] = []
let currentDate = Date()
let entry = FirewallEntry(date: Date(), size: context.displaySize, isFirewallEnabled: getUserWantsFirewallEnabled(), dayMetricsString: getDayMetricsString(commas: true))
entries.append(entry)
let timeline = Timeline(
entries: entries,
policy: .after(Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!)
)
completion(timeline)
}
}
struct FirewallEntry: TimelineEntry {
let date: Date
let size: CGSize
let isFirewallEnabled: Bool
let dayMetricsString: String
var buttonColor: Color {
if isFirewallEnabled {
return .confirmedBlue
} else {
return Color(.systemGray)
}
}
}
struct LockdownFirewallWidgetEntryView : View {
var entry: FirewallProvider.Entry
var body: some View {
VStack(spacing: 0) {
LoadingCircle(
tunnelState: TunnelState(
color: entry.buttonColor,
circleColor: entry.buttonColor
),
side: entry.size.height,
link: "lockdown://"
)
.padding(EdgeInsets(top: 12, leading: 0, bottom: 2, trailing: 0))
if entry.isFirewallEnabled {
StatusLabel(text: NSLocalizedString("FIREWALL ON", comment: ""), color: .confirmedBlue)
} else {
StatusLabel(text: NSLocalizedString("FIREWALL OFF", comment: ""), color: .flatRed)
}
if entry.size.height < 160 {
Spacer().frame(minHeight: 4)
} else {
Spacer()
}
Link(destination: URL(string: "lockdown://showMetrics")!, label: {
VStack(spacing: 0) {
Text(entry.dayMetricsString)
.font(.system(size: 21, weight: .semibold))
Text(NSLocalizedString("Blocked Today", comment: ""))
.font(.system(size: 12, weight: .medium))
}
.padding(.bottom, 12)
})
}.frame(width: entry.size.height, height: entry.size.height)
}
}
struct VPNProvider: TimelineProvider {
func placeholder(in context: Context) -> VPNEntry {
VPNEntry(date: Date(), size: context.displaySize, isVPNEnabled: false, vpnRegion: VPNRegion())
}
func getSnapshot(in context: Context, completion: @escaping (VPNEntry) -> ()) {
let entry = VPNEntry(date: Date(), size: context.displaySize, isVPNEnabled: LatestKnowledge.isVPNEnabled, vpnRegion: getSavedVPNRegion())
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [VPNEntry] = []
let currentDate = Date()
let entry = VPNEntry(date: Date(), size: context.displaySize, isVPNEnabled: LatestKnowledge.isVPNEnabled, vpnRegion: getSavedVPNRegion())
entries.append(entry)
let timeline = Timeline(
entries: entries,
policy: .after(Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!)
)
completion(timeline)
}
}
struct VPNEntry: TimelineEntry {
let date: Date
let size: CGSize
let isVPNEnabled: Bool
let vpnRegion: VPNRegion
var buttonColor: Color {
if isVPNEnabled {
return .confirmedBlue
} else {
return Color(.systemGray)
}
}
}
struct LockdownVPNWidgetEntryView : View {
var entry: VPNProvider.Entry
var body: some View {
VStack(spacing: 0) {
LoadingCircle(
tunnelState: TunnelState(
color: entry.buttonColor,
circleColor: entry.buttonColor
),
side: entry.size.height,
link: "lockdown://"
)
.padding(EdgeInsets(top: 12, leading: 0, bottom: 2, trailing: 0))
if entry.isVPNEnabled {
StatusLabel(text: NSLocalizedString("Tunnel On", comment: "").uppercased(), color: .confirmedBlue)
} else {
StatusLabel(text: NSLocalizedString("Tunnel Off", comment: "").uppercased(), color: .flatRed)
}
if entry.size.height < 160 {
Spacer().frame(minHeight: 4)
} else {
Spacer()
}
Link(destination: URL(string: "lockdown://changeVPNregion")!, label: {
VStack(spacing: 0) {
Text(entry.vpnRegion.regionFlagEmoji)
.font(.system(size: 21, weight: .semibold))
Text(entry.vpnRegion.regionDisplayNameShort)
.font(.system(size: 12, weight: .medium))
}
.padding(.bottom, 12)
})
}.frame(width: entry.size.height, height: entry.size.height)
}
}
struct CombinedWidgetView: View {
let firewall: FirewallEntry
let vpn: VPNEntry
var body: some View {
HStack(alignment: .top, spacing: 0) {
LockdownFirewallWidgetEntryView(entry: firewall)
LockdownVPNWidgetEntryView(entry: vpn)
}
.frame(minWidth: firewall.size.width, minHeight: firewall.size.height)
}
}
@main
struct LockdownWidgetBundle: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
LockdownFirewallWidget()
LockdownVPNWidget()
LockdownCombinedWidget()
}
}
struct LockdownFirewallWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "LockdownFirewallWidget",
provider: FirewallProvider(),
content: { entry in
ZStack {
Color.panelBackground
LockdownFirewallWidgetEntryView(entry: entry)
}
}
)
.configurationDisplayName("Firewall")
.supportedFamilies([.systemSmall])
}
}
struct LockdownVPNWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "LockdownVPNWidget",
provider: VPNProvider(),
content: { entry in
ZStack {
Color.panelBackground
LockdownVPNWidgetEntryView(entry: entry)
}
}
)
.configurationDisplayName("Secure Tunnel")
.supportedFamilies([.systemSmall])
}
}
struct LockdownCombinedWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(
kind: "LockdownCombinedWidget",
provider: CombinedProvider(main: FirewallProvider(), supplemental: VPNProvider()),
content: { entry in
ZStack {
Color.panelBackground
CombinedWidgetView(firewall: entry.main, vpn: entry.supplemental)
}
}
)
.configurationDisplayName("Firewall + Tunnel")
.supportedFamilies([.systemMedium])
}
}