@symbiote-native/contacts 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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/build/angular/contact-access-button/index.d.ts +18 -0
  4. package/build/angular/contact-access-button/index.js +123 -0
  5. package/build/angular/index.d.ts +2 -0
  6. package/build/angular/index.js +3 -0
  7. package/build/core/contact-access-button.d.ts +29 -0
  8. package/build/core/contact-access-button.js +27 -0
  9. package/build/core/contact.d.ts +3 -0
  10. package/build/core/contact.js +3 -0
  11. package/build/core/container.d.ts +5 -0
  12. package/build/core/container.js +5 -0
  13. package/build/core/enums.d.ts +53 -0
  14. package/build/core/enums.js +56 -0
  15. package/build/core/group.d.ts +5 -0
  16. package/build/core/group.js +4 -0
  17. package/build/core/index.d.ts +8 -0
  18. package/build/core/index.js +7 -0
  19. package/build/core/listeners.d.ts +3 -0
  20. package/build/core/listeners.js +11 -0
  21. package/build/core/native-module.d.ts +177 -0
  22. package/build/core/native-module.js +63 -0
  23. package/build/core/permissions.d.ts +3 -0
  24. package/build/core/permissions.js +7 -0
  25. package/build/core/types.d.ts +244 -0
  26. package/build/core/types.js +1 -0
  27. package/build/legacy/contacts.d.ts +30 -0
  28. package/build/legacy/contacts.js +177 -0
  29. package/build/legacy/enums.d.ts +68 -0
  30. package/build/legacy/enums.js +73 -0
  31. package/build/legacy/index.d.ts +3 -0
  32. package/build/legacy/index.js +3 -0
  33. package/build/legacy/native-module.d.ts +30 -0
  34. package/build/legacy/native-module.js +4 -0
  35. package/build/legacy/types.d.ts +158 -0
  36. package/build/legacy/types.js +1 -0
  37. package/build/react/contact-access-button.d.ts +10 -0
  38. package/build/react/contact-access-button.js +11 -0
  39. package/build/react/index.d.ts +2 -0
  40. package/build/react/index.js +2 -0
  41. package/build/solid/contact-access-button.d.ts +10 -0
  42. package/build/solid/contact-access-button.js +15 -0
  43. package/build/solid/index.d.ts +2 -0
  44. package/build/solid/index.js +2 -0
  45. package/build/svelte/contact-access-button.svelte +18 -0
  46. package/build/svelte/contact-access-button.svelte.d.ts +4 -0
  47. package/build/svelte/index.d.ts +7 -0
  48. package/build/svelte/index.js +8 -0
  49. package/build/vue/contact-access-button.d.ts +19 -0
  50. package/build/vue/contact-access-button.js +19 -0
  51. package/build/vue/index.d.ts +2 -0
  52. package/build/vue/index.js +2 -0
  53. package/build-ngc/angular/contact-access-button/index.d.ts +21 -0
  54. package/build-ngc/angular/contact-access-button/index.js +59 -0
  55. package/build-ngc/angular/contact-access-button/index.js.map +1 -0
  56. package/build-ngc/angular/index.d.ts +2 -0
  57. package/build-ngc/angular/index.js +4 -0
  58. package/build-ngc/angular/index.js.map +1 -0
  59. package/build-ngc/core/contact-access-button.d.ts +29 -0
  60. package/build-ngc/core/contact-access-button.js +28 -0
  61. package/build-ngc/core/contact-access-button.js.map +1 -0
  62. package/build-ngc/core/contact.d.ts +3 -0
  63. package/build-ngc/core/contact.js +4 -0
  64. package/build-ngc/core/contact.js.map +1 -0
  65. package/build-ngc/core/container.d.ts +5 -0
  66. package/build-ngc/core/container.js +6 -0
  67. package/build-ngc/core/container.js.map +1 -0
  68. package/build-ngc/core/enums.d.ts +53 -0
  69. package/build-ngc/core/enums.js +57 -0
  70. package/build-ngc/core/enums.js.map +1 -0
  71. package/build-ngc/core/group.d.ts +5 -0
  72. package/build-ngc/core/group.js +5 -0
  73. package/build-ngc/core/group.js.map +1 -0
  74. package/build-ngc/core/index.d.ts +8 -0
  75. package/build-ngc/core/index.js +8 -0
  76. package/build-ngc/core/index.js.map +1 -0
  77. package/build-ngc/core/listeners.d.ts +3 -0
  78. package/build-ngc/core/listeners.js +12 -0
  79. package/build-ngc/core/listeners.js.map +1 -0
  80. package/build-ngc/core/native-module.d.ts +177 -0
  81. package/build-ngc/core/native-module.js +64 -0
  82. package/build-ngc/core/native-module.js.map +1 -0
  83. package/build-ngc/core/permissions.d.ts +3 -0
  84. package/build-ngc/core/permissions.js +8 -0
  85. package/build-ngc/core/permissions.js.map +1 -0
  86. package/build-ngc/core/types.d.ts +244 -0
  87. package/build-ngc/core/types.js +2 -0
  88. package/build-ngc/core/types.js.map +1 -0
  89. package/native-link.json +26 -0
  90. package/package.json +152 -0
  91. package/src/angular/contact-access-button/index.ts +45 -0
  92. package/src/angular/index.ts +3 -0
  93. package/src/core/contact-access-button.ts +83 -0
  94. package/src/core/contact.ts +3 -0
  95. package/src/core/container.ts +5 -0
  96. package/src/core/enums.ts +55 -0
  97. package/src/core/group.ts +4 -0
  98. package/src/core/index.ts +14 -0
  99. package/src/core/listeners.ts +19 -0
  100. package/src/core/native-module.ts +281 -0
  101. package/src/core/permissions.ts +10 -0
  102. package/src/core/types.ts +220 -0
  103. package/src/legacy/contacts.ts +274 -0
  104. package/src/legacy/enums.ts +72 -0
  105. package/src/legacy/index.ts +3 -0
  106. package/src/legacy/native-module.ts +66 -0
  107. package/src/legacy/types.ts +159 -0
  108. package/src/react/contact-access-button.ts +20 -0
  109. package/src/react/index.ts +2 -0
  110. package/src/solid/contact-access-button.ts +24 -0
  111. package/src/solid/index.ts +2 -0
  112. package/src/svelte/contact-access-button.svelte +18 -0
  113. package/src/svelte/index.ts +10 -0
  114. package/src/svelte/svelte-ambient.d.ts +3 -0
  115. package/src/svelte/svelte-compile.test-helper.ts +7 -0
  116. package/src/vue/contact-access-button.ts +24 -0
  117. package/src/vue/index.ts +2 -0
