| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- import Contacts
- import ContactsUI
- import SwiftUI
- import Swinject
- extension ContactTrick {
- struct RootView: BaseView {
- let resolver: Resolver
- @StateObject var state = StateModel()
- @State private var contactStore = CNContactStore()
- @State private var authorization = CNContactStore.authorizationStatus(for: .contacts)
- @State private var contactTrickEntries = [ContactTrickEntry]()
- var body: some View {
- Form {
- switch authorization {
- case .authorized:
- Section(header: Text("Contacts")) {
- list
- addButton
- }
- Section(
- header: state.changed ?
- Text("Don't forget to save your changes.")
- .frame(maxWidth: .infinity, alignment: .center)
- .foregroundStyle(.primary) : nil
- ) {
- HStack {
- if state.syncInProgress {
- ProgressView().padding(.trailing, 10)
- }
- Button { state.save() }
- label: {
- Text(state.syncInProgress ? "Saving..." : "Save")
- }
- .disabled(state.syncInProgress || !state.changed)
- .frame(maxWidth: .infinity, alignment: .center)
- }
- }
- case .notDetermined:
- Section {
- Text(
- "Trio needs access to your contacts for this feature to work"
- )
- }
- Section {
- Button(action: onRequestContactsAccess) {
- Text("Grant Trio access to contacts")
- }
- }
- case .denied:
- Section {
- Text(
- "Access to contacts denied"
- )
- }
- case .restricted:
- Section {
- Text(
- "Access to contacts is restricted (parental control?)"
- )
- }
- case .limited:
- Section {
- Text(
- "Access to contacts is limited. Trio needs full access to contacts for this feature to work"
- )
- }
- @unknown default:
- Section {
- Text(
- "Access to contacts - unknown state"
- )
- }
- }
- Section {}
- footer: {
- Text(
- "A Contact Image can be used to get live updates from Trio to your Apple Watch Contact complication and/or your iPhone Contact widget."
- )
- .frame(maxWidth: .infinity, alignment: .center)
- }
- }
- .dynamicTypeSize(...DynamicTypeSize.xxLarge)
- .onAppear(perform: configureView)
- .navigationTitle("Contact Image")
- .navigationBarTitleDisplayMode(.automatic)
- .navigationBarItems(
- trailing: EditButton()
- )
- }
- private func contactSettings(for index: Int) -> some View {
- EntryView(entry: Binding(
- get: { state.items[index].entry },
- set: { newValue in state.update(index, newValue) }
- ), previewState: previewState)
- }
- var previewState: ContactTrickState {
- let units = state.units
- return ContactTrickState(
- glucose: units == .mmolL ? "6,8" : "127",
- trend: "↗︎",
- delta: units == .mmolL ? "+0,3" : "+7",
- lastLoopDate: .now,
- iob: 6.1,
- iobText: "6,1",
- cob: 27.0,
- cobText: "27",
- eventualBG: units == .mmolL ? "8,9" : "163",
- maxIOB: 12.0,
- maxCOB: 120.0
- )
- }
- private var list: some View {
- List {
- ForEach(state.items.indexed(), id: \.1.id) { index, item in
- NavigationLink(destination: contactSettings(for: index)) {
- EntryListView(entry: .constant(item.entry), index: .constant(index), previewState: previewState)
- }
- .moveDisabled(true)
- }
- .onDelete(perform: onDelete)
- }
- }
- private var addButton: some View {
- AnyView(Button(action: onAdd) { Text("Add") })
- }
- func onAdd() {
- state.add()
- }
- func onRequestContactsAccess() {
- contactStore.requestAccess(for: .contacts) { _, _ in
- DispatchQueue.main.async {
- authorization = CNContactStore.authorizationStatus(for: .contacts)
- }
- }
- }
- private func onDelete(offsets: IndexSet) {
- state.remove(atOffsets: offsets)
- }
- }
- struct EntryListView: View {
- @Binding var entry: ContactTrickEntry
- @Binding var index: Int
- @State private var refreshKey = UUID()
- let previewState: ContactTrickState
- var body: some View {
- HStack {
- VStack(alignment: .leading) {
- GeometryReader { geometry in
- ZStack {
- Circle()
- .fill(entry.darkMode ? .black : .white)
- .foregroundColor(.white)
- Image(uiImage: ContactPicture.getImage(contact: entry, state: previewState))
- .resizable()
- .aspectRatio(1, contentMode: .fit)
- .frame(width: geometry.size.height, height: geometry.size.height)
- .clipShape(Circle())
- Circle()
- .stroke(lineWidth: 2)
- .foregroundColor(.white)
- }
- .frame(width: geometry.size.height, height: geometry.size.height)
- }
- }
- .fixedSize(horizontal: true, vertical: false)
- .padding(.horizontal, 30)
- Spacer()
- VStack {
- Text("Contact: Trio \(index + 1)").bold()
- // HStack {
- // Text("Layout: \(entry.layout.displayName)")
- // Text("\(entry.ring.displayName)")
- // if entry.layout == .single {
- // Text("\(entry.primary.displayName)")
- // }
- // Text("\(entry.top.displayName), \(entry.bottom.displayName)")
- // }.foregroundStyle(.secondary)
- // HStack {
- // Text("Font Size \(entry.fontSize.displayName)")
- // Text("Font Width \(entry.fontWidth.displayName)")
- // Text("Font Weight \(entry.fontWeight.displayName)")
- // }.foregroundStyle(.secondary)
- }
- }
- .frame(maxWidth: .infinity)
- }
- }
- struct EntryView: View {
- @Binding var entry: ContactTrickEntry
- @State private var availableFonts: [String]? = nil
- let previewState: ContactTrickState
- private let ringWidths: [Int] = [5, 10, 15]
- private let ringGaps: [Int] = [0, 2, 4]
- var body: some View {
- VStack {
- Section {
- HStack {
- ZStack {
- Circle()
- .fill(entry.darkMode ? .black : .white)
- Image(uiImage: ContactPicture.getImage(contact: entry, state: previewState))
- .resizable()
- .aspectRatio(1, contentMode: .fit)
- .frame(width: 64, height: 64)
- .clipShape(Circle())
- Circle()
- .stroke(lineWidth: 2)
- .foregroundColor(.white)
- }
- .frame(width: 64, height: 64)
- }
- }
- Form {
- Section {
- Picker(
- selection: $entry.layout,
- label: Text("Layout")
- ) {
- ForEach(ContactTrickLayout.allCases, id: \.self) { layout in
- Text(layout.displayName).tag(layout)
- }
- }
- }
- layoutSpecificSection
- Section(header: Text("Ring")) {
- Picker(
- selection: $entry.ring,
- label: Text("Outer")
- ) {
- ForEach(ContactTrickLargeRing.allCases, id: \.self) { ring in
- Text(ring.displayName).tag(ring)
- }
- }
- if entry.ring != .none {
- Picker(
- selection: $entry.ringWidth,
- label: Text("Width")
- ) {
- ForEach(
- [
- ContactTrickEntry.RingWidth.tiny,
- ContactTrickEntry.RingWidth.small,
- ContactTrickEntry.RingWidth.regular,
- ContactTrickEntry.RingWidth.medium,
- ContactTrickEntry.RingWidth.large
- ],
- id: \.self
- ) { width in
- Text(width.displayName).tag(width)
- }
- }
- Picker(
- selection: $entry.ringGap,
- label: Text("Gap")
- ) {
- ForEach(
- [
- ContactTrickEntry.RingGap.tiny,
- ContactTrickEntry.RingGap.small,
- ContactTrickEntry.RingGap.regular,
- ContactTrickEntry.RingGap.medium,
- ContactTrickEntry.RingGap.large
- ],
- id: \.self
- ) { gap in
- Text(gap.displayName).tag(gap)
- }
- }
- }
- }
- Section(header: Text("Font")) {
- Picker(
- selection: $entry.fontSize,
- label: Text("Size")
- ) {
- ForEach(
- [
- ContactTrickEntry.FontSize.tiny,
- ContactTrickEntry.FontSize.small,
- ContactTrickEntry.FontSize.regular,
- ContactTrickEntry.FontSize.large
- ],
- id: \.self
- ) { size in
- Text(size.displayName).tag(size)
- }
- }
- Picker(
- selection: $entry.secondaryFontSize,
- label: Text("Secondary size")
- ) {
- ForEach(
- [
- ContactTrickEntry.FontSize.tiny,
- ContactTrickEntry.FontSize.small,
- ContactTrickEntry.FontSize.regular,
- ContactTrickEntry.FontSize.large
- ],
- id: \.self
- ) { size in
- Text(size.displayName).tag(size)
- }
- }
- Picker(
- selection: $entry.fontWidth,
- label: Text("Width")
- ) {
- ForEach(
- [Font.Width.standard, Font.Width.condensed, Font.Width.expanded],
- id: \.self
- ) { width in
- Text(width.displayName).tag(width)
- }
- }
- Picker(
- selection: $entry.fontWeight,
- label: Text("Weight")
- ) {
- ForEach(
- [Font.Weight.light, Font.Weight.regular, Font.Weight.medium, Font.Weight.bold, Font.Weight.black],
- id: \.self
- ) { weight in
- Text(weight.displayName).tag(weight)
- }
- }
- }
- Section {
- Toggle("Dark mode", isOn: $entry.darkMode)
- }
- }
- }
- }
- private var layoutSpecificSection: some View {
- Section {
- if entry.layout == .single {
- Picker(
- selection: $entry.primary,
- label: Text("Primary")
- ) {
- ForEach(ContactTrickValue.allCases, id: \.self) { value in
- Text(value.displayName).tag(value)
- }
- }
- Picker(
- selection: $entry.top,
- label: Text("Top")
- ) {
- ForEach(ContactTrickValue.allCases, id: \.self) { value in
- Text(value.displayName).tag(value)
- }
- }
- Picker(
- selection: $entry.bottom,
- label: Text("Bottom")
- ) {
- ForEach(ContactTrickValue.allCases, id: \.self) { value in
- Text(value.displayName).tag(value)
- }
- }
- } else if entry.layout == .split {
- Picker(
- selection: $entry.top,
- label: Text("Top")
- ) {
- ForEach(ContactTrickValue.allCases, id: \.self) { value in
- Text(value.displayName).tag(value)
- }
- }
- Picker(
- selection: $entry.bottom,
- label: Text("Bottom")
- ) {
- ForEach(ContactTrickValue.allCases, id: \.self) { value in
- Text(value.displayName).tag(value)
- }
- }
- }
- }
- }
- }
- }
|