diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 379f9048..94a25f7f 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index d45ca996..d06dfea7 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/app/app.json b/packages/app/app.json index 34b9efe1..b7e45508 100644 --- a/packages/app/app.json +++ b/packages/app/app.json @@ -48,6 +48,14 @@ ] } ], + [ + "expo-build-properties", + { + "ios": { + "deploymentTarget": "17.0" + } + } + ], "sentry-expo", [ "expo-router", diff --git a/packages/app/expoPlugin/ios/ORWidgetReloadBridge.m b/packages/app/expoPlugin/ios/ORWidgetReloadBridge.m new file mode 100644 index 00000000..1f2397ac --- /dev/null +++ b/packages/app/expoPlugin/ios/ORWidgetReloadBridge.m @@ -0,0 +1,17 @@ +// +// ORWidgetReloadBridge.m +// Orbit +// +// Created by Andy Matuschak on 2024-07-12. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCT_EXTERN_MODULE(WidgetReloadBridge, NSObject) +RCT_EXTERN_METHOD(reloadTimelines) +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/app/expoPlugin/ios/ORWidgetReloadBridge.swift b/packages/app/expoPlugin/ios/ORWidgetReloadBridge.swift new file mode 100644 index 00000000..73a1515b --- /dev/null +++ b/packages/app/expoPlugin/ios/ORWidgetReloadBridge.swift @@ -0,0 +1,16 @@ +// +// ORWidgetReloadBridge.swift +// Orbit +// +// Created by Andy Matuschak on 2024-07-12. +// + +import Foundation +import WidgetKit + +@objc(WidgetReloadBridge) class WidgetReloadBridge: NSObject { + @objc public func reloadTimelines() { + WidgetCenter.shared.reloadAllTimelines() + print("Reloading timelines") + } +} diff --git a/packages/app/expoPlugin/withWidgetPluginFixes.js b/packages/app/expoPlugin/withWidgetPluginFixes.js index 4dbaa75b..6cf346cd 100644 --- a/packages/app/expoPlugin/withWidgetPluginFixes.js +++ b/packages/app/expoPlugin/withWidgetPluginFixes.js @@ -1,7 +1,18 @@ -const { withMod } = require("expo/config-plugins"); +const { withMod, withXcodeProject } = require("expo/config-plugins"); const { PBXBuildFile, PBXShellScriptBuildPhase } = require("@bacons/xcode"); const path = require("path"); +const { addSourceFile } = require("./util"); module.exports = function withWidgetPluginFixes(config) { + config = withXcodeProject(config, async (config) => { + for (const file of [ + "ORWidgetReloadBridge.m", + "ORWidgetReloadBridge.swift", + ]) { + addSourceFile(config, file); + } + return config; + }); + config = withMod(config, { platform: "ios", // Bit of a hack: we're hooking into @bacons/apple-target's mod here. This string must match the one in https://github.com/EvanBacon/expo-apple-targets/blob/afd63577f43aa50665f2c31ed4ed14c052bc57bd/packages/apple-targets/src/withXcparse.ts. diff --git a/packages/app/package.json b/packages/app/package.json index 09a9a841..4d2aed53 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -28,6 +28,7 @@ "base-64": "^1.0.0", "expo": "~51.0.17", "expo-application": "~5.9.1", + "expo-build-properties": "^0.12.3", "expo-constants": "~16.0.2", "expo-crypto": "~13.0.2", "expo-device": "~6.0.2", diff --git a/packages/app/src/reviewSession/ReviewSession.tsx b/packages/app/src/reviewSession/ReviewSession.tsx index 6fea2072..cb1b85e7 100644 --- a/packages/app/src/reviewSession/ReviewSession.tsx +++ b/packages/app/src/reviewSession/ReviewSession.tsx @@ -26,7 +26,7 @@ import { } from "@withorbit/ui"; import { useLocalSearchParams } from "expo-router"; import React, { useEffect, useRef, useState } from "react"; -import { Platform, Text, View } from "react-native"; +import { NativeModules, Platform, Text, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AuthenticationClient } from "../authentication/index.js"; import { @@ -39,6 +39,8 @@ import { useReviewSessionManager } from "../reviewSessionManager.js"; import { useAPIClient } from "../util/useAPIClient.js"; import { LoadingScreen } from "./LoadingScreen.js"; +const { WidgetReloadBridge } = NativeModules; + export function useDatabaseManager( authenticationClient: AuthenticationClient, ): DatabaseManager2 | null { @@ -92,6 +94,7 @@ function persistMarking({ .recordEvents([event]) .then(() => { console.log("[Performance] Log written", Date.now() / 1000.0); + WidgetReloadBridge.reloadTimelines(); }) .catch((error) => { console.error("Couldn't commit", reviewItem.task.id, error); @@ -304,6 +307,7 @@ export default function ReviewSession() { ]) .then(() => { console.log("Wrote delete event", Date.now() / 1000.0); + WidgetReloadBridge.reloadTimelines(); }) .catch((error) => { console.error("Couldn't commit delete event", error); diff --git a/packages/app/targets/widgets/AppIntents.swift b/packages/app/targets/widgets/AppIntents.swift deleted file mode 100644 index 1f32d7a1..00000000 --- a/packages/app/targets/widgets/AppIntents.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// AppIntents.swift -// OrbitWidgetExtension -// -// Created by Andy Matuschak on 2024-06-26. -// - -import Foundation -import AppIntents - -struct ConfigurationAppIntent: WidgetConfigurationIntent { - static var title: LocalizedStringResource = "Configuration" -} diff --git a/packages/app/targets/widgets/Widget.swift b/packages/app/targets/widgets/Widget.swift index 4e6cc8b9..359bcd31 100644 --- a/packages/app/targets/widgets/Widget.swift +++ b/packages/app/targets/widgets/Widget.swift @@ -27,38 +27,42 @@ struct ReviewState { var transientState = ReviewState() let bridge = Bridge() -struct Provider: AppIntentTimelineProvider { - func placeholder(in context: Context) -> Entry { - Entry(item: .placeholder, isShowingAnswer: false, date: Date()) - } - - func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> Entry { - Entry(item: .placeholder, isShowingAnswer: false, date: Date()) - } - - func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline { - var entries: [Entry] = [] - print("REFRESH TIMELINE") - - // transientState.queue = [.placeholder, .placeholder] - if transientState.queue == nil { - transientState.queue = await bridge.generateReviewQueue() +struct Provider: TimelineProvider { + func getSnapshot(in context: Context, completion: @escaping @Sendable (Entry) -> Void) { + Task { + let queue = await bridge.generateReviewQueue() + completion(Entry(item: queue[0], isShowingAnswer: false, date: Date())) } + } + + func getTimeline(in context: Context, completion: @escaping @Sendable (Timeline) -> Void) { + Task { + var entries: [Entry] = [] + print("REFRESH TIMELINE") + +// if transientState.queue == nil { + transientState.queue = await bridge.generateReviewQueue() +// } + + let queue = transientState.queue!.filter { item in !transientState.reviewedTaskIDs.contains(item.task.id) } + + let currentDate = Date() + for hourOffset in 0.. Entry { + Entry(item: .placeholder, isShowingAnswer: false, date: Date()) } } @@ -158,7 +162,7 @@ struct PromptTextStyle: ViewModifier { let fontStyle: OrbitFont.Type init(text: String) { self.fontStyle = switch text.count { - case 0 ... 80: + case 0 ... 70: OrbitPromptMedium.self default: OrbitPromptSmall.self @@ -295,7 +299,7 @@ struct OrbitHomeScreen: Widget { let kind: String = "OrbitHomeScreen" var body: some WidgetConfiguration { - AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in + StaticConfiguration(kind: kind, provider: Provider()) { entry in let colorPalette = colorPalette(for: entry.item) PromptView(item: entry.item, isShowingAnswer: entry.isShowingAnswer) .containerBackground(colorPalette.backgroundColor, for: .widget)