@@ -0,0 +1,64 @@
1
+ import { Platform, requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_CONTACTS_NEXT_MODULE_NAME = 'ExpoContactsNext';
3
+ export const ON_CONTACTS_CHANGE_EVENT_NAME = 'contactsDidChange';
4
+ export class FallbackGroup {
5
+ id;
6
+ constructor(id) {
7
+ this.id = id;
8
+ }
9
+ getName() {
10
+ throw new Error('Not implemented');
11
+ }
12
+ setName(_name) {
13
+ throw new Error('Not implemented');
14
+ }
15
+ addContact(_contact) {
16
+ throw new Error('Not implemented');
17
+ }
18
+ removeContact(_contact) {
19
+ throw new Error('Not implemented');
20
+ }
21
+ getContacts() {
22
+ throw new Error('Not implemented');
23
+ }
24
+ delete() {
25
+ throw new Error('Not implemented');
26
+ }
27
+ static create(_name, _containerId) {
28
+ throw new Error('Not implemented');
29
+ }
30
+ static getAll(_containerId) {
31
+ throw new Error('Not implemented');
32
+ }
33
+ }
34
+ export class FallbackContainer {
35
+ id;
36
+ constructor(id) {
37
+ this.id = id;
38
+ }
39
+ getName() {
40
+ throw new Error('Not implemented');
41
+ }
42
+ getType() {
43
+ throw new Error('Not implemented');
44
+ }
45
+ getGroups() {
46
+ throw new Error('Not implemented');
47
+ }
48
+ getContacts() {
49
+ throw new Error('Not implemented');
50
+ }
51
+ static getAll() {
52
+ throw new Error('Not implemented');
53
+ }
54
+ static getDefault() {
55
+ throw new Error('Not implemented');
56
+ }
57
+ }
58
+ export const expoContactsNext = requireNativeModule(EXPO_CONTACTS_NEXT_MODULE_NAME);
59
+ // iOS registers the class as `ContactNext` (distinct from the deprecated legacy `Contact` JS
60
+ // class); Android registers it directly as `Contact`. Alias so both platforms expose `.Contact`.
61
+ if (Platform.OS === 'ios' && expoContactsNext.ContactNext) {
62
+ expoContactsNext.Contact = expoContactsNext.ContactNext;
63
+ }
64
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAiClE,MAAM,8BAA8B,GAAG,kBAAkB,CAAC;AAC1D,MAAM,CAAC,MAAM,6BAA6B,GAAG,mBAAmB,CAAC;AA8KjE,MAAM,OAAO,aAAa;IACI;IAA5B,YAA4B,EAAU;QAAV,OAAE,GAAF,EAAE,CAAQ;IAAG,CAAC;IAC1C,OAAO;QACL,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,KAAa;QACnB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,UAAU,CAAC,QAAuB;QAChC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,aAAa,CAAC,QAAuB;QACnC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,WAAW;QACT,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,MAAM;QACJ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,KAAa,EAAE,YAAqB;QAChD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,YAAqB;QACjC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;CACF;AAED,MAAM,OAAO,iBAAiB;IACA;IAA5B,YAA4B,EAAU;QAAV,OAAE,GAAF,EAAE,CAAQ;IAAG,CAAC;IAC1C,OAAO;QACL,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,OAAO;QACL,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,SAAS;QACP,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,WAAW;QACT,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,MAAM;QACX,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,CAAC,UAAU;QACf,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;CACF;AAgBD,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CACjD,8BAA8B,CAC/B,CAAC;AAEF,6FAA6F;AAC7F,iGAAiG;AACjG,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,gBAAgB,CAAC,WAAW,EAAE,CAAC;IAC1D,gBAAgB,CAAC,OAAO,GAAG,gBAAgB,CAAC,WAAW,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ContactsPermissionResponse } from './types';
2
+ export declare function getPermissionsAsync(): Promise<ContactsPermissionResponse>;
3
+ export declare function requestPermissionsAsync(): Promise<ContactsPermissionResponse>;
@@ -0,0 +1,8 @@
1
+ import { expoContactsNext } from './native-module';
2
+ export async function getPermissionsAsync() {
3
+ return expoContactsNext.getPermissionsAsync();
4
+ }
5
+ export async function requestPermissionsAsync() {
6
+ return expoContactsNext.requestPermissionsAsync();
7
+ }
8
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,OAAO,gBAAgB,CAAC,mBAAmB,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,OAAO,gBAAgB,CAAC,uBAAuB,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,244 @@
1
+ import type { PermissionResponse } from 'expo-modules-core';
2
+ import type { ContactField, ContactsSortOrder, NonGregorianCalendar } from './enums';
3
+ export type ContactsPermissionResponse = PermissionResponse & {
4
+ accessPrivileges?: 'all' | 'limited' | 'none';
5
+ };
6
+ export type ContactQueryOptions = {
7
+ limit?: number;
8
+ offset?: number;
9
+ sortOrder?: ContactsSortOrder;
10
+ name?: string;
11
+ /** @platform ios */
12
+ rawContacts?: boolean;
13
+ };
14
+ export type FormOptions = {
15
+ displayedPropertyKeys?: ContactField[];
16
+ message?: string;
17
+ alternateName?: string;
18
+ allowsEditing?: boolean;
19
+ allowsActions?: boolean;
20
+ shouldShowLinkedContacts?: boolean;
21
+ isNew?: boolean;
22
+ cancelButtonTitle?: string;
23
+ showsCancelButton?: boolean;
24
+ preventAnimation?: boolean;
25
+ groupId?: string;
26
+ };
27
+ export type CreateFormOptions = {
28
+ cancelButtonTitle?: string;
29
+ showsCancelButton?: boolean;
30
+ preventAnimation?: boolean;
31
+ };
32
+ export type NewEmail = {
33
+ label?: string;
34
+ address?: string;
35
+ };
36
+ export type ExistingEmail = NewEmail & {
37
+ id: string;
38
+ };
39
+ export type NewPhone = {
40
+ label?: string;
41
+ number?: string;
42
+ };
43
+ export type ExistingPhone = NewPhone & {
44
+ id: string;
45
+ };
46
+ export type NewDate = {
47
+ label?: string;
48
+ date?: ContactDate;
49
+ };
50
+ export type ExistingDate = NewDate & {
51
+ id: string;
52
+ };
53
+ export type NewExtraName = {
54
+ label?: string;
55
+ name?: string;
56
+ };
57
+ export type ExistingExtraName = NewExtraName & {
58
+ id: string;
59
+ };
60
+ export type NewAddress = {
61
+ label?: string;
62
+ street?: string;
63
+ city?: string;
64
+ state?: string;
65
+ postcode?: string;
66
+ region?: string;
67
+ country?: string;
68
+ };
69
+ export type ExistingAddress = NewAddress & {
70
+ id: string;
71
+ };
72
+ export type NewRelation = {
73
+ label?: string;
74
+ name?: string;
75
+ };
76
+ export type ExistingRelation = NewRelation & {
77
+ id: string;
78
+ };
79
+ export type NewUrlAddress = {
80
+ label?: string;
81
+ url?: string;
82
+ };
83
+ export type ExistingUrlAddress = NewUrlAddress & {
84
+ id: string;
85
+ };
86
+ export type NewImAddress = {
87
+ label?: string;
88
+ username?: string;
89
+ service?: string;
90
+ };
91
+ export type ExistingImAddress = NewImAddress & {
92
+ id: string;
93
+ };
94
+ export type NewSocialProfile = {
95
+ label?: string;
96
+ username?: string;
97
+ service?: string;
98
+ url?: string;
99
+ userId?: string;
100
+ };
101
+ export type ExistingSocialProfile = NewSocialProfile & {
102
+ id: string;
103
+ };
104
+ export type ContactDate = {
105
+ year?: number;
106
+ month: number;
107
+ day: number;
108
+ };
109
+ /** @platform ios */
110
+ export type NonGregorianBirthday = {
111
+ year?: number;
112
+ month: number;
113
+ day: number;
114
+ calendar: NonGregorianCalendar;
115
+ };
116
+ export type ContactPatch = {
117
+ isFavourite?: boolean | null;
118
+ givenName?: string | null;
119
+ middleName?: string | null;
120
+ familyName?: string | null;
121
+ nickname?: string | null;
122
+ maidenName?: string | null;
123
+ prefix?: string | null;
124
+ suffix?: string | null;
125
+ phoneticGivenName?: string | null;
126
+ phoneticMiddleName?: string | null;
127
+ phoneticFamilyName?: string | null;
128
+ company?: string | null;
129
+ department?: string | null;
130
+ jobTitle?: string | null;
131
+ phoneticCompanyName?: string | null;
132
+ note?: string | null;
133
+ image?: string | null;
134
+ birthday?: ContactDate | null;
135
+ nonGregorianBirthday?: NonGregorianBirthday | null;
136
+ emails?: (ExistingEmail | NewEmail)[];
137
+ phones?: (ExistingPhone | NewPhone)[];
138
+ dates?: (ExistingDate | NewDate)[];
139
+ extraNames?: (ExistingExtraName | NewExtraName)[];
140
+ addresses?: (ExistingAddress | NewAddress)[];
141
+ relations?: (ExistingRelation | NewRelation)[];
142
+ urlAddresses?: (ExistingUrlAddress | NewUrlAddress)[];
143
+ socialProfiles?: (ExistingSocialProfile | NewSocialProfile)[];
144
+ imAddresses?: (ExistingImAddress | NewImAddress)[];
145
+ };
146
+ export type CreateContactRecord = {
147
+ isFavourite?: boolean;
148
+ givenName?: string;
149
+ middleName?: string;
150
+ familyName?: string;
151
+ maidenName?: string;
152
+ nickname?: string;
153
+ prefix?: string;
154
+ suffix?: string;
155
+ phoneticGivenName?: string;
156
+ phoneticMiddleName?: string;
157
+ phoneticFamilyName?: string;
158
+ company?: string;
159
+ department?: string;
160
+ jobTitle?: string;
161
+ phoneticCompanyName?: string;
162
+ note?: string;
163
+ image?: string;
164
+ birthday?: ContactDate;
165
+ nonGregorianBirthday?: NonGregorianBirthday;
166
+ emails?: NewEmail[];
167
+ dates?: NewDate[];
168
+ phones?: NewPhone[];
169
+ addresses?: NewAddress[];
170
+ relations?: NewRelation[];
171
+ urlAddresses?: NewUrlAddress[];
172
+ imAddresses?: NewImAddress[];
173
+ socialProfiles?: NewSocialProfile[];
174
+ extraNames?: NewExtraName[];
175
+ };
176
+ export type ContactDetails = {
177
+ isFavourite: boolean;
178
+ fullName: string | null;
179
+ givenName: string | null;
180
+ middleName: string | null;
181
+ familyName: string | null;
182
+ maidenName?: string | null;
183
+ nickname?: string | null;
184
+ prefix: string | null;
185
+ suffix: string | null;
186
+ phoneticGivenName: string | null;
187
+ phoneticMiddleName: string | null;
188
+ phoneticFamilyName: string | null;
189
+ company: string | null;
190
+ department: string | null;
191
+ jobTitle?: string;
192
+ phoneticCompanyName?: string;
193
+ note: string | null;
194
+ image: string | null;
195
+ thumbnail: string | null;
196
+ birthday?: ContactDate | null;
197
+ nonGregorianBirthday?: NonGregorianBirthday | null;
198
+ emails: ExistingEmail[];
199
+ dates: ExistingDate[];
200
+ phones: ExistingPhone[];
201
+ extraNames: ExistingExtraName[];
202
+ addresses: ExistingAddress[];
203
+ relations: ExistingRelation[];
204
+ urlAddresses: ExistingUrlAddress[];
205
+ socialProfiles: ExistingSocialProfile[];
206
+ imAddresses: ExistingImAddress[];
207
+ };
208
+ export type ContactFieldKey = {
209
+ [ContactField.IS_FAVOURITE]: 'isFavourite';
210
+ [ContactField.FULL_NAME]: 'fullName';
211
+ [ContactField.GIVEN_NAME]: 'givenName';
212
+ [ContactField.MIDDLE_NAME]: 'middleName';
213
+ [ContactField.FAMILY_NAME]: 'familyName';
214
+ [ContactField.MAIDEN_NAME]: 'maidenName';
215
+ [ContactField.NICKNAME]: 'nickname';
216
+ [ContactField.PREFIX]: 'prefix';
217
+ [ContactField.SUFFIX]: 'suffix';
218
+ [ContactField.PHONETIC_GIVEN_NAME]: 'phoneticGivenName';
219
+ [ContactField.PHONETIC_MIDDLE_NAME]: 'phoneticMiddleName';
220
+ [ContactField.PHONETIC_FAMILY_NAME]: 'phoneticFamilyName';
221
+ [ContactField.COMPANY]: 'company';
222
+ [ContactField.PHONETIC_COMPANY_NAME]: 'phoneticCompanyName';
223
+ [ContactField.DEPARTMENT]: 'department';
224
+ [ContactField.JOB_TITLE]: 'jobTitle';
225
+ [ContactField.NOTE]: 'note';
226
+ [ContactField.IMAGE]: 'image';
227
+ [ContactField.THUMBNAIL]: 'thumbnail';
228
+ [ContactField.BIRTHDAY]: 'birthday';
229
+ [ContactField.NON_GREGORIAN_BIRTHDAY]: 'nonGregorianBirthday';
230
+ [ContactField.EMAILS]: 'emails';
231
+ [ContactField.PHONES]: 'phones';
232
+ [ContactField.ADDRESSES]: 'addresses';
233
+ [ContactField.DATES]: 'dates';
234
+ [ContactField.EXTRA_NAMES]: 'extraNames';
235
+ [ContactField.RELATIONS]: 'relations';
236
+ [ContactField.URL_ADDRESSES]: 'urlAddresses';
237
+ [ContactField.SOCIAL_PROFILES]: 'socialProfiles';
238
+ [ContactField.IM_ADDRESSES]: 'imAddresses';
239
+ };
240
+ export type PartialContactDetails<T extends readonly ContactField[]> = {
241
+ id: string;
242
+ } & {
243
+ [K in T[number]]: ContactDetails[K];
244
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ {
2
+ "android": {
3
+ "gradleProjectName": "expo-contacts",
4
+ "modules": [
5
+ {
6
+ "importPath": "expo.modules.contacts.ContactsModule",
7
+ "className": "ContactsModule",
8
+ "nativeName": "ExpoContacts"
9
+ },
10
+ {
11
+ "importPath": "expo.modules.contacts.next.ContactsNextModule",
12
+ "className": "ContactsNextModule",
13
+ "nativeName": "ExpoContactsNext"
14
+ }
15
+ ],
16
+ "manifestPermissions": [
17
+ "android.permission.READ_CONTACTS",
18
+ "android.permission.WRITE_CONTACTS"
19
+ ]
20
+ },
21
+ "ios": {
22
+ "infoPlistKeys": {
23
+ "NSContactsUsageDescription": "This app uses your contacts to read and manage your address book."
24
+ }
25
+ }
26
+ }
package/package.json ADDED
@@ -0,0 +1,152 @@
1
+ {
2
+ "name": "@symbiote-native/contacts",
3
+ "version": "0.1.0",
4
+ "description": "expo-contacts wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Both surfaces: the modern SharedObject-class Contact/Group/Container API (default entry) and the legacy function-based API (/legacy subpath).",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "react-native",
8
+ "symbiote-native",
9
+ "expo-contacts",
10
+ "react",
11
+ "vue",
12
+ "svelte",
13
+ "solid",
14
+ "angular",
15
+ "contacts",
16
+ "address-book"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/OneEyed1366/symbiote-native.git",
21
+ "directory": "packages/contacts"
22
+ },
23
+ "homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/contacts#readme",
24
+ "bugs": {
25
+ "url": "https://github.com/OneEyed1366/symbiote-native/issues"
26
+ },
27
+ "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
28
+ "type": "module",
29
+ "main": "./build/core/index.js",
30
+ "module": "./build/core/index.js",
31
+ "types": "./build/core/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./build/core/index.d.ts",
35
+ "default": "./build/core/index.js"
36
+ },
37
+ "./vue": {
38
+ "types": "./build/vue/index.d.ts",
39
+ "default": "./build/vue/index.js"
40
+ },
41
+ "./react": {
42
+ "types": "./build/react/index.d.ts",
43
+ "default": "./build/react/index.js"
44
+ },
45
+ "./svelte": "./src/svelte/index.ts",
46
+ "./solid": {
47
+ "types": "./build/solid/index.d.ts",
48
+ "default": "./build/solid/index.js"
49
+ },
50
+ "./angular": {
51
+ "types": "./build-ngc/angular/index.d.ts",
52
+ "react-native": "./build-ngc/angular/index.js",
53
+ "default": "./src/angular/index.ts"
54
+ },
55
+ "./legacy": {
56
+ "types": "./build/legacy/index.d.ts",
57
+ "default": "./build/legacy/index.js"
58
+ }
59
+ },
60
+ "files": [
61
+ "src",
62
+ "build",
63
+ "build-ngc",
64
+ "native-link.json",
65
+ "!src/**/*.test.*",
66
+ "!src/**/*.spec.*",
67
+ "!src/**/*.detox.*"
68
+ ],
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
72
+ "dependencies": {
73
+ "expo-contacts": "57.0.6",
74
+ "expo-modules-core": "57.0.20",
75
+ "@symbiote-native/components": "3.1.3"
76
+ },
77
+ "peerDependencies": {
78
+ "@angular/core": ">=20",
79
+ "@vue/runtime-core": "^3.5.13",
80
+ "react": ">=19.0.0",
81
+ "react-native": ">=0.86",
82
+ "solid-js": ">=1.9.0",
83
+ "svelte": ">=5.56.0",
84
+ "vue": ">=3.5.0",
85
+ "@symbiote-native/angular": "^3.1.3",
86
+ "@symbiote-native/engine": "^1.4.0",
87
+ "@symbiote-native/solid": "^3.0.5",
88
+ "@symbiote-native/react": "^3.1.0",
89
+ "@symbiote-native/svelte": "^3.0.5",
90
+ "@symbiote-native/vue": "^3.1.0"
91
+ },
92
+ "peerDependenciesMeta": {
93
+ "@symbiote-native/angular": {
94
+ "optional": true
95
+ },
96
+ "@symbiote-native/react": {
97
+ "optional": true
98
+ },
99
+ "@symbiote-native/solid": {
100
+ "optional": true
101
+ },
102
+ "@symbiote-native/svelte": {
103
+ "optional": true
104
+ },
105
+ "@symbiote-native/vue": {
106
+ "optional": true
107
+ },
108
+ "@angular/core": {
109
+ "optional": true
110
+ },
111
+ "@vue/runtime-core": {
112
+ "optional": true
113
+ },
114
+ "react": {
115
+ "optional": true
116
+ },
117
+ "solid-js": {
118
+ "optional": true
119
+ },
120
+ "svelte": {
121
+ "optional": true
122
+ },
123
+ "vue": {
124
+ "optional": true
125
+ }
126
+ },
127
+ "devDependencies": {
128
+ "@angular/compiler": "~22.0.8",
129
+ "@angular/compiler-cli": "~22.0.8",
130
+ "@angular/core": "~22.0.8",
131
+ "@types/node": "^26.0.0",
132
+ "@types/react": "^19.2.0",
133
+ "@vue/runtime-core": "^3.5.13",
134
+ "react": "19.2.3",
135
+ "solid-js": "^1.9.14",
136
+ "typescript": "~6.0.0",
137
+ "svelte": "^5.56.0",
138
+ "@symbiote-native/angular": "3.1.3",
139
+ "@symbiote-native/solid": "3.0.5",
140
+ "@symbiote-native/engine": "1.4.0",
141
+ "@symbiote-native/test-utils": "0.4.5",
142
+ "@symbiote-native/react": "3.1.0",
143
+ "@symbiote-native/svelte": "3.0.5",
144
+ "@symbiote-native/vue": "3.1.0"
145
+ },
146
+ "scripts": {
147
+ "typecheck": "tsc --build",
148
+ "clean": "rm -rf build-ngc",
149
+ "ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
150
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
151
+ }
152
+ }
@@ -0,0 +1,45 @@
1
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2
+ import { DescriptorOutlet, NativeViewBase } from '@symbiote-native/angular';
3
+ import type { IDescriptor } from '@symbiote-native/components';
4
+ import {
5
+ isContactAccessButtonAvailable,
6
+ renderContactAccessButton,
7
+ } from '../../core/contact-access-button';
8
+ import type { IContactAccessButtonProps } from '../../core/contact-access-button';
9
+
10
+ /** Angular twin of `expo-contacts`' `ContactAccessButton`, iOS 18+ only, nothing elsewhere */
11
+ @Component({
12
+ selector: 'ContactAccessButton',
13
+ standalone: true,
14
+ imports: [DescriptorOutlet],
15
+ changeDetection: ChangeDetectionStrategy.OnPush,
16
+ template: `@if (descriptor; as node) {
17
+ <symbiote-descriptor-outlet [node]="node" />
18
+ }`,
19
+ })
20
+ export class ContactAccessButton extends NativeViewBase {
21
+ /** True only on iOS 18.0 and newer */
22
+ static readonly isAvailable = isContactAccessButtonAvailable;
23
+
24
+ @Input() query?: string;
25
+ @Input() caption?: IContactAccessButtonProps['caption'];
26
+ @Input() ignoredEmails?: string[];
27
+ @Input() ignoredPhoneNumbers?: string[];
28
+ @Input() tintColor?: IContactAccessButtonProps['tintColor'];
29
+ @Input() backgroundColor?: IContactAccessButtonProps['backgroundColor'];
30
+ @Input() textColor?: IContactAccessButtonProps['textColor'];
31
+
32
+ protected override readonly propNames = [
33
+ 'query',
34
+ 'caption',
35
+ 'ignoredEmails',
36
+ 'ignoredPhoneNumbers',
37
+ 'tintColor',
38
+ 'backgroundColor',
39
+ 'textColor',
40
+ ] as const satisfies readonly (keyof ContactAccessButton)[];
41
+
42
+ protected override renderView(props: object): IDescriptor | null {
43
+ return renderContactAccessButton(props);
44
+ }
45
+ }
@@ -0,0 +1,3 @@
1
+ // No per-instance state or event stream to wrap in an injectable service - plain re-export.
2
+ export * from '../core';
3
+ export { ContactAccessButton } from './contact-access-button';
@@ -0,0 +1,83 @@
1
+ import {
2
+ Platform,
3
+ requireNativeViewManager,
4
+ requireOptionalNativeModule,
5
+ } from 'expo-modules-core';
6
+ import { el } from '@symbiote-native/components';
7
+ import type {
8
+ IAccessibilityProps,
9
+ IAriaProps,
10
+ IDescriptor,
11
+ IResponderProps,
12
+ } from '@symbiote-native/components';
13
+ import {
14
+ expoViewManagerName,
15
+ tryRegisterNativeView,
16
+ } from '@symbiote-native/engine';
17
+ import type {
18
+ IColorValue,
19
+ IStyleProp,
20
+ ISymbioteEvent,
21
+ IViewStyle,
22
+ } from '@symbiote-native/engine';
23
+
24
+ export const CONTACT_ACCESS_BUTTON_MODULE_NAME = 'ExpoContactAccessButton';
25
+
26
+ export type IContactAccessButtonCaption = 'default' | 'email' | 'phone';
27
+
28
+ // Every field is iOS 18+ only, the button renders nothing anywhere else
29
+ export type IContactAccessButtonProps = IAccessibilityProps &
30
+ IAriaProps &
31
+ IResponderProps & {
32
+ /** Matched against contacts not yet exposed to the app, typically from a search field */
33
+ query?: string;
34
+ /** Caption under the contact name when the query has one match, nothing by default */
35
+ caption?: IContactAccessButtonCaption;
36
+ /** Contacts matching `query` that also match one of these emails are left out */
37
+ ignoredEmails?: string[];
38
+ /** Contacts matching `query` that also match one of these phone numbers are left out */
39
+ ignoredPhoneNumbers?: string[];
40
+ /** Tint of the button and of the modal shown when there is more than one match */
41
+ tintColor?: IColorValue;
42
+ /** Button background, keep it opaque to satisfy the platform legibility rules */
43
+ backgroundColor?: IColorValue;
44
+ /** Button title color, a dimmed version of it is used for the caption */
45
+ textColor?: IColorValue;
46
+ style?: IStyleProp<IViewStyle>;
47
+ testID?: string;
48
+ nativeID?: string;
49
+ onLayout?: (event: ISymbioteEvent) => void;
50
+ };
51
+
52
+ type IContactAccessButtonModule = { isAvailable: boolean };
53
+
54
+ export function contactAccessButtonViewName(): string {
55
+ return expoViewManagerName(CONTACT_ACCESS_BUTTON_MODULE_NAME);
56
+ }
57
+
58
+ /** True only on iOS 18.0 and newer, where the native module reports the button as available */
59
+ export function isContactAccessButtonAvailable(): boolean {
60
+ return (
61
+ Platform.OS === 'ios' &&
62
+ (requireOptionalNativeModule<IContactAccessButtonModule>(
63
+ CONTACT_ACCESS_BUTTON_MODULE_NAME,
64
+ )?.isAvailable ??
65
+ false)
66
+ );
67
+ }
68
+
69
+ // Registers the view config in React Native's registry, which the engine derives the view's events
70
+ // and prop processors from. Called at render, not at import: a registration that only a barrel's
71
+ // load-time side effect performs is dropped by Metro's `inlineRequires` in release
72
+ export function ensureContactAccessButtonRegistered(): boolean {
73
+ if (Platform.OS !== 'ios') return false;
74
+ return tryRegisterNativeView(() =>
75
+ requireNativeViewManager(CONTACT_ACCESS_BUTTON_MODULE_NAME),
76
+ );
77
+ }
78
+
79
+ // `object`, not the props type: Vue and Angular hand over an untyped attribute bag
80
+ export function renderContactAccessButton(props: object): IDescriptor | null {
81
+ if (!ensureContactAccessButtonRegistered()) return null;
82
+ return el(contactAccessButtonViewName(), { ...props });
83
+ }
@@ -0,0 +1,3 @@
1
+ import { expoContactsNext } from './native-module';
2
+
3
+ export class Contact extends expoContactsNext.Contact {}
@@ -0,0 +1,5 @@
1
+ import { expoContactsNext, FallbackContainer } from './native-module';
2
+
3
+ /** @platform ios - Android has no Container concept in expo-contacts. */
4
+ export class Container extends (expoContactsNext.Container ??
5
+ FallbackContainer) {}