// @ts-check const fs = require("fs"); const path = require("path"); const { withDangerousMod } = require("@expo/config-plugins"); const widgetsPath = path.join("node_modules", "expo-widgets", "ios", "Widgets"); const liveActivityBannerSource = `import SwiftUI import WidgetKit import ActivityKit @available(iOS 18.0, *) struct LiveActivityBanner: View { @Environment(\\.activityFamily) var activityFamily var context: ActivityViewContext var nodes: [String: Any]? var body: some View { if let nodes, let node = nodes["banner"] as? [String: Any] { WidgetsDynamicView(name: context.activityID, kind: .liveActivity, node: node) } else { EmptyView() } } } `; function patchWidgetLiveActivitySwift(source) { const target = ` if #available(iOS 18.0, *) { LiveActivityBanner(context: context, nodes: nodes) } else if let node = nodes["banner"] as? [String: Any] { WidgetsDynamicView(name: context.activityID, kind: .liveActivity, node: node) } else { EmptyView() }`; const framedTarget = ` if #available(iOS 18.0, *) { LiveActivityBanner(context: context, nodes: nodes) } else if let node = nodes["banner"] as? [String: Any] { WidgetsDynamicView(name: context.activityID, kind: .liveActivity, node: node) .frame(maxWidth: .infinity, alignment: .leading) } else { EmptyView() }`; const hardcodedBanner = ` if #available(iOS 18.0, *) { LiveActivityBanner(context: context, nodes: nodes) } else { TimeClockNativeActivityBanner(context: context) }`; if (source.includes(target)) { return source; } return source.replace(framedTarget, target).replace(hardcodedBanner, target); } /** @type {import('@expo/config-plugins').ConfigPlugin} */ function withLiveActivityBannerFrame(config) { return withDangerousMod(config, [ "ios", async (config) => { const sourceDir = path.join(config.modRequest.projectRoot, widgetsPath); const bannerPath = path.join(sourceDir, "LiveActivityBanner.swift"); const activityPath = path.join(sourceDir, "WidgetLiveActivity.swift"); if (!fs.existsSync(bannerPath) || !fs.existsSync(activityPath)) { throw new Error(`Could not find expo-widgets live activity sources in ${sourceDir}`); } fs.writeFileSync(bannerPath, liveActivityBannerSource); const activitySource = fs.readFileSync(activityPath, "utf8"); const patchedActivitySource = patchWidgetLiveActivitySwift(activitySource); if (patchedActivitySource !== activitySource) { fs.writeFileSync(activityPath, patchedActivitySource); } return config; }, ]); } module.exports = withLiveActivityBannerFrame;