nn-widgets 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +577 -0
- package/android/build.gradle +90 -0
- package/app.plugin.js +4 -0
- package/build/NNWidgets.types.d.ts +113 -0
- package/build/NNWidgets.types.d.ts.map +1 -0
- package/build/NNWidgets.types.js +2 -0
- package/build/NNWidgets.types.js.map +1 -0
- package/build/NNWidgetsModule.d.ts +3 -0
- package/build/NNWidgetsModule.d.ts.map +1 -0
- package/build/NNWidgetsModule.js +3 -0
- package/build/NNWidgetsModule.js.map +1 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +152 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/NNWidgets.podspec +27 -0
- package/ios/NNWidgetsModule.swift +97 -0
- package/package.json +49 -0
- package/plugin/build/index.d.ts +9 -0
- package/plugin/build/index.d.ts.map +1 -0
- package/plugin/build/index.js +70 -0
- package/plugin/build/types.d.ts +353 -0
- package/plugin/build/types.d.ts.map +1 -0
- package/plugin/build/types.js +2 -0
- package/plugin/build/withAndroidWidget.d.ts +5 -0
- package/plugin/build/withAndroidWidget.d.ts.map +1 -0
- package/plugin/build/withAndroidWidget.js +700 -0
- package/plugin/build/withIosWidget.d.ts +6 -0
- package/plugin/build/withIosWidget.d.ts.map +1 -0
- package/plugin/build/withIosWidget.js +1589 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/publish.sh +59 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget data that can be shared between app and widget (legacy flat format)
|
|
3
|
+
*/
|
|
4
|
+
export interface WidgetData {
|
|
5
|
+
[key: string]: string | number | boolean | null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for updating widget
|
|
9
|
+
*/
|
|
10
|
+
export interface WidgetUpdateConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Widget kind identifier (iOS) or widget class name (Android)
|
|
13
|
+
*/
|
|
14
|
+
widgetName?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* System font size keys (maps to SwiftUI Font / Android sp).
|
|
18
|
+
*/
|
|
19
|
+
export type WidgetFontSize = "caption2" | "caption" | "footnote" | "subheadline" | "body" | "headline" | "title3" | "title2" | "title";
|
|
20
|
+
/**
|
|
21
|
+
* Font weight keys.
|
|
22
|
+
*/
|
|
23
|
+
export type WidgetFontWeight = "regular" | "medium" | "semibold" | "bold";
|
|
24
|
+
/**
|
|
25
|
+
* Icon configuration for a widget item.
|
|
26
|
+
* - string: SF Symbol name (iOS) or drawable resource name (Android)
|
|
27
|
+
* - object: icon with extra styling
|
|
28
|
+
*/
|
|
29
|
+
export type WidgetItemIcon = string | {
|
|
30
|
+
/** Icon name: SF Symbol (iOS), drawable name (Android), or bundled icon name */
|
|
31
|
+
url: string;
|
|
32
|
+
/** Icon size in points/dp @default 32 */
|
|
33
|
+
size?: number;
|
|
34
|
+
/** Border radius (0 = square, size/2 = circle) @default 0 */
|
|
35
|
+
radius?: number;
|
|
36
|
+
/** Background color behind the icon (hex) */
|
|
37
|
+
backgroundColor?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Text configuration for a widget item.
|
|
41
|
+
* - string: plain text
|
|
42
|
+
* - object: text with custom styling
|
|
43
|
+
*/
|
|
44
|
+
export type WidgetItemText = string | {
|
|
45
|
+
/** The text content */
|
|
46
|
+
text: string;
|
|
47
|
+
/** Text color (hex). Falls back to widget-level style. */
|
|
48
|
+
color?: string;
|
|
49
|
+
/** Font size key */
|
|
50
|
+
fontSize?: WidgetFontSize;
|
|
51
|
+
/** Font weight */
|
|
52
|
+
fontWeight?: WidgetFontWeight;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A single item in a list-type widget.
|
|
56
|
+
*/
|
|
57
|
+
export interface WidgetListItem {
|
|
58
|
+
/** Left icon (SF Symbol name, bundled icon, or object with styling) */
|
|
59
|
+
icon?: WidgetItemIcon;
|
|
60
|
+
/** Right icon (SF Symbol name, bundled icon, or object with styling) */
|
|
61
|
+
rightIcon?: WidgetItemIcon;
|
|
62
|
+
/** Title text (string or styled object) */
|
|
63
|
+
title: WidgetItemText;
|
|
64
|
+
/** Description text (string or styled object) */
|
|
65
|
+
description?: WidgetItemText;
|
|
66
|
+
/** Per-item deep link URL */
|
|
67
|
+
deepLink?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Structured data payload for `updateWidget()`.
|
|
71
|
+
*/
|
|
72
|
+
export interface WidgetDataPayload {
|
|
73
|
+
/** Title text (for single/list fallback) */
|
|
74
|
+
title?: string;
|
|
75
|
+
/** Subtitle text (for single/list fallback) */
|
|
76
|
+
subtitle?: string;
|
|
77
|
+
/** Numeric value (for single-type widgets) */
|
|
78
|
+
value?: number;
|
|
79
|
+
/** List items (for list-type widgets) */
|
|
80
|
+
items?: WidgetListItem[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Widget module interface
|
|
84
|
+
*/
|
|
85
|
+
export interface NNWidgetsModuleType {
|
|
86
|
+
/**
|
|
87
|
+
* Set shared data that widgets can read (legacy flat key-value format).
|
|
88
|
+
* Keys are stored with `widget_` prefix in UserDefaults/SharedPreferences.
|
|
89
|
+
*/
|
|
90
|
+
setWidgetData(data: WidgetData): Promise<boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* Get current shared widget data (legacy format)
|
|
93
|
+
*/
|
|
94
|
+
getWidgetData(): Promise<WidgetData>;
|
|
95
|
+
/**
|
|
96
|
+
* Update a specific widget with structured data.
|
|
97
|
+
* Automatically serializes items to JSON and reloads the widget timeline.
|
|
98
|
+
*
|
|
99
|
+
* @param widgetName - The widget kind identifier (matches `name` in app.json config)
|
|
100
|
+
* @param data - Structured widget data payload
|
|
101
|
+
* @returns Promise<boolean> - Success status
|
|
102
|
+
*/
|
|
103
|
+
updateWidget(widgetName: string, data: WidgetDataPayload): Promise<boolean>;
|
|
104
|
+
/**
|
|
105
|
+
* Request widget to reload/update its timeline (iOS) or update (Android)
|
|
106
|
+
*/
|
|
107
|
+
reloadWidget(config?: WidgetUpdateConfig): Promise<boolean>;
|
|
108
|
+
/**
|
|
109
|
+
* Check if widgets are supported on this device
|
|
110
|
+
*/
|
|
111
|
+
isSupported(): boolean;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=NNWidgets.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NNWidgets.types.d.ts","sourceRoot":"","sources":["../src/NNWidgets.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,SAAS,GACT,UAAU,GACV,aAAa,GACb,MAAM,GACN,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,OAAO,CAAC;AAEZ;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1E;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN;IACE,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN;IACE,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,kBAAkB;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,wEAAwE;IACxE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,2CAA2C;IAC3C,KAAK,EAAE,cAAc,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAElD;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAErC;;;;;;;OAOG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE5E;;OAEG;IACH,YAAY,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE5D;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NNWidgets.types.js","sourceRoot":"","sources":["../src/NNWidgets.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Widget data that can be shared between app and widget (legacy flat format)\n */\nexport interface WidgetData {\n [key: string]: string | number | boolean | null;\n}\n\n/**\n * Configuration for updating widget\n */\nexport interface WidgetUpdateConfig {\n /**\n * Widget kind identifier (iOS) or widget class name (Android)\n */\n widgetName?: string;\n}\n\n// ──────────────────────────────────────────────\n// Rich widget data types (for list/single widgets)\n// ──────────────────────────────────────────────\n\n/**\n * System font size keys (maps to SwiftUI Font / Android sp).\n */\nexport type WidgetFontSize =\n | \"caption2\"\n | \"caption\"\n | \"footnote\"\n | \"subheadline\"\n | \"body\"\n | \"headline\"\n | \"title3\"\n | \"title2\"\n | \"title\";\n\n/**\n * Font weight keys.\n */\nexport type WidgetFontWeight = \"regular\" | \"medium\" | \"semibold\" | \"bold\";\n\n/**\n * Icon configuration for a widget item.\n * - string: SF Symbol name (iOS) or drawable resource name (Android)\n * - object: icon with extra styling\n */\nexport type WidgetItemIcon =\n | string\n | {\n /** Icon name: SF Symbol (iOS), drawable name (Android), or bundled icon name */\n url: string;\n /** Icon size in points/dp @default 32 */\n size?: number;\n /** Border radius (0 = square, size/2 = circle) @default 0 */\n radius?: number;\n /** Background color behind the icon (hex) */\n backgroundColor?: string;\n };\n\n/**\n * Text configuration for a widget item.\n * - string: plain text\n * - object: text with custom styling\n */\nexport type WidgetItemText =\n | string\n | {\n /** The text content */\n text: string;\n /** Text color (hex). Falls back to widget-level style. */\n color?: string;\n /** Font size key */\n fontSize?: WidgetFontSize;\n /** Font weight */\n fontWeight?: WidgetFontWeight;\n };\n\n/**\n * A single item in a list-type widget.\n */\nexport interface WidgetListItem {\n /** Left icon (SF Symbol name, bundled icon, or object with styling) */\n icon?: WidgetItemIcon;\n /** Right icon (SF Symbol name, bundled icon, or object with styling) */\n rightIcon?: WidgetItemIcon;\n /** Title text (string or styled object) */\n title: WidgetItemText;\n /** Description text (string or styled object) */\n description?: WidgetItemText;\n /** Per-item deep link URL */\n deepLink?: string;\n}\n\n/**\n * Structured data payload for `updateWidget()`.\n */\nexport interface WidgetDataPayload {\n /** Title text (for single/list fallback) */\n title?: string;\n /** Subtitle text (for single/list fallback) */\n subtitle?: string;\n /** Numeric value (for single-type widgets) */\n value?: number;\n /** List items (for list-type widgets) */\n items?: WidgetListItem[];\n}\n\n/**\n * Widget module interface\n */\nexport interface NNWidgetsModuleType {\n /**\n * Set shared data that widgets can read (legacy flat key-value format).\n * Keys are stored with `widget_` prefix in UserDefaults/SharedPreferences.\n */\n setWidgetData(data: WidgetData): Promise<boolean>;\n\n /**\n * Get current shared widget data (legacy format)\n */\n getWidgetData(): Promise<WidgetData>;\n\n /**\n * Update a specific widget with structured data.\n * Automatically serializes items to JSON and reloads the widget timeline.\n *\n * @param widgetName - The widget kind identifier (matches `name` in app.json config)\n * @param data - Structured widget data payload\n * @returns Promise<boolean> - Success status\n */\n updateWidget(widgetName: string, data: WidgetDataPayload): Promise<boolean>;\n\n /**\n * Request widget to reload/update its timeline (iOS) or update (Android)\n */\n reloadWidget(config?: WidgetUpdateConfig): Promise<boolean>;\n\n /**\n * Check if widgets are supported on this device\n */\n isSupported(): boolean;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NNWidgetsModule.d.ts","sourceRoot":"","sources":["../src/NNWidgetsModule.ts"],"names":[],"mappings":";AACA,wBAAgD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NNWidgetsModule.js","sourceRoot":"","sources":["../src/NNWidgetsModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,eAAe,mBAAmB,CAAC,WAAW,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from \"expo-modules-core\";\nexport default requireNativeModule(\"NNWidgets\");\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WidgetData, WidgetDataPayload, WidgetListItem, WidgetItemIcon, WidgetItemText, WidgetUpdateConfig, NNWidgetsModuleType } from "./NNWidgets.types";
|
|
2
|
+
export type { WidgetData, WidgetDataPayload, WidgetListItem, WidgetItemIcon, WidgetItemText, WidgetUpdateConfig, NNWidgetsModuleType, };
|
|
3
|
+
/**
|
|
4
|
+
* NNWidgets - Native widget integration for React Native / Expo
|
|
5
|
+
*
|
|
6
|
+
* This module provides APIs to communicate between your React Native app
|
|
7
|
+
* and native widgets (iOS WidgetKit & Android App Widgets).
|
|
8
|
+
*/
|
|
9
|
+
export declare const NNWidgets: NNWidgetsModuleType;
|
|
10
|
+
export default NNWidgets;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,mBAAmB,GACpB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,mBAqJvB,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import NNWidgetsModule from "./NNWidgetsModule";
|
|
2
|
+
/**
|
|
3
|
+
* NNWidgets - Native widget integration for React Native / Expo
|
|
4
|
+
*
|
|
5
|
+
* This module provides APIs to communicate between your React Native app
|
|
6
|
+
* and native widgets (iOS WidgetKit & Android App Widgets).
|
|
7
|
+
*/
|
|
8
|
+
export const NNWidgets = {
|
|
9
|
+
/**
|
|
10
|
+
* Set shared data that widgets can read (legacy flat key-value format).
|
|
11
|
+
* Data is stored in App Groups (iOS) or SharedPreferences (Android).
|
|
12
|
+
*
|
|
13
|
+
* @param data - Key-value pairs to share with widget
|
|
14
|
+
* @returns Promise<boolean> - Success status
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* await NNWidgets.setWidgetData({
|
|
19
|
+
* userName: 'John',
|
|
20
|
+
* points: 100,
|
|
21
|
+
* isLoggedIn: true
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
async setWidgetData(data) {
|
|
26
|
+
try {
|
|
27
|
+
return await NNWidgetsModule.setWidgetData(data);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.error("[NNWidgets] Failed to set widget data:", error);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Get current shared widget data.
|
|
36
|
+
*
|
|
37
|
+
* @returns Promise<WidgetData> - Current shared data
|
|
38
|
+
*/
|
|
39
|
+
async getWidgetData() {
|
|
40
|
+
try {
|
|
41
|
+
return await NNWidgetsModule.getWidgetData();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error("[NNWidgets] Failed to get widget data:", error);
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Update a specific widget with structured data.
|
|
50
|
+
* Automatically serializes items to JSON, stores data with proper key prefixes,
|
|
51
|
+
* and reloads the widget timeline.
|
|
52
|
+
*
|
|
53
|
+
* @param widgetName - The widget kind identifier (matches `name` in app.json widgets config)
|
|
54
|
+
* @param data - Structured widget data payload
|
|
55
|
+
* @returns Promise<boolean> - Success status
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* // Update a list-type widget
|
|
60
|
+
* await NNWidgets.updateWidget('SessionsWidget', {
|
|
61
|
+
* items: [
|
|
62
|
+
* {
|
|
63
|
+
* icon: 'brainstorm',
|
|
64
|
+
* title: { text: 'Morning Focus', color: '#1E1E2E', fontWeight: 'bold' },
|
|
65
|
+
* description: '25 min session',
|
|
66
|
+
* deepLink: 'myapp://session/123',
|
|
67
|
+
* },
|
|
68
|
+
* {
|
|
69
|
+
* icon: { url: 'meditation', size: 28, radius: 14 },
|
|
70
|
+
* title: 'Evening Wind Down',
|
|
71
|
+
* description: { text: '15 min', color: '#888888' },
|
|
72
|
+
* },
|
|
73
|
+
* ],
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* // Update a single-type widget
|
|
77
|
+
* await NNWidgets.updateWidget('StatsWidget', {
|
|
78
|
+
* title: 'Focus Time',
|
|
79
|
+
* subtitle: 'This week',
|
|
80
|
+
* value: 142,
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
async updateWidget(widgetName, data) {
|
|
85
|
+
try {
|
|
86
|
+
const flatData = {};
|
|
87
|
+
if (data.title !== undefined) {
|
|
88
|
+
flatData[`${widgetName}_title`] = data.title;
|
|
89
|
+
}
|
|
90
|
+
if (data.subtitle !== undefined) {
|
|
91
|
+
flatData[`${widgetName}_subtitle`] = data.subtitle;
|
|
92
|
+
}
|
|
93
|
+
if (data.value !== undefined) {
|
|
94
|
+
flatData[`${widgetName}_value`] = data.value;
|
|
95
|
+
}
|
|
96
|
+
if (data.items !== undefined) {
|
|
97
|
+
// Serialize items array to JSON string for native storage
|
|
98
|
+
flatData[`${widgetName}_items`] = JSON.stringify(data.items);
|
|
99
|
+
}
|
|
100
|
+
const success = await NNWidgetsModule.setWidgetData(flatData);
|
|
101
|
+
if (success) {
|
|
102
|
+
await NNWidgetsModule.reloadWidget(widgetName);
|
|
103
|
+
}
|
|
104
|
+
return success;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error(`[NNWidgets] Failed to update widget "${widgetName}":`, error);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Request widget to reload/update.
|
|
113
|
+
* - iOS: Reloads widget timeline
|
|
114
|
+
* - Android: Triggers widget update broadcast
|
|
115
|
+
*
|
|
116
|
+
* @param config - Optional configuration
|
|
117
|
+
* @returns Promise<boolean> - Success status
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* // Update all widgets
|
|
122
|
+
* await NNWidgets.reloadWidget();
|
|
123
|
+
*
|
|
124
|
+
* // Update specific widget
|
|
125
|
+
* await NNWidgets.reloadWidget({ widgetName: 'MyWidget' });
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
async reloadWidget(config) {
|
|
129
|
+
try {
|
|
130
|
+
return await NNWidgetsModule.reloadWidget(config?.widgetName || null);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error("[NNWidgets] Failed to reload widget:", error);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* Check if widgets are supported on this device.
|
|
139
|
+
*
|
|
140
|
+
* @returns boolean - True if widgets are supported
|
|
141
|
+
*/
|
|
142
|
+
isSupported() {
|
|
143
|
+
try {
|
|
144
|
+
return NNWidgetsModule.isSupported();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
export default NNWidgets;
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAqBhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAwB;IAC5C;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,aAAa,CAAC,IAAgB;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,eAAe,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;YAC/D,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC;YACH,OAAO,MAAM,eAAe,CAAC,aAAa,EAAE,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;YAC/D,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,KAAK,CAAC,YAAY,CAChB,UAAkB,EAClB,IAAuB;QAEvB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAe,EAAE,CAAC;YAEhC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,QAAQ,CAAC,GAAG,UAAU,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;YAC/C,CAAC;YACD,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAChC,QAAQ,CAAC,GAAG,UAAU,WAAW,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;YACrD,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,QAAQ,CAAC,GAAG,UAAU,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;YAC/C,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC7B,0DAA0D;gBAC1D,QAAQ,CAAC,GAAG,UAAU,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/D,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC9D,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,eAAe,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,wCAAwC,UAAU,IAAI,EACtD,KAAK,CACN,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,YAAY,CAAC,MAA2B;QAC5C,IAAI,CAAC;YACH,OAAO,MAAM,eAAe,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,IAAI,IAAI,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;YAC7D,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,IAAI,CAAC;YACH,OAAO,eAAe,CAAC,WAAW,EAAE,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF,CAAC;AAEF,eAAe,SAAS,CAAC","sourcesContent":["import NNWidgetsModule from \"./NNWidgetsModule\";\nimport type {\n WidgetData,\n WidgetDataPayload,\n WidgetListItem,\n WidgetItemIcon,\n WidgetItemText,\n WidgetUpdateConfig,\n NNWidgetsModuleType,\n} from \"./NNWidgets.types\";\n\nexport type {\n WidgetData,\n WidgetDataPayload,\n WidgetListItem,\n WidgetItemIcon,\n WidgetItemText,\n WidgetUpdateConfig,\n NNWidgetsModuleType,\n};\n\n/**\n * NNWidgets - Native widget integration for React Native / Expo\n *\n * This module provides APIs to communicate between your React Native app\n * and native widgets (iOS WidgetKit & Android App Widgets).\n */\nexport const NNWidgets: NNWidgetsModuleType = {\n /**\n * Set shared data that widgets can read (legacy flat key-value format).\n * Data is stored in App Groups (iOS) or SharedPreferences (Android).\n *\n * @param data - Key-value pairs to share with widget\n * @returns Promise<boolean> - Success status\n *\n * @example\n * ```ts\n * await NNWidgets.setWidgetData({\n * userName: 'John',\n * points: 100,\n * isLoggedIn: true\n * });\n * ```\n */\n async setWidgetData(data: WidgetData): Promise<boolean> {\n try {\n return await NNWidgetsModule.setWidgetData(data);\n } catch (error) {\n console.error(\"[NNWidgets] Failed to set widget data:\", error);\n return false;\n }\n },\n\n /**\n * Get current shared widget data.\n *\n * @returns Promise<WidgetData> - Current shared data\n */\n async getWidgetData(): Promise<WidgetData> {\n try {\n return await NNWidgetsModule.getWidgetData();\n } catch (error) {\n console.error(\"[NNWidgets] Failed to get widget data:\", error);\n return {};\n }\n },\n\n /**\n * Update a specific widget with structured data.\n * Automatically serializes items to JSON, stores data with proper key prefixes,\n * and reloads the widget timeline.\n *\n * @param widgetName - The widget kind identifier (matches `name` in app.json widgets config)\n * @param data - Structured widget data payload\n * @returns Promise<boolean> - Success status\n *\n * @example\n * ```ts\n * // Update a list-type widget\n * await NNWidgets.updateWidget('SessionsWidget', {\n * items: [\n * {\n * icon: 'brainstorm',\n * title: { text: 'Morning Focus', color: '#1E1E2E', fontWeight: 'bold' },\n * description: '25 min session',\n * deepLink: 'myapp://session/123',\n * },\n * {\n * icon: { url: 'meditation', size: 28, radius: 14 },\n * title: 'Evening Wind Down',\n * description: { text: '15 min', color: '#888888' },\n * },\n * ],\n * });\n *\n * // Update a single-type widget\n * await NNWidgets.updateWidget('StatsWidget', {\n * title: 'Focus Time',\n * subtitle: 'This week',\n * value: 142,\n * });\n * ```\n */\n async updateWidget(\n widgetName: string,\n data: WidgetDataPayload,\n ): Promise<boolean> {\n try {\n const flatData: WidgetData = {};\n\n if (data.title !== undefined) {\n flatData[`${widgetName}_title`] = data.title;\n }\n if (data.subtitle !== undefined) {\n flatData[`${widgetName}_subtitle`] = data.subtitle;\n }\n if (data.value !== undefined) {\n flatData[`${widgetName}_value`] = data.value;\n }\n if (data.items !== undefined) {\n // Serialize items array to JSON string for native storage\n flatData[`${widgetName}_items`] = JSON.stringify(data.items);\n }\n\n const success = await NNWidgetsModule.setWidgetData(flatData);\n if (success) {\n await NNWidgetsModule.reloadWidget(widgetName);\n }\n return success;\n } catch (error) {\n console.error(\n `[NNWidgets] Failed to update widget \"${widgetName}\":`,\n error,\n );\n return false;\n }\n },\n\n /**\n * Request widget to reload/update.\n * - iOS: Reloads widget timeline\n * - Android: Triggers widget update broadcast\n *\n * @param config - Optional configuration\n * @returns Promise<boolean> - Success status\n *\n * @example\n * ```ts\n * // Update all widgets\n * await NNWidgets.reloadWidget();\n *\n * // Update specific widget\n * await NNWidgets.reloadWidget({ widgetName: 'MyWidget' });\n * ```\n */\n async reloadWidget(config?: WidgetUpdateConfig): Promise<boolean> {\n try {\n return await NNWidgetsModule.reloadWidget(config?.widgetName || null);\n } catch (error) {\n console.error(\"[NNWidgets] Failed to reload widget:\", error);\n return false;\n }\n },\n\n /**\n * Check if widgets are supported on this device.\n *\n * @returns boolean - True if widgets are supported\n */\n isSupported(): boolean {\n try {\n return NNWidgetsModule.isSupported();\n } catch (error) {\n return false;\n }\n },\n};\n\nexport default NNWidgets;\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'NNWidgets'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = { :ios => '15.1' }
|
|
14
|
+
s.swift_version = '5.9'
|
|
15
|
+
s.source = { git: 'https://github.com/NamReplIT/expo-utils' }
|
|
16
|
+
s.static_framework = true
|
|
17
|
+
|
|
18
|
+
s.dependency 'ExpoModulesCore'
|
|
19
|
+
|
|
20
|
+
# Swift/Objective-C compatibility
|
|
21
|
+
s.pod_target_xcconfig = {
|
|
22
|
+
'DEFINES_MODULE' => 'YES',
|
|
23
|
+
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
s.source_files = "**/*.{h,m,swift}"
|
|
27
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import WidgetKit
|
|
3
|
+
|
|
4
|
+
public class NNWidgetsModule: Module {
|
|
5
|
+
|
|
6
|
+
// App Group identifier - will be set via plugin config
|
|
7
|
+
private var appGroupIdentifier: String {
|
|
8
|
+
// Try to get from Info.plist or use default pattern
|
|
9
|
+
if let identifier = Bundle.main.object(forInfoDictionaryKey: "NNWidgetsAppGroup") as? String {
|
|
10
|
+
return identifier
|
|
11
|
+
}
|
|
12
|
+
// Fallback: derive from bundle identifier
|
|
13
|
+
if let bundleId = Bundle.main.bundleIdentifier {
|
|
14
|
+
return "group.\(bundleId)"
|
|
15
|
+
}
|
|
16
|
+
return "group.app.widget"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private var userDefaults: UserDefaults? {
|
|
20
|
+
return UserDefaults(suiteName: appGroupIdentifier)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public func definition() -> ModuleDefinition {
|
|
24
|
+
Name("NNWidgets")
|
|
25
|
+
|
|
26
|
+
// Check if widgets are supported (iOS 14+)
|
|
27
|
+
Function("isSupported") { () -> Bool in
|
|
28
|
+
if #available(iOS 14.0, *) {
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Set widget data to shared UserDefaults
|
|
35
|
+
AsyncFunction("setWidgetData") { (data: [String: Any]) -> Bool in
|
|
36
|
+
guard let defaults = self.userDefaults else {
|
|
37
|
+
print("[NNWidgets] Failed to access App Group UserDefaults")
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Store each key-value pair
|
|
42
|
+
for (key, value) in data {
|
|
43
|
+
defaults.set(value, forKey: "widget_\(key)")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Store keys list for retrieval
|
|
47
|
+
let keys = data.keys.map { "widget_\($0)" }
|
|
48
|
+
defaults.set(keys, forKey: "widget_data_keys")
|
|
49
|
+
|
|
50
|
+
defaults.synchronize()
|
|
51
|
+
print("[NNWidgets] Widget data saved: \(data.keys.joined(separator: ", "))")
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Get widget data from shared UserDefaults
|
|
56
|
+
AsyncFunction("getWidgetData") { () -> [String: Any] in
|
|
57
|
+
guard let defaults = self.userDefaults else {
|
|
58
|
+
print("[NNWidgets] Failed to access App Group UserDefaults")
|
|
59
|
+
return [:]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
guard let keys = defaults.array(forKey: "widget_data_keys") as? [String] else {
|
|
63
|
+
return [:]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var result: [String: Any] = [:]
|
|
67
|
+
for key in keys {
|
|
68
|
+
if let value = defaults.object(forKey: key) {
|
|
69
|
+
// Remove "widget_" prefix from key
|
|
70
|
+
let cleanKey = String(key.dropFirst(7))
|
|
71
|
+
result[cleanKey] = value
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Reload widget timeline
|
|
79
|
+
AsyncFunction("reloadWidget") { (widgetKind: String?) -> Bool in
|
|
80
|
+
if #available(iOS 14.0, *) {
|
|
81
|
+
DispatchQueue.main.async {
|
|
82
|
+
if let kind = widgetKind {
|
|
83
|
+
// Reload specific widget
|
|
84
|
+
WidgetCenter.shared.reloadTimelines(ofKind: kind)
|
|
85
|
+
print("[NNWidgets] Reloaded widget: \(kind)")
|
|
86
|
+
} else {
|
|
87
|
+
// Reload all widgets
|
|
88
|
+
WidgetCenter.shared.reloadAllTimelines()
|
|
89
|
+
print("[NNWidgets] Reloaded all widgets")
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nn-widgets",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Expo config plugin for adding native widgets (iOS WidgetKit & Android App Widgets)",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "expo-module build && npm run build:plugin",
|
|
9
|
+
"build:plugin": "npx tsc -p plugin/tsconfig.json",
|
|
10
|
+
"clean": "expo-module clean && rm -rf plugin/build",
|
|
11
|
+
"lint": "expo-module lint",
|
|
12
|
+
"test": "expo-module test",
|
|
13
|
+
"prepare": "expo-module prepare",
|
|
14
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
15
|
+
"expo-module": "expo-module"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"react-native",
|
|
19
|
+
"expo",
|
|
20
|
+
"expo-plugin",
|
|
21
|
+
"widget",
|
|
22
|
+
"ios-widget",
|
|
23
|
+
"android-widget",
|
|
24
|
+
"widgetkit",
|
|
25
|
+
"app-widget"
|
|
26
|
+
],
|
|
27
|
+
"repository": "https://github.com/NamReplIT/expo-utils",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/NamReplIT/expo-utils/issues"
|
|
30
|
+
},
|
|
31
|
+
"author": "NamReplIT <89992703+NamReplIT@users.noreply.github.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"homepage": "https://github.com/NamReplIT/expo-utils#readme",
|
|
34
|
+
"dependencies": {},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@expo/config-plugins": "^9.0.0",
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"expo-module-scripts": "^4.0.5",
|
|
39
|
+
"expo-modules-core": "^2.5.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@types/react": "*",
|
|
44
|
+
"@types/react-native": "*",
|
|
45
|
+
"expo": "*",
|
|
46
|
+
"react": "*",
|
|
47
|
+
"react-native": "*"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ConfigPlugin } from "@expo/config-plugins";
|
|
2
|
+
import { withIosWidget } from "./withIosWidget";
|
|
3
|
+
import { withAndroidWidget } from "./withAndroidWidget";
|
|
4
|
+
import type { NNWidgetsPluginProps, WidgetConfig, ResolvedPluginProps, ResolvedWidgetConfig } from "./types";
|
|
5
|
+
export type { NNWidgetsPluginProps, WidgetConfig, ResolvedPluginProps, ResolvedWidgetConfig, };
|
|
6
|
+
export { withIosWidget, withAndroidWidget };
|
|
7
|
+
declare const _default: ConfigPlugin<void | NNWidgetsPluginProps>;
|
|
8
|
+
export default _default;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,GACrB,CAAC;AACF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC;;AAyE5C,wBAAyE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withAndroidWidget = exports.withIosWidget = void 0;
|
|
4
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
|
+
const withIosWidget_1 = require("./withIosWidget");
|
|
6
|
+
Object.defineProperty(exports, "withIosWidget", { enumerable: true, get: function () { return withIosWidget_1.withIosWidget; } });
|
|
7
|
+
const withAndroidWidget_1 = require("./withAndroidWidget");
|
|
8
|
+
Object.defineProperty(exports, "withAndroidWidget", { enumerable: true, get: function () { return withAndroidWidget_1.withAndroidWidget; } });
|
|
9
|
+
/**
|
|
10
|
+
* nn-widgets Expo Config Plugin
|
|
11
|
+
*
|
|
12
|
+
* Adds native widget support for iOS (WidgetKit) and Android (App Widgets)
|
|
13
|
+
* to your Expo/React Native app.
|
|
14
|
+
*
|
|
15
|
+
* Supports multiple independent widgets per app.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```js
|
|
19
|
+
* // app.json or app.config.js
|
|
20
|
+
* {
|
|
21
|
+
* "expo": {
|
|
22
|
+
* "plugins": [
|
|
23
|
+
* ["nn-widgets", {
|
|
24
|
+
* "widgets": [
|
|
25
|
+
* {
|
|
26
|
+
* "name": "MainWidget",
|
|
27
|
+
* "displayName": "My App",
|
|
28
|
+
* "description": "Quick access",
|
|
29
|
+
* "deepLinkUrl": "myapp://widget/home",
|
|
30
|
+
* "widgetFamilies": ["systemSmall", "systemMedium"]
|
|
31
|
+
* },
|
|
32
|
+
* {
|
|
33
|
+
* "name": "StatsWidget",
|
|
34
|
+
* "displayName": "Stats",
|
|
35
|
+
* "description": "View your stats",
|
|
36
|
+
* "deepLinkUrl": "myapp://widget/stats",
|
|
37
|
+
* "widgetFamilies": ["systemSmall"]
|
|
38
|
+
* }
|
|
39
|
+
* ],
|
|
40
|
+
* "ios": {
|
|
41
|
+
* "deploymentTarget": "17.0",
|
|
42
|
+
* "devTeam": "YOUR_TEAM_ID"
|
|
43
|
+
* },
|
|
44
|
+
* "android": {
|
|
45
|
+
* "minWidth": 110,
|
|
46
|
+
* "minHeight": 40
|
|
47
|
+
* }
|
|
48
|
+
* }]
|
|
49
|
+
* ]
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
const withNNWidgets = (config, props = {}) => {
|
|
55
|
+
const pluginProps = props || {};
|
|
56
|
+
const widgetCount = pluginProps.widgets?.length || 1;
|
|
57
|
+
console.log(`[nn-widgets] Configuring ${widgetCount} native widget(s)...`);
|
|
58
|
+
// Apply iOS widget configuration
|
|
59
|
+
config = (0, withIosWidget_1.withIosWidget)(config, pluginProps);
|
|
60
|
+
// Apply Android widget configuration
|
|
61
|
+
config = (0, withAndroidWidget_1.withAndroidWidget)(config, pluginProps);
|
|
62
|
+
console.log("[nn-widgets] Widget configuration complete!");
|
|
63
|
+
return config;
|
|
64
|
+
};
|
|
65
|
+
// Package info for createRunOncePlugin
|
|
66
|
+
const pkg = {
|
|
67
|
+
name: "nn-widgets",
|
|
68
|
+
version: "0.2.0",
|
|
69
|
+
};
|
|
70
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withNNWidgets, pkg.name, pkg.version);
|