Skip to content

Commit e0bcb95

Browse files
committed
feat: add annotation export via text or markdown
1 parent 055dbe2 commit e0bcb95

File tree

12 files changed

+539
-185
lines changed

12 files changed

+539
-185
lines changed

packages/android/app/src/main/java/io/literal/lib/WebEvent.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ public class WebEvent {
2727
public static final String TYPE_ANALYTICS_SET_USER_ID = "ANALYTICS_SET_USER_ID";
2828
public static final String TYPE_AUTH_SIGN_OUT = "AUTH_SIGN_OUT";
2929
public static final String TYPE_AUTH_SIGN_OUT_RESULT = "AUTH_SIGN_OUT_RESULT";
30-
public static final String TYPE_ACTION_VIEW_URI = "ACTION_VIEW_URI";
3130
public static final String TYPE_GET_APP_VERSION = "GET_APP_VERSION";
3231
public static final String TYPE_GET_APP_VERSION_RESULT = "GET_APP_VERSION_RESULT";
32+
public static final String TYPE_ACTION_VIEW_URI = "ACTION_VIEW_URI";
33+
public static final String TYPE_ACTION_SHARE = "ACTION_SHARE";
3334

3435
// Source WebView
3536
public static final String TYPE_VIEW_STATE_EDIT_ANNOTATION_TAGS = "VIEW_STATE_EDIT_ANNOTATION_TAGS";

packages/android/app/src/main/java/io/literal/ui/activity/MainActivity.java

+59-56
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ public void onReceive(Context context, Intent intent) {
8989
}
9090
}
9191
};
92+
private final ActivityResultLauncher<Intent> createAnnotationFromSourceLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), MainActivity.this::handleCreateAnnotationFromSourceResult);
9293
private SourceWebView sourceWebViewBottomSheetFragment = null;
9394
private AppWebView appWebViewBottomSheetFragment = null;
9495
private Observer<SourceInitializationStatus> sourceInitializationStatusObserver;
95-
private final ActivityResultLauncher<Intent> createAnnotationFromSourceLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), MainActivity.this::handleCreateAnnotationFromSourceResult);
9696

9797
@Override
9898
protected void onCreate(Bundle savedInstanceState) {
@@ -189,50 +189,14 @@ private CompletableFuture<User> initializeViewModel() {
189189
Annotation annotation = Annotation.fromJson(data.getJSONObject("annotation"));
190190
boolean displayBottomSheet = data.getBoolean("displayBottomSheet");
191191

192-
sourceWebViewBottomSheetFragment.handleViewTargetForAnnotation(annotation, targetId)
193-
.ifPresent((s) -> {
194-
if (displayBottomSheet) {
195-
sourceWebViewViewModelBottomSheet.getBottomSheetBehavior().ifPresent(b -> b.setState(BottomSheetBehavior.STATE_EXPANDED));
196-
197-
SourceInitializationStatus sourceInitializationStatus = sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().getValue();
198-
// Source wil already be initialized if it was previously accessed or it may have loaded in a previous call where `displayBottomSheet` was false.
199-
if (sourceInitializationStatus.equals(SourceInitializationStatus.INITIALIZED)) {
200-
appWebViewViewModelBottomSheet.setBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED);
201-
boolean javascriptEnabled = Optional.ofNullable(sourceWebViewViewModelBottomSheet).map((vm) -> vm.getSourceJavaScriptConfig().getValue().isEnabled()).orElse(true);
202-
if (!javascriptEnabled) {
203-
ToastRepository.show(this, R.string.toast_javascript_disabled, ToastRepository.STYLE_DARK_ACCENT);
204-
}
205-
} else if (sourceInitializationStatus.equals(SourceInitializationStatus.FAILED)) {
206-
ToastRepository.show(this, R.string.toast_annotation_renderer_failed_to_initialize, ToastRepository.STYLE_DARK_ACCENT);
207-
} else {
208-
if (sourceInitializationStatusObserver != null) {
209-
sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().removeObserver(sourceInitializationStatusObserver);
210-
}
211-
sourceInitializationStatusObserver = new Observer<SourceInitializationStatus>() {
212-
@Override
213-
public void onChanged(SourceInitializationStatus sourceInitializationStatus) {
214-
int sourceWebViewBottomSheetState = sourceWebViewViewModelBottomSheet.getBottomSheetBehavior()
215-
.map(BottomSheetBehavior::getState)
216-
.orElse(BottomSheetBehavior.STATE_HIDDEN);
217-
218-
if (sourceInitializationStatus.equals(SourceInitializationStatus.INITIALIZED) && sourceWebViewBottomSheetState != BottomSheetBehavior.STATE_HIDDEN) {
219-
appWebViewViewModelBottomSheet.setBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED);
220-
}
221-
222-
if (sourceInitializationStatus.equals(SourceInitializationStatus.INITIALIZED) || sourceInitializationStatus.equals(SourceInitializationStatus.FAILED)) {
223-
sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().removeObserver(this);
224-
}
225-
}
226-
};
227-
sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().observe(this, sourceInitializationStatusObserver);
228-
}
229-
}
230-
});
192+
this.handleViewTargetForAnnotation(targetId, annotation, displayBottomSheet);
231193
} catch (JSONException e) {
232-
ErrorRepository.captureException(e, webEvent.toString());
194+
ErrorRepository.captureException(e);
233195
}
234196
} else if (webEvent.getType().equals(WebEvent.TYPE_CREATE_ANNOTATION_FROM_SOURCE)) {
235197
this.handleCreateAnnotationFromSource(null);
198+
} else if (webEvent.getType().equals(WebEvent.TYPE_ACTION_SHARE)) {
199+
236200
}
237201
});
238202
appWebViewModelPrimary.clearReceivedWebEvents();
@@ -337,26 +301,65 @@ private void handleCreateAnnotationFromSource(String sourceUrl) {
337301
createAnnotationFromSourceLauncher.launch(intent);
338302
}
339303

