diff --git a/.changeset/late-forks-behave.md b/.changeset/late-forks-behave.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/.changeset/late-forks-behave.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/app.config.ts b/app.config.ts index b8deea44..721b0ef1 100644 --- a/app.config.ts +++ b/app.config.ts @@ -51,6 +51,7 @@ export default { }, ios: { icon: "src/assets/icon-ios.png", + appleTeamId: "665NDX7LBZ", bundleIdentifier: "app.exactly", associatedDomains: [`webcredentials:${process.env.EXPO_PUBLIC_DOMAIN ?? "web.exactly.app"}`], supportsTablet: false, @@ -117,6 +118,7 @@ export default { largeIcons: ["src/assets/notifications_default_large.png"], } satisfies OneSignalPlugin.OneSignalPluginProps, ], + "@bacons/apple-targets", ], experiments: { typedRoutes: true }, extra: { eas: { projectId: "06bc0158-d23b-430b-a7e8-802df03c450b" } }, diff --git a/modules/widgets/expo-module.config.json b/modules/widgets/expo-module.config.json new file mode 100644 index 00000000..15a744b0 --- /dev/null +++ b/modules/widgets/expo-module.config.json @@ -0,0 +1,6 @@ +{ + "platforms": ["apple"], + "apple": { + "modules": ["ExpoWidgetModule"] + } +} diff --git a/modules/widgets/index.ts b/modules/widgets/index.ts new file mode 100644 index 00000000..88ed4fa4 --- /dev/null +++ b/modules/widgets/index.ts @@ -0,0 +1,18 @@ +declare global { + namespace Native { + interface ExpoWidget { + set(key: string, value: string, suite?: string): void; + } + } + + interface NativeModules { + ExpoWidget?: Native.ExpoWidget; + } +} + +// TODO: Can we drop this? +const m = (expo?.modules?.ExpoWidget ?? { + set() {}, +}) as Native.ExpoWidget; + +export default m; diff --git a/modules/widgets/ios/ExpoWidget.podspec b/modules/widgets/ios/ExpoWidget.podspec new file mode 100644 index 00000000..0a3107cc --- /dev/null +++ b/modules/widgets/ios/ExpoWidget.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = 'ExpoWidget' + s.version = '1.0.0' + s.summary = 'A sample project summary' + s.description = 'A sample project description' + s.author = '' + s.homepage = 'https://docs.expo.dev/modules/' + s.platforms = { + :ios => '15.1', + :tvos => '15.1' + } + s.source = { git: '' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + # Swift/Objective-C compatibility + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + } + + s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}" +end diff --git a/modules/widgets/ios/ExpoWidgetModule.swift b/modules/widgets/ios/ExpoWidgetModule.swift new file mode 100644 index 00000000..0e65ca82 --- /dev/null +++ b/modules/widgets/ios/ExpoWidgetModule.swift @@ -0,0 +1,15 @@ +import ExpoModulesCore +import WidgetKit + +public class ExpoWidgetModule: Module { + public func definition() -> ModuleDefinition { + Name("ExpoWidget") + + Function("set") { (key: String, value: String, group: String?) in + let userDefaults = UserDefaults(suiteName: group) + userDefaults?.set(value, forKey: key) + + WidgetCenter.shared.reloadAllTimelines() + } + } +} diff --git a/targets/widgets/Assets.xcassets/$accent.colorset/Contents.json b/targets/widgets/Assets.xcassets/$accent.colorset/Contents.json new file mode 100644 index 00000000..161ffc99 --- /dev/null +++ b/targets/widgets/Assets.xcassets/$accent.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors": [ + { + "color": { + "color-space": "display-p3", + "components": { + "red": 0.27450980392156865, + "green": 0.5098039215686274, + "blue": 0.7058823529411765, + "alpha": 1 + } + }, + "idiom": "universal" + } + ], + "info": { + "version": 1, + "author": "expo" + } +} \ No newline at end of file diff --git a/targets/widgets/Assets.xcassets/$widgetBackground.colorset/Contents.json b/targets/widgets/Assets.xcassets/$widgetBackground.colorset/Contents.json new file mode 100644 index 00000000..25153d77 --- /dev/null +++ b/targets/widgets/Assets.xcassets/$widgetBackground.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors": [ + { + "color": { + "color-space": "display-p3", + "components": { + "red": 0.11764705882352941, + "green": 0.5647058823529412, + "blue": 1, + "alpha": 1 + } + }, + "idiom": "universal" + } + ], + "info": { + "version": 1, + "author": "expo" + } +} \ No newline at end of file diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png new file mode 100644 index 00000000..19e4e0bb Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png new file mode 100644 index 00000000..b484ad2a Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png new file mode 100644 index 00000000..a3ee63bb Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png new file mode 100644 index 00000000..523a9ad3 Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png new file mode 100644 index 00000000..239e49c9 Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png new file mode 100644 index 00000000..63b25eca Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png new file mode 100644 index 00000000..b484ad2a Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png new file mode 100644 index 00000000..f28451b1 Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png new file mode 100644 index 00000000..d22e4a6e Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png new file mode 100644 index 00000000..d22e4a6e Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png new file mode 100644 index 00000000..d369a438 Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png new file mode 100644 index 00000000..80978507 Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png new file mode 100644 index 00000000..126fe953 Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png new file mode 100644 index 00000000..ed34a4af Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png differ diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/Contents.json b/targets/widgets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..f920cb0e --- /dev/null +++ b/targets/widgets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images": [ + { + "idiom": "iphone", + "size": "20x20", + "scale": "2x", + "filename": "App-Icon-20x20@2x.png" + }, + { + "idiom": "iphone", + "size": "20x20", + "scale": "3x", + "filename": "App-Icon-20x20@3x.png" + }, + { + "idiom": "iphone", + "size": "29x29", + "scale": "1x", + "filename": "App-Icon-29x29@1x.png" + }, + { + "idiom": "iphone", + "size": "29x29", + "scale": "2x", + "filename": "App-Icon-29x29@2x.png" + }, + { + "idiom": "iphone", + "size": "29x29", + "scale": "3x", + "filename": "App-Icon-29x29@3x.png" + }, + { + "idiom": "iphone", + "size": "40x40", + "scale": "2x", + "filename": "App-Icon-40x40@2x.png" + }, + { + "idiom": "iphone", + "size": "40x40", + "scale": "3x", + "filename": "App-Icon-40x40@3x.png" + }, + { + "idiom": "iphone", + "size": "60x60", + "scale": "2x", + "filename": "App-Icon-60x60@2x.png" + }, + { + "idiom": "iphone", + "size": "60x60", + "scale": "3x", + "filename": "App-Icon-60x60@3x.png" + }, + { + "idiom": "ipad", + "size": "20x20", + "scale": "1x", + "filename": "App-Icon-20x20@1x.png" + }, + { + "idiom": "ipad", + "size": "20x20", + "scale": "2x", + "filename": "App-Icon-20x20@2x.png" + }, + { + "idiom": "ipad", + "size": "29x29", + "scale": "1x", + "filename": "App-Icon-29x29@1x.png" + }, + { + "idiom": "ipad", + "size": "29x29", + "scale": "2x", + "filename": "App-Icon-29x29@2x.png" + }, + { + "idiom": "ipad", + "size": "40x40", + "scale": "1x", + "filename": "App-Icon-40x40@1x.png" + }, + { + "idiom": "ipad", + "size": "40x40", + "scale": "2x", + "filename": "App-Icon-40x40@2x.png" + }, + { + "idiom": "ipad", + "size": "76x76", + "scale": "1x", + "filename": "App-Icon-76x76@1x.png" + }, + { + "idiom": "ipad", + "size": "76x76", + "scale": "2x", + "filename": "App-Icon-76x76@2x.png" + }, + { + "idiom": "ipad", + "size": "83.5x83.5", + "scale": "2x", + "filename": "App-Icon-83.5x83.5@2x.png" + }, + { + "idiom": "ios-marketing", + "size": "1024x1024", + "scale": "1x", + "filename": "ItunesArtwork@2x.png" + } + ], + "info": { + "version": 1, + "author": "expo" + } +} \ No newline at end of file diff --git a/targets/widgets/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/targets/widgets/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 00000000..51957c4a Binary files /dev/null and b/targets/widgets/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/targets/widgets/Assets.xcassets/Color1.colorset/Contents.json b/targets/widgets/Assets.xcassets/Color1.colorset/Contents.json new file mode 100644 index 00000000..a7d67c46 --- /dev/null +++ b/targets/widgets/Assets.xcassets/Color1.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x73", + "green" : "0x1D", + "red" : "0xB9" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/targets/widgets/Assets.xcassets/Color2.colorset/Contents.json b/targets/widgets/Assets.xcassets/Color2.colorset/Contents.json new file mode 100644 index 00000000..b977521b --- /dev/null +++ b/targets/widgets/Assets.xcassets/Color2.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xC6", + "green" : "0x53", + "red" : "0xF9" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/targets/widgets/Assets.xcassets/Contents.json b/targets/widgets/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/targets/widgets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/targets/widgets/Info.plist b/targets/widgets/Info.plist new file mode 100644 index 00000000..0f118fb7 --- /dev/null +++ b/targets/widgets/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/targets/widgets/PrivacyInfo.xcprivacy b/targets/widgets/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..5bb83c5d --- /dev/null +++ b/targets/widgets/PrivacyInfo.xcprivacy @@ -0,0 +1,48 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + 0A2A.1 + 3B52.1 + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + 85F4.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/targets/widgets/expo-target.config.js b/targets/widgets/expo-target.config.js new file mode 100644 index 00000000..9994d637 --- /dev/null +++ b/targets/widgets/expo-target.config.js @@ -0,0 +1,12 @@ +/** @type {import('@bacons/apple-targets').Config} */ +module.exports = { + type: "widget", + icon: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/FullMoon2010.jpg/1200px-FullMoon2010.jpg", + colors: { + $accent: "steelblue", + $widgetBackground: "dodgerblue", + }, + entitlements: { + "com.apple.security.application-groups": ["group.bacon.data"], + }, +}; diff --git a/targets/widgets/generated.entitlements b/targets/widgets/generated.entitlements new file mode 100644 index 00000000..d1b5ffac --- /dev/null +++ b/targets/widgets/generated.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.bacon.data + + + \ No newline at end of file diff --git a/targets/widgets/index.swift b/targets/widgets/index.swift new file mode 100644 index 00000000..aa01e706 --- /dev/null +++ b/targets/widgets/index.swift @@ -0,0 +1,10 @@ +import WidgetKit +import SwiftUI + +@main +struct exportWidgets: WidgetBundle { + var body: some Widget { + // Export widgets here + widgets() + } +} diff --git a/targets/widgets/widgets.intentdefinition b/targets/widgets/widgets.intentdefinition new file mode 100644 index 00000000..bdb40455 --- /dev/null +++ b/targets/widgets/widgets.intentdefinition @@ -0,0 +1,59 @@ + + + + + INEnums + + INIntentDefinitionModelVersion + 1.2 + INIntentDefinitionNamespace + 88xZPY + INIntentDefinitionSystemVersion + 20A294 + INIntentDefinitionToolsBuildVersion + 12A6144 + INIntentDefinitionToolsVersion + 12.0 + INIntents + + + INIntentCategory + information + INIntentDescriptionID + tVvJ9c + INIntentEligibleForWidgets + + INIntentIneligibleForSuggestions + + INIntentName + Configuration + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseCodeName + failure + + + + INIntentTitle + Configuration + INIntentTitleID + gpCwrM + INIntentType + Custom + INIntentVerb + View + + + INTypes + + + diff --git a/targets/widgets/widgets.swift b/targets/widgets/widgets.swift new file mode 100644 index 00000000..4b94090e --- /dev/null +++ b/targets/widgets/widgets.swift @@ -0,0 +1,63 @@ +import WidgetKit +import SwiftUI +import Intents + +struct Provider: IntentTimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), configuration: ConfigurationIntent()) + } + + func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date(), configuration: configuration) + completion(entry) + } + + func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [SimpleEntry] = [] + + // Generate a timeline consisting of five entries an hour apart, starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = SimpleEntry(date: entryDate, configuration: configuration) + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let configuration: ConfigurationIntent +} + +struct widgetsEntryView : View { + var entry: Provider.Entry + + var body: some View { + let defaults = UserDefaults(suiteName: "group.bacon.data") + let index = defaults?.integer(forKey: "index") + Text("\(index ?? 0)") + } +} + +struct widgets: Widget { + let kind: String = "widgets" + + var body: some WidgetConfiguration { + IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in + widgetsEntryView(entry: entry) + } + .configurationDisplayName("My Widget") + .description("This is an example widget.") + } +} + +struct widgets_Previews: PreviewProvider { + static var previews: some View { + widgetsEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent())) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + } +} diff --git a/tsconfig.json b/tsconfig.json index a83bea9a..3b859a20 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,7 @@ ".expo/types/**/*.ts", "common/**/*.ts", "expo-env.d.ts", + "modules/**/*.ts", "src/**/*.ts", "src/**/*.tsx", "tamagui.config.ts" diff --git a/tsconfig.node.json b/tsconfig.node.json index d643bace..9974a317 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -17,5 +17,5 @@ "pretty": true, "strict": true }, - "include": ["**/*.config.ts", "**/*.config.mjs", "**/*.config.cjs", ".eslintrc.cjs"] + "include": ["**/*.config.ts", "**/*.config.mjs", "**/*.config.cjs", "**/*.config.js", ".eslintrc.cjs"] }