Skip to content

Commit

Permalink
Refresh widget when answering questions in the app
Browse files Browse the repository at this point in the history
  • Loading branch information
andymatuschak committed Jul 12, 2024
1 parent 3ef0d4c commit 47a5685
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 47 deletions.
1 change: 0 additions & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified bun.lockb
Binary file not shown.
8 changes: 8 additions & 0 deletions packages/app/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@
]
}
],
[
"expo-build-properties",
{
"ios": {
"deploymentTarget": "17.0"
}
}
],
"sentry-expo",
[
"expo-router",
Expand Down
17 changes: 17 additions & 0 deletions packages/app/expoPlugin/ios/ORWidgetReloadBridge.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// ORWidgetReloadBridge.m
// Orbit
//
// Created by Andy Matuschak on 2024-07-12.
//

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCT_EXTERN_MODULE(WidgetReloadBridge, NSObject)
RCT_EXTERN_METHOD(reloadTimelines)
@end

NS_ASSUME_NONNULL_END
16 changes: 16 additions & 0 deletions packages/app/expoPlugin/ios/ORWidgetReloadBridge.swift
Original file line number Diff line number Diff line change
@@ -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")
}
}
13 changes: 12 additions & 1 deletion packages/app/expoPlugin/withWidgetPluginFixes.js
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion packages/app/src/reviewSession/ReviewSession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 0 additions & 13 deletions packages/app/targets/widgets/AppIntents.swift

This file was deleted.

66 changes: 35 additions & 31 deletions packages/app/targets/widgets/Widget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Entry> {
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<Entry>) -> 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..<min(50, queue.count) {
let entryDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
let entry = Entry(
item: queue[hourOffset],
isShowingAnswer: transientState.isShowingAnswer,
date: entryDate
)
entries.append(entry)
}

let queue = transientState.queue!.filter { item in !transientState.reviewedTaskIDs.contains(item.task.id) }

let currentDate = Date()
for hourOffset in 0..<min(50, queue.count) {
let entryDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
let entry = Entry(
item: queue[hourOffset],
isShowingAnswer: transientState.isShowingAnswer,
date: entryDate
)
entries.append(entry)
completion(Timeline(entries: entries, policy: .after(Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!)))
}

return Timeline(entries: entries, policy: .after(Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!))
}

func placeholder(in context: Context) -> Entry {
Entry(item: .placeholder, isShowingAnswer: false, date: Date())
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 47a5685

Please sign in to comment.