304+
private void handleViewTargetForAnnotation(String targetId, Annotation annotation, boolean displayBottomSheet) {
305+
sourceWebViewBottomSheetFragment.handleViewTargetForAnnotation(annotation, targetId)
306+
.ifPresent((s) -> {
307+
if (displayBottomSheet) {
308+
sourceWebViewViewModelBottomSheet.getBottomSheetBehavior().ifPresent(b -> b.setState(BottomSheetBehavior.STATE_EXPANDED));
309+
310+
SourceInitializationStatus sourceInitializationStatus = sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().getValue();
311+
// Source wil already be initialized if it was previously accessed or it may have loaded in a previous call where `displayBottomSheet` was false.
312+
if (sourceInitializationStatus.equals(SourceInitializationStatus.INITIALIZED)) {
313+
appWebViewViewModelBottomSheet.setBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED);
314+
boolean javascriptEnabled = Optional.ofNullable(sourceWebViewViewModelBottomSheet).map((vm) -> vm.getSourceJavaScriptConfig().getValue().isEnabled()).orElse(true);
315+
if (!javascriptEnabled) {
316+
ToastRepository.show(this, R.string.toast_javascript_disabled, ToastRepository.STYLE_DARK_ACCENT);
317+
}
318+
} else if (sourceInitializationStatus.equals(SourceInitializationStatus.FAILED)) {
319+
ToastRepository.show(this, R.string.toast_annotation_renderer_failed_to_initialize, ToastRepository.STYLE_DARK_ACCENT);
320+
} else {
321+
if (sourceInitializationStatusObserver != null) {
322+
sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().removeObserver(sourceInitializationStatusObserver);
323+
}
324+
sourceInitializationStatusObserver = new Observer<SourceInitializationStatus>() {
325+
@Override
326+
public void onChanged(SourceInitializationStatus sourceInitializationStatus) {
327+
int sourceWebViewBottomSheetState = sourceWebViewViewModelBottomSheet.getBottomSheetBehavior()
328+
.map(BottomSheetBehavior::getState)
329+
.orElse(BottomSheetBehavior.STATE_HIDDEN);
330+
331+
if (sourceInitializationStatus.equals(SourceInitializationStatus.INITIALIZED) && sourceWebViewBottomSheetState != BottomSheetBehavior.STATE_HIDDEN) {
332+
appWebViewViewModelBottomSheet.setBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED);
333+
}
334+
335+
if (sourceInitializationStatus.equals(SourceInitializationStatus.INITIALIZED) || sourceInitializationStatus.equals(SourceInitializationStatus.FAILED)) {
336+
sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().removeObserver(this);
337+
}
338+
}
339+
};
340+
sourceWebViewViewModelBottomSheet.getSourceInitializationStatus().observe(this, sourceInitializationStatusObserver);
341+
}
342+
}
343+
});
344+
}
345+
340346
private void handleSourceWebViewBottomSheetHidden(Annotation[] createdAnnotations) {
341347
sourceWebViewViewModelBottomSheet.getBottomSheetBehavior().ifPresent(b -> b.setState(BottomSheetBehavior.STATE_HIDDEN));
342348
try {
343-
String json = JsonArrayUtil.stringifyObjectArray(createdAnnotations, Annotation::toJson).toString();
344-
JSONObject addCacheAnnotationsData = new JSONObject();
345-
addCacheAnnotationsData.put("annotations", json);
346-
appWebViewPrimaryFragment.postWebEvent(new WebEvent(
347-
WebEvent.TYPE_ADD_CACHE_ANNOTATIONS,
348-
UUID.randomUUID().toString(),
349-
addCacheAnnotationsData
350-
));
351-
Annotation[] annotations;
352-
try {
353-
annotations = JsonArrayUtil.parseJsonObjectArray(new JSONArray(json), new Annotation[0], Annotation::fromJson);
354-
} catch (JSONException e) {
355-
annotations = new Annotation[0];
349+
if (createdAnnotations.length > 0) {
350+
String json = JsonArrayUtil.stringifyObjectArray(createdAnnotations, Annotation::toJson).toString();
351+
JSONObject addCacheAnnotationsData = new JSONObject();
352+
addCacheAnnotationsData.put("annotations", json);
353+
appWebViewPrimaryFragment.postWebEvent(new WebEvent(
354+
WebEvent.TYPE_ADD_CACHE_ANNOTATIONS,
355+
UUID.randomUUID().toString(),
356+
addCacheAnnotationsData
357+
));
356358
}
357-
if (annotations.length == 1) {
359+
360+
if (createdAnnotations.length == 1) {
358361
ToastRepository.show(this, R.string.toast_annotation_created, ToastRepository.STYLE_DARK_ACCENT);
359-
} else if (annotations.length > 1) {
362+
} else if (createdAnnotations.length > 1) {
360363
ToastRepository.show(this, R.string.toast_annotations_created, ToastRepository.STYLE_DARK_ACCENT);
361364
}
362365
} catch (JSONException e) {

packages/android/app/src/main/java/io/literal/ui/fragment/AppWebView.java

+21
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,17 @@ private void handleActionViewURI(MessagingWebView view, String uri) {
228228
}
229229
}
230230

231+
private void handleActionShare(String text, String contentType) {
232+
Intent sendIntent = new Intent();
233+
sendIntent.setAction(Intent.ACTION_SEND);
234+
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
235+
sendIntent.setType(contentType);
236+
237+
Intent shareIntent = Intent.createChooser(sendIntent, null);
238+
startActivity(shareIntent);
239+
}
240+
241+
231242
private void handleGetAppVersion(MessagingWebView view) {
232243
try {
233244
JSONObject result = new JSONObject();
@@ -358,6 +369,16 @@ public void onReceiveValue(Uri[] value) {
358369
ErrorRepository.captureException(e);
359370
}
360371
return null;
372+
case WebEvent.TYPE_ACTION_SHARE:
373+
try {
374+
String text = event.getData().getString("text");
375+
String contentType = event.getData().getString("contentType");
376+
377+
this.handleActionShare(text, contentType);
378+
} catch (JSONException e) {
379+
ErrorRepository.captureException(e);
380+
}
381+
return null;
361382
case WebEvent.TYPE_GET_APP_VERSION:
362383
this.handleGetAppVersion(webView);
363384
return null;

packages/web/amplify/.config/project-config.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
"providers": [
33
"awscloudformation"
44
],
5-
"projectName": "literal - production",
5+
"projectName": "literal - staging",
66
"version": "3.0",
77
"frontend": "javascript",
88
"javascript": {
99
"framework": "react",
1010
"config": {
1111
"SourceDir": "src",
12-
"DistributionDir": "out",
12+
"DistributionDir": "build",
1313
"BuildCommand": "npm run-script build",
1414
"StartCommand": "npm run-script start"
1515
}

packages/web/amplify/team-provider-info.json

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"production": {
2+
"staging": {
33
"awscloudformation": {
4+
"AuthRoleName": "amplify-literal-staging-211408-authRole",
5+
"UnauthRoleArn": "arn:aws:iam::046525304497:role/amplify-literal-staging-211408-unauthRole",
6+
"AuthRoleArn": "arn:aws:iam::046525304497:role/amplify-literal-staging-211408-authRole",
47
"Region": "us-east-1",
5-
"DeploymentBucketName": "amplify-literal-production-114446-deployment",
6-
"UnauthRoleName": "amplify-literal-production-114446-unauthRole",
7-
"StackName": "amplify-literal-production-114446",
8-
"StackId": "arn:aws:cloudformation:us-east-1:046525304497:stack/amplify-literal-production-114446/cc4d0500-5b12-11ea-883c-12002b7e66d1",
9-
"AuthRoleName": "amplify-literal-production-114446-authRole",
10-
"UnauthRoleArn": "arn:aws:iam::046525304497:role/amplify-literal-production-114446-unauthRole",
11-
"AuthRoleArn": "arn:aws:iam::046525304497:role/amplify-literal-production-114446-authRole",
12-
"AmplifyAppId": "d3kn1jmjzilue"
8+
"DeploymentBucketName": "amplify-literal-staging-211408-deployment",
9+
"UnauthRoleName": "amplify-literal-staging-211408-unauthRole",
10+
"StackName": "amplify-literal-staging-211408",
11+
"StackId": "arn:aws:cloudformation:us-east-1:046525304497:stack/amplify-literal-staging-211408/c067b540-005e-11eb-aa67-0e80bfdca6c9",
12+
"AmplifyAppId": "d3h0fixyxiyfg5"
1313
},
1414
"categories": {
1515
"auth": {

0 commit comments

Comments
 (0)