Skip to content

Commit dfee26c

Browse files
author
Wojtach
committed
Merge branch 'main' into fix/46-two-db-instances
2 parents 253199c + 1f53693 commit dfee26c

File tree

9 files changed

+570
-10
lines changed

9 files changed

+570
-10
lines changed

.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
android/
22
build/
3-
expo-example/
3+
# expo-example/
44
node_modules/
55
dist/
66
lib/
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React, { useContext, useState } from 'react';
2+
import { Collection, MutableDocument } from 'cbl-reactnative';
3+
import { useStyleScheme } from '@/components/Themed/Themed';
4+
import { SafeAreaView } from 'react-native';
5+
import ResultListView from '@/components/ResultsListView/ResultsListView';
6+
import DatabaseScopeCollectionActionForm from '@/components/DatabaseScopeCollectionActionForm/DatabaseScopeCollectionActionForm';
7+
import useNavigationBarTitleOption from '@/hooks/useNativgationBarTitle';
8+
import { useNavigation } from '@react-navigation/native';
9+
import DatabaseContext from '@/providers/DatabaseContext';
10+
11+
export default function CollectionStatusScreen() {
12+
const { databases } = useContext(DatabaseContext)!;
13+
const [databaseName, setDatabaseName] = useState<string>('');
14+
const [scopeName, setScopeName] = useState<string>('');
15+
const [collectionName, setCollectionName] = useState<string>('');
16+
17+
const [isListenerAdded, setIsListenerAdded] = useState(false);
18+
const [token, setToken] = useState<string>('');
19+
const [collection, setCollection] = useState<Collection | null>(null);
20+
21+
const styles = useStyleScheme();
22+
const navigation = useNavigation();
23+
useNavigationBarTitleOption('Collection Change Listener', navigation);
24+
const [informationMessages, setInformationMessages] = useState<string[]>([]);
25+
26+
async function update(): Promise<void> {
27+
try {
28+
const database = databases[databaseName];
29+
if (database) {
30+
const collection = await database.collection(collectionName, scopeName);
31+
if (collection != null) {
32+
setCollection(collection);
33+
if (!isListenerAdded || token === '') {
34+
setInformationMessages((prev) => [
35+
...prev,
36+
`::Information: Collection <${collection.name}> Starting Change listener...`,
37+
]);
38+
const token = await collection.addChangeListener((change) => {
39+
for (const doc of change.documentIDs) {
40+
const dateString = new Date().toISOString();
41+
const newMessage = `${dateString}::Change:: Collection <${collection.name}> changed: ${doc}`;
42+
setInformationMessages((prev) => [...prev, newMessage]);
43+
}
44+
});
45+
46+
const saveDocuments = async () => {
47+
const doc1 = new MutableDocument();
48+
const doc2 = new MutableDocument();
49+
doc1.setId('doc1');
50+
doc1.setString('name', 'Alice');
51+
doc2.setId('doc2');
52+
doc2.setString('name', 'tdbGamer');
53+
await collection.save(doc1);
54+
await collection.save(doc2);
55+
};
56+
await saveDocuments();
57+
setIsListenerAdded(true);
58+
setToken(token);
59+
}
60+
} else {
61+
setInformationMessages((prev) => [
62+
...prev,
63+
`::ERROR: ${scopeName}.${collectionName} not found`,
64+
]);
65+
}
66+
} else {
67+
setInformationMessages((prev) => [
68+
...prev,
69+
`::ERROR: Database ${databaseName} not found`,
70+
]);
71+
}
72+
} catch (error) {
73+
// @ts-ignore
74+
setInformationMessages((prev) => [...prev, `::ERROR: ${error.message}`]);
75+
}
76+
}
77+
78+
async function stop(): Promise<void> {
79+
const database = databases[databaseName];
80+
if (database != null && isListenerAdded && collection) {
81+
await collection.removeChangeListener(token);
82+
setIsListenerAdded(false);
83+
setInformationMessages([
84+
`::Information: Removed Listening for changes on collection: ${collection.name}`,
85+
]);
86+
}
87+
setToken('');
88+
setDatabaseName('');
89+
setCollectionName('');
90+
setScopeName('');
91+
}
92+
93+
return (
94+
<SafeAreaView style={styles.container}>
95+
<DatabaseScopeCollectionActionForm
96+
databaseName={databaseName}
97+
setDatabaseName={setDatabaseName}
98+
scopeName={scopeName}
99+
setScopeName={setScopeName}
100+
collectionName={collectionName}
101+
setCollectionName={setCollectionName}
102+
handleUpdatePressed={update}
103+
handleStopPressed={stop}
104+
/>
105+
<ResultListView useScrollView={true} messages={informationMessages} />
106+
</SafeAreaView>
107+
);
108+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import React, { useContext, useState } from 'react';
2+
import { Collection, MutableDocument } from 'cbl-reactnative';
3+
import { useStyleScheme } from '@/components/Themed/Themed';
4+
import { SafeAreaView, View } from 'react-native';
5+
import ResultListView from '@/components/ResultsListView/ResultsListView';
6+
import DatabaseScopeCollectionActionForm from '@/components/DatabaseScopeCollectionActionForm/DatabaseScopeCollectionActionForm';
7+
import useNavigationBarTitleOption from '@/hooks/useNativgationBarTitle';
8+
import { useNavigation } from '@react-navigation/native';
9+
import DatabaseContext from '@/providers/DatabaseContext';
10+
import { StyledTextInput } from '@/components/StyledTextInput/StyledTextInput';
11+
import HeaderView from '@/components/HeaderView/HeaderView';
12+
13+
export default function CollectionStatusScreen() {
14+
const { databases } = useContext(DatabaseContext)!;
15+
const [databaseName, setDatabaseName] = useState<string>('');
16+
const [scopeName, setScopeName] = useState<string>('');
17+
const [collectionName, setCollectionName] = useState<string>('');
18+
const [documentID, setDocumentID] = useState<string>('-1');
19+
20+
const [isListenerAdded, setIsListenerAdded] = useState(false);
21+
const [token, setToken] = useState<string>('');
22+
const [collection, setCollection] = useState<Collection | null>(null);
23+
24+
const styles = useStyleScheme();
25+
const navigation = useNavigation();
26+
useNavigationBarTitleOption(
27+
'Collection Document Change Listener',
28+
navigation
29+
);
30+
const [informationMessages, setInformationMessages] = useState<string[]>([]);
31+
32+
async function update(): Promise<void> {
33+
try {
34+
const database = databases[databaseName];
35+
if (database) {
36+
const collection = await database.collection(collectionName, scopeName);
37+
if (collection != null) {
38+
setCollection(collection);
39+
if (!isListenerAdded || token === '') {
40+
setInformationMessages((prev) => [
41+
...prev,
42+
`::Information: Collection <${collection.name}> Starting Document Change listener...`,
43+
]);
44+
const token = await collection.addDocumentChangeListener(
45+
documentID,
46+
(change) => {
47+
const dateString = new Date().toISOString();
48+
const newMessage = `${dateString}::Change:: Document with id: <${change.documentId}> changed in database: <${change.database.getName()}>, collection: <${change.collection.name}>`;
49+
setInformationMessages((prev) => [...prev, newMessage]);
50+
}
51+
);
52+
53+
const saveDocuments = async () => {
54+
const doc1 = new MutableDocument();
55+
doc1.setId(documentID);
56+
doc1.setString('name', 'Alice');
57+
58+
await collection.save(doc1);
59+
};
60+
await saveDocuments();
61+
62+
const fetchedDoc = await collection.document(documentID);
63+
const mutableDoc = MutableDocument.fromDocument(fetchedDoc);
64+
mutableDoc.setString('key', 'value2');
65+
await collection.save(mutableDoc);
66+
67+
setIsListenerAdded(true);
68+
setToken(token);
69+
}
70+
} else {
71+
setInformationMessages((prev) => [
72+
...prev,
73+
`::ERROR: ${scopeName}.${collectionName} not found`,
74+
]);
75+
}
76+
} else {
77+
setInformationMessages((prev) => [
78+
...prev,
79+
`::ERROR: Database ${databaseName} not found`,
80+
]);
81+
}
82+
} catch (error) {
83+
// @ts-ignore
84+
setInformationMessages((prev) => [...prev, `::ERROR: ${error.message}`]);
85+
}
86+
}
87+
88+
async function stop(): Promise<void> {
89+
const database = databases[databaseName];
90+
if (database != null && isListenerAdded && collection) {
91+
await collection.removeChangeListener(token);
92+
setIsListenerAdded(false);
93+
setInformationMessages([
94+
`::Information: Removed Listening for changes od document: ${documentID} on collection: ${collection.name}`,
95+
]);
96+
}
97+
setToken('');
98+
setDatabaseName('');
99+
setCollectionName('');
100+
setScopeName('');
101+
}
102+
103+
return (
104+
<SafeAreaView style={styles.container}>
105+
<DatabaseScopeCollectionActionForm
106+
databaseName={databaseName}
107+
setDatabaseName={setDatabaseName}
108+
scopeName={scopeName}
109+
setScopeName={setScopeName}
110+
collectionName={collectionName}
111+
setCollectionName={setCollectionName}
112+
handleUpdatePressed={update}
113+
handleStopPressed={stop}
114+
/>
115+
<HeaderView name="Document ID" iconName="file-document" />
116+
<View style={styles.component}>
117+
<StyledTextInput
118+
autoCapitalize="none"
119+
placeholder="Document ID"
120+
onChangeText={(newText) => setDocumentID(newText)}
121+
defaultValue={documentID}
122+
/>
123+
</View>
124+
<ResultListView useScrollView={true} messages={informationMessages} />
125+
</SafeAreaView>
126+
);
127+
}

expo-example/components/DatabaseScopeCollectionActionForm/DatabaseScopeCollectionActionForm.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,22 @@ export default function DatabaseScopeCollectionActionForm({
1111
collectionName,
1212
setCollectionName,
1313
handleUpdatePressed,
14+
handleStopPressed,
1415
style,
1516
}: DatabaseScopeCollectionActionFormProps) {
1617
const icons = [
1718
{
1819
iconName: 'play',
1920
onPress: handleUpdatePressed,
2021
},
22+
...(handleStopPressed
23+
? [
24+
{
25+
iconName: 'stop',
26+
onPress: handleStopPressed,
27+
},
28+
]
29+
: []),
2130
];
2231
return (
2332
<>

expo-example/components/DatabaseScopeCollectionActionForm/databaseScopeCollectionActionFormProps.type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export type DatabaseScopeCollectionActionFormProps = {
66
collectionName: string;
77
setCollectionName: (arg: string) => void;
88
handleUpdatePressed: () => void;
9+
handleStopPressed?: () => void;
910
style?: object;
1011
};

expo-example/hooks/useCollectionNavigationSections.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ export function useCollectionNavigationSections() {
4646
title: 'List Collections',
4747
path: '/collection/list',
4848
},
49+
{
50+
id: 14,
51+
title: 'Collection Change Listener',
52+
path: '/collection/changeListener',
53+
},
54+
{
55+
id: 15,
56+
title: 'Collection Document Change Listener',
57+
path: '/collection/documentChangeListener',
58+
},
4959
],
5060
},
5161
{

ios/CblReactnative.mm

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ @interface RCT_EXTERN_MODULE(CblReactnative, RCTEventEmitter)
55

66
// MARK: - Collection Functions
77

8+
RCT_EXTERN_METHOD(
9+
collection_AddChangeListener:(NSString *)changeListenerToken
10+
fromCollectionWithName:(NSString *)collectionName
11+
fromDatabaseWithName:(NSString *)name
12+
fromScopeWithName:(NSString *)scopeName
13+
withResolver:(RCTPromiseResolveBlock)resolve
14+
withRejecter:(RCTPromiseRejectBlock)reject
15+
)
16+
17+
RCT_EXTERN_METHOD(
18+
collection_AddDocumentChangeListener:(NSString *)changeListenerToken
19+
forDocumentWithId:(NSString *)documentId
20+
fromCollectionWithName:(NSString *)collectionName
21+
fromDatabaseWithName:(NSString *)name
22+
fromScopeWithName:(NSString *)scopeName
23+
withResolver:(RCTPromiseResolveBlock)resolve
24+
withRejecter:(RCTPromiseRejectBlock)reject
25+
)
26+
27+
28+
RCT_EXTERN_METHOD(
29+
collection_RemoveChangeListener:(NSString *)changeListenerToken
30+
withResolver:(RCTPromiseResolveBlock)resolve
31+
withRejecter:(RCTPromiseRejectBlock)reject
32+
)
33+
834
RCT_EXTERN_METHOD(collection_CreateCollection:
935
(NSString *) collectionName
1036
fromDatabaseWithName:(NSString *) name

0 commit comments

Comments
 (0)