├── .gitignore ├── LICENSE ├── README.md ├── lerna.json ├── metadata └── en-US │ ├── changelogs │ ├── 31.txt │ ├── 32.txt │ ├── 33.txt │ ├── 34.txt │ ├── 35.txt │ ├── 36.txt │ ├── 37.txt │ ├── 38.txt │ └── 39.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1_en-US.png │ │ ├── 2_en-US.png │ │ ├── 3_en-US.png │ │ ├── 4_en-US.png │ │ └── 5_en-US.png │ ├── short_description.txt │ ├── title.txt │ └── video.txt ├── package-lock.json ├── package.json ├── packages ├── android │ ├── .gitignore │ ├── .graphqlconfig.yml │ ├── .idea │ │ ├── .name │ │ ├── codeStyles │ │ │ └── Project.xml │ │ ├── compiler.xml │ │ ├── gradle.xml │ │ ├── jarRepositories.xml │ │ ├── misc.xml │ │ ├── runConfigurations.xml │ │ └── vcs.xml │ ├── LICENSE │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── literal │ │ │ │ └── ExampleInstrumentedTest.java │ │ │ ├── beta │ │ │ └── res │ │ │ │ └── values │ │ │ │ └── strings.xml │ │ │ ├── debug │ │ │ └── res │ │ │ │ └── values │ │ │ │ └── strings.xml │ │ │ ├── foss │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── literal │ │ │ │ │ └── repository │ │ │ │ │ ├── AnalyticsRepository.java │ │ │ │ │ └── ErrorRepository.java │ │ │ └── res │ │ │ │ └── raw │ │ │ │ ├── amplifyconfiguration.json │ │ │ │ └── awsconfiguration.json │ │ │ ├── full │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── literal │ │ │ │ └── repository │ │ │ │ ├── AnalyticsRepository.java │ │ │ │ └── ErrorRepository.java │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── graphql │ │ │ │ ├── com │ │ │ │ │ └── amazonaws │ │ │ │ │ │ └── amplify │ │ │ │ │ │ └── generated │ │ │ │ │ │ └── graphql │ │ │ │ │ │ ├── mutations.graphql │ │ │ │ │ │ └── queries.graphql │ │ │ │ └── schema.json │ │ │ ├── ic_launcher-playstore.png │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── literal │ │ │ │ │ ├── factory │ │ │ │ │ ├── AWSMobileClientFactory.java │ │ │ │ │ ├── AmazonS3ClientFactory.java │ │ │ │ │ └── AppSyncClientFactory.java │ │ │ │ │ ├── lib │ │ │ │ │ ├── AWSConfigurationLib.java │ │ │ │ │ ├── AmazonS3URILib.java │ │ │ │ │ ├── AnnotationCollectionLib.java │ │ │ │ │ ├── AnnotationLib.java │ │ │ │ │ ├── AnnotationTargetLib.java │ │ │ │ │ ├── ArrayUtil.java │ │ │ │ │ ├── Box.java │ │ │ │ │ ├── Callback.java │ │ │ │ │ ├── Callback2.java │ │ │ │ │ ├── Callback3.java │ │ │ │ │ ├── Constants.java │ │ │ │ │ ├── ContentResolverLib.java │ │ │ │ │ ├── Crypto.java │ │ │ │ │ ├── DateUtil.java │ │ │ │ │ ├── DomainMetadata.java │ │ │ │ │ ├── FileActivityResultCallback.java │ │ │ │ │ ├── JsonArrayUtil.java │ │ │ │ │ ├── JsonMapper.java │ │ │ │ │ ├── JsonReaderParser.java │ │ │ │ │ ├── ManyCallback.java │ │ │ │ │ ├── ResultCallback.java │ │ │ │ │ ├── Thunk.java │ │ │ │ │ ├── WebEvent.java │ │ │ │ │ └── WebRoutes.java │ │ │ │ │ ├── model │ │ │ │ │ ├── Annotation.java │ │ │ │ │ ├── AnnotationType.java │ │ │ │ │ ├── Body.java │ │ │ │ │ ├── CreateAnnotationNotification.java │ │ │ │ │ ├── ErrorRepositoryLevel.java │ │ │ │ │ ├── ExternalTarget.java │ │ │ │ │ ├── Format.java │ │ │ │ │ ├── HTMLScriptElement.java │ │ │ │ │ ├── Language.java │ │ │ │ │ ├── Motivation.java │ │ │ │ │ ├── RangeSelector.java │ │ │ │ │ ├── ResourceType.java │ │ │ │ │ ├── Selector.java │ │ │ │ │ ├── SourceInitializationStatus.java │ │ │ │ │ ├── SourceJavaScriptConfig.java │ │ │ │ │ ├── SourceWebViewAnnotation.java │ │ │ │ │ ├── SpecificResourceType.java │ │ │ │ │ ├── SpecificTarget.java │ │ │ │ │ ├── State.java │ │ │ │ │ ├── StorageObject.java │ │ │ │ │ ├── Target.java │ │ │ │ │ ├── TextDirection.java │ │ │ │ │ ├── TextPositionSelector.java │ │ │ │ │ ├── TextualBody.java │ │ │ │ │ ├── TextualTarget.java │ │ │ │ │ ├── TimeState.java │ │ │ │ │ ├── User.java │ │ │ │ │ ├── WebArchive.java │ │ │ │ │ └── XPathSelector.java │ │ │ │ │ ├── repository │ │ │ │ │ ├── AnnotationRepository.java │ │ │ │ │ ├── AuthenticationRepository.java │ │ │ │ │ ├── BitmapRepository.java │ │ │ │ │ ├── NotificationRepository.java │ │ │ │ │ ├── ScriptRepository.java │ │ │ │ │ ├── SharedPreferencesRepository.java │ │ │ │ │ ├── StorageRepository.java │ │ │ │ │ ├── ToastRepository.java │ │ │ │ │ ├── WebArchiveRepository.java │ │ │ │ │ └── WebViewRepository.java │ │ │ │ │ ├── service │ │ │ │ │ ├── AnnotationService.java │ │ │ │ │ ├── CreateAnnotationIntent.java │ │ │ │ │ └── SplashService.java │ │ │ │ │ ├── ui │ │ │ │ │ ├── MainApplication.java │ │ │ │ │ ├── activity │ │ │ │ │ │ ├── InstrumentedActivity.java │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── ShareTargetHandler.java │ │ │ │ │ ├── fragment │ │ │ │ │ │ ├── AppWebView.java │ │ │ │ │ │ ├── AppWebViewBottomSheetAnimator.java │ │ │ │ │ │ └── SourceWebView.java │ │ │ │ │ └── view │ │ │ │ │ │ ├── AppWebView.java │ │ │ │ │ │ ├── MessagingWebView.java │ │ │ │ │ │ ├── NestedScrollingChildWebView.java │ │ │ │ │ │ └── SourceWebView │ │ │ │ │ │ ├── Client.java │ │ │ │ │ │ ├── CreateAnnotationActionModeCallback.java │ │ │ │ │ │ ├── EditAnnotationActionModeCallback.java │ │ │ │ │ │ ├── Source.java │ │ │ │ │ │ └── SourceWebView.java │ │ │ │ │ └── viewmodel │ │ │ │ │ ├── AppWebViewViewModel.java │ │ │ │ │ ├── AuthenticationViewModel.java │ │ │ │ │ └── SourceWebViewViewModel.java │ │ │ └── res │ │ │ │ ├── drawable-hdpi │ │ │ │ ├── done_white.png │ │ │ │ ├── highlight_white.png │ │ │ │ ├── ic_stat_name.png │ │ │ │ └── toast_accent_frame.xml │ │ │ │ ├── drawable-mdpi │ │ │ │ ├── done_white.png │ │ │ │ ├── highlight_white.png │ │ │ │ └── ic_stat_name.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ ├── done_white.png │ │ │ │ ├── highlight_white.png │ │ │ │ └── ic_stat_name.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── done_white.png │ │ │ │ ├── highlight_white.png │ │ │ │ └── ic_stat_name.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── done_white.png │ │ │ │ ├── highlight_white.png │ │ │ │ └── ic_stat_name.png │ │ │ │ ├── drawable │ │ │ │ ├── ic_baseline_arrow_drop_down_24.xml │ │ │ │ ├── ic_baseline_build_24.xml │ │ │ │ ├── ic_baseline_keyboard_arrow_down_24.xml │ │ │ │ ├── ic_baseline_more_vert_24.xml │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── ic_launcher_foreground.xml │ │ │ │ ├── ic_logo.xml │ │ │ │ └── toast_primary_frame.xml │ │ │ │ ├── font │ │ │ │ ├── font.xml │ │ │ │ ├── roboto_mono.ttf │ │ │ │ └── roboto_mono_italic.ttf │ │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_share_target_handler.xml │ │ │ │ ├── authentication_handler.xml │ │ │ │ ├── fragment_app_web_view.xml │ │ │ │ ├── fragment_source_web_view.xml │ │ │ │ ├── splash.xml │ │ │ │ ├── toast_accent.xml │ │ │ │ ├── toast_primary.xml │ │ │ │ └── webview_toolbar_indeterminate_progress.xml │ │ │ │ ├── menu │ │ │ │ ├── source_webview_commit_edit_annotation_menu.xml │ │ │ │ ├── source_webview_create_annotation_menu.xml │ │ │ │ ├── source_webview_edit_annotation_menu.xml │ │ │ │ ├── source_webview_toolbar_create.xml │ │ │ │ └── source_webview_toolbar_read.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── values-v28 │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── test │ │ │ └── java │ │ │ └── io │ │ │ └── literal │ │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── schema.json │ ├── scripts │ │ ├── amplify-pull-production.sh │ │ ├── amplify-pull-staging.sh │ │ ├── create-foss-amplify-and-aws-configuration.js │ │ ├── graphql-sync-schema-production.sh │ │ └── graphql-sync-schema-staging.sh │ ├── settings.gradle │ └── source-webview-scripts │ │ ├── .gitignore │ │ ├── .nvmrc │ │ ├── annotation-renderer │ │ ├── annotation-focus-manager.mjs │ │ ├── highlighter.mjs │ │ ├── index.mjs │ │ ├── messenger.mjs │ │ ├── renderer.mjs │ │ └── util.mjs │ │ ├── build.sh │ │ ├── get-annotation-bounding-box │ │ └── index.mjs │ │ ├── get-annotation │ │ ├── index.mjs │ │ ├── model.mjs │ │ └── xpath.mjs │ │ ├── get-scripts │ │ └── index.mjs │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── shared │ │ ├── storage.mjs │ │ └── xpath.mjs │ │ ├── webpack.config.js │ │ └── wrap-dist-with-executor.mjs ├── apollo-link-analytics │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── ApolloLink.js │ │ ├── ApolloLink.re │ │ ├── ApolloLinkAnalytics.js │ │ └── ApolloLinkAnalytics.re ├── bs-amazon-cognito-identity-js │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── AmazonCognitoIdentity.re ├── bs-amplitude │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── Amplitude.js │ │ └── Amplitude.re ├── bs-aws-amplify │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── AwsAmplify.re ├── bs-aws-appsync │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── AwsAppSync.js │ │ └── AwsAppSync.re ├── bs-aws-sdk │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── AWSSDK.re ├── bs-sentry │ ├── .gitignore │ ├── LICENSE │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── SentryBrowser.re │ │ └── SentryNode.re ├── model │ ├── .gitignore │ ├── bsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── Annotation.js │ │ ├── Annotation.re │ │ ├── Body.js │ │ ├── Body.re │ │ ├── Format.js │ │ ├── Format.re │ │ ├── Language.js │ │ ├── Language.re │ │ ├── Motivation.js │ │ ├── Motivation.re │ │ ├── ResourceType.js │ │ ├── ResourceType.re │ │ ├── Selector.js │ │ ├── Selector.re │ │ ├── State.js │ │ ├── State.re │ │ ├── Target.js │ │ ├── Target.re │ │ ├── TextDirection.js │ │ └── TextDirection.re ├── pamphlet │ ├── .gitignore │ ├── index.ts │ ├── package-lock.json │ ├── package.json │ ├── templates │ │ ├── android-launch │ │ │ ├── template.mjml │ │ │ └── variables.json │ │ └── android-release-source-viewer │ │ │ └── template.mjml │ └── tsconfig.json └── web │ ├── .gitignore │ ├── LICENSE │ ├── amplify │ ├── .config │ │ └── project-config.json │ └── team-provider-info.json │ ├── bsconfig.json │ ├── fragment-types.json │ ├── graphql-schema.json │ ├── next.config.js │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── favicon-32.png │ ├── how-it-works-1.png │ ├── how-it-works-2.png │ ├── how-it-works-3.png │ ├── how-it-works-4.png │ ├── how-it-works-5.png │ ├── logo-128.png │ └── logo-opaque-128.png │ ├── scripts │ ├── amplify-pull-production.sh │ ├── amplify-pull-staging.sh │ ├── bsb-js-post-build.js │ ├── bsb-post-clean.sh │ ├── generate-fragment-types.js │ └── graphql-sync-schema.sh │ ├── src │ ├── Containers │ │ ├── Containers_AnnotationCollectionHeader │ │ │ ├── Containers_AnnotationCollectionHeader.re │ │ │ └── Containers_AnnotationCollectionHeader_GraphQL.re │ │ ├── Containers_AnnotationCollectionsHeader │ │ │ └── Containers_AnnotationCollectionsHeader.re │ │ ├── Containers_AnnotationEditor │ │ │ ├── Containers_AnnotationEditor.re │ │ │ ├── Containers_AnnotationEditor_GraphQL.re │ │ │ └── Containers_AnnotationEditor_Tag.re │ │ ├── Containers_NewAnnotationEditor │ │ │ ├── Containers_NewAnnotationEditor.re │ │ │ ├── Containers_NewAnnotationEditor_GraphQL.re │ │ │ ├── Containers_NewAnnotationEditor_PhasePrompt.re │ │ │ ├── Containers_NewAnnotationEditor_PhaseTextInput.re │ │ │ └── Containers_NewAnnotationEditor_Types.re │ │ ├── Containers_NewAnnotationFromMessageEventEditor │ │ │ └── Containers_NewAnnotationFromMessageEventEditor.re │ │ ├── Containers_NewAnnotationFromMessageEventHeader │ │ │ └── Containers_NewAnnotationFromMessageEventHeader.re │ │ ├── Containers_NewAnnotationFromShareEditor │ │ │ ├── Containers_NewAnnotationFromShareEditor.re │ │ │ └── Containers_NewAnnotationFromShareEditor_GraphQL.re │ │ ├── Containers_NewAnnotationFromShareHeader │ │ │ ├── Containers_NewAnnotationFromShareHeader.re │ │ │ └── Containers_NewAnnotationFromShareHeader_GraphQL.re │ │ ├── Containers_NewAnnotationHeader │ │ │ └── Containers_NewAnnotationHeader.re │ │ └── Containers_Onboarding │ │ │ ├── Containers_Onboarding.re │ │ │ └── Containers_Onboarding_GraphQL.re │ ├── Hooks │ │ └── Hooks_SearchParams.re │ ├── Lib │ │ ├── Lib_Apollo │ │ │ └── Lib_Apollo_Cache.re │ │ ├── Lib_GraphQL │ │ │ ├── Lib_GraphQL.re │ │ │ ├── Lib_GraphQL_Agent.re │ │ │ ├── Lib_GraphQL_Annotation.re │ │ │ ├── Lib_GraphQL_AnnotationBodyInput.re │ │ │ ├── Lib_GraphQL_AnnotationCollection.re │ │ │ ├── Lib_GraphQL_AnnotationTargetInput.re │ │ │ ├── Lib_GraphQL_CreateAnnotationFromExternalTargetMutation.re │ │ │ ├── Lib_GraphQL_CreateAnnotationMutation.re │ │ │ ├── Lib_GraphQL_DeleteAnnotationMutation.re │ │ │ ├── Lib_GraphQL_Language.re │ │ │ ├── Lib_GraphQL_Motivation.re │ │ │ ├── Lib_GraphQL_PatchAnnotationMutation.re │ │ │ ├── Lib_GraphQL_ResourceType.re │ │ │ ├── Lib_GraphQL_SelectorInput.re │ │ │ ├── Lib_GraphQL_StateInput.re │ │ │ └── Lib_GraphQL_TextDirection.re │ │ └── Lib_WebView_Model │ │ │ └── Lib_WebView_Model_Apollo.re │ ├── Providers │ │ ├── Providers_Apollo │ │ │ ├── Providers_Apollo.re │ │ │ ├── Providers_Apollo_AnalyticsLink.re │ │ │ ├── Providers_Apollo_AppSync.re │ │ │ ├── Providers_Apollo_Client.re │ │ │ └── Providers_Apollo_Credentials.re │ │ ├── Providers_Authentication │ │ │ ├── Providers_Authentication.re │ │ │ ├── Providers_Authentication_Credentials.re │ │ │ ├── Providers_Authentication_GraphQL.re │ │ │ └── Providers_Authentication_User.re │ │ ├── Providers_BottomNavigation │ │ │ └── Providers_BottomNavigation.re │ │ └── Providers_ModalNavigation │ │ │ └── Providers_ModalNavigation.re │ ├── QueryRenderers │ │ ├── QueryRenderers_AnnotationCollection │ │ │ ├── QueryRenderers_AnnotationCollection.re │ │ │ └── QueryRenderers_AnnotationCollection_GraphQL.re │ │ ├── QueryRenderers_AnnotationCollections │ │ │ ├── QueryRenderers_AnnotationCollections.re │ │ │ └── QueryRenderers_AnnotationCollections_GraphQL.re │ │ ├── QueryRenderers_NewAnnotation │ │ │ └── QueryRenderers_NewAnnotation.re │ │ ├── QueryRenderers_NewAnnotationFromShare │ │ │ ├── QueryRenderers_NewAnnotationFromShare.re │ │ │ └── QueryRenderers_NewAnnotationFromShare_GraphQL.re │ │ ├── QueryRenderers_TagsFilter │ │ │ ├── QueryRenderers_TagsFilter.re │ │ │ └── QueryRenderers_TagsFilter_GraphQL.re │ │ ├── QueryRenderers_WebView_CollapsedAnnotationTags │ │ │ └── QueryRenderers_WebView_CollapsedAnnotationTags.re │ │ └── QueryRenderers_WebView_EditAnnotationTags │ │ │ └── QueryRenderers_WebView_EditAnnotationTags.re │ ├── app.css │ ├── components │ │ ├── AddTagInput │ │ │ ├── AddTagInput.module.css │ │ │ └── AddTagInput.re │ │ ├── Alert.re │ │ ├── AnnotationCollectionHeader_OverflowMenu │ │ │ └── AnnotationCollectionHeader_OverflowMenu.re │ │ ├── AnnotationCollectionHeader_Title │ │ │ └── AnnotationCollectionHeader_Title.re │ │ ├── AppCard │ │ │ └── AppCard.re │ │ ├── ArrowTextButton │ │ │ └── ArrowTextButton.re │ │ ├── AuthenticationFields │ │ │ ├── AuthenticationFields.module.css │ │ │ └── AuthenticationFields.re │ │ ├── BottomAlert.re │ │ ├── BottomNavigation │ │ │ └── BottomNavigation.re │ │ ├── ErrorBoundary │ │ │ └── ErrorBoundary.re │ │ ├── ErrorDisplay.re │ │ ├── ExternalTargetCard │ │ │ ├── ExternalTargetCard.re │ │ │ └── ExternalTargetCard_GraphQL.re │ │ ├── FeatureIndicator │ │ │ └── FeatureIndicator.re │ │ ├── FeatureListItem │ │ │ └── FeatureListItem.re │ │ ├── FloatingActionButton.re │ │ ├── FooterLink │ │ │ └── FooterLink.re │ │ ├── Head.re │ │ ├── Loading.re │ │ ├── Markdown │ │ │ ├── Markdown.module.css │ │ │ └── Markdown.re │ │ ├── ProfileCard │ │ │ └── ProfileCard.re │ │ ├── PromptIconButton │ │ │ └── PromptIconButton.re │ │ ├── Redirect.re │ │ ├── ScrollSnapList.re │ │ ├── Skeleton │ │ │ ├── Skeleton.module.css │ │ │ └── Skeleton.re │ │ ├── SourceList │ │ │ └── SourceList.re │ │ ├── SourceListItem │ │ │ ├── SourceListItem.module.css │ │ │ ├── SourceListItem.re │ │ │ └── SourceListItem_GraphQL.re │ │ ├── Svg │ │ │ ├── Svg.re │ │ │ ├── add-circle.svg │ │ │ ├── add.svg │ │ │ ├── android.svg │ │ │ ├── apple.svg │ │ │ ├── arrow-down.svg │ │ │ ├── arrow-right.svg │ │ │ ├── arrow-up.svg │ │ │ ├── article.svg │ │ │ ├── back.svg │ │ │ ├── close.svg │ │ │ ├── delete-black.svg │ │ │ ├── delete.svg │ │ │ ├── done.svg │ │ │ ├── error-outline.svg │ │ │ ├── help-outline.svg │ │ │ ├── highlight.svg │ │ │ ├── label.svg │ │ │ ├── language.svg │ │ │ ├── logo.svg │ │ │ ├── manage-accounts.svg │ │ │ ├── more.svg │ │ │ ├── remove-circle.svg │ │ │ ├── share-black.svg │ │ │ ├── source.svg │ │ │ ├── text-fields.svg │ │ │ ├── text-snippet.svg │ │ │ └── waves.svg │ │ ├── TagLinkAndInput │ │ │ └── TagLinkAndInput.re │ │ ├── TagList │ │ │ └── TagList.re │ │ ├── TagListItem │ │ │ ├── TagListItem.re │ │ │ └── TagListItem_GraphQL.re │ │ ├── TagsList │ │ │ ├── TagsList.module.css │ │ │ └── TagsList.re │ │ ├── TextInput │ │ │ ├── TextInput.re │ │ │ ├── TextInput_Annotation.re │ │ │ ├── TextInput_Basic.module.css │ │ │ ├── TextInput_Basic.re │ │ │ └── TextInput_Loading.re │ │ ├── UnderlineLink │ │ │ └── UnderlineLink.re │ │ └── header.re │ ├── constants.re │ ├── externals │ │ ├── Apollo.re │ │ ├── Externals_Crypto.re │ │ ├── Externals_Error.re │ │ ├── Externals_URLSearchParams.re │ │ ├── FontFaceSet.re │ │ ├── LiteralWebview.re │ │ ├── MaterialUiLab.re │ │ ├── bowser.re │ │ ├── draft.re │ │ ├── lodash.re │ │ ├── next.re │ │ ├── ramda.re │ │ ├── raw.re │ │ └── uuid.re │ ├── pages │ │ └── _app.jsx │ ├── routes │ │ ├── Route_Authenticate.re │ │ ├── Route_Authenticate_SignIn.re │ │ ├── Route_Authenticate_SignUp.re │ │ ├── Route_Creators.re │ │ ├── Route_Creators_Id_Annotation_Collections.re │ │ ├── Route_Creators_Id_Annotation_Collections_Id.re │ │ ├── Route_Creators_Id_Annotations_New.re │ │ ├── Route_Creators_Id_Settings.re │ │ ├── Route_Creators_Id_Webview.re │ │ ├── Route_Index.re │ │ ├── Route_Policies_Id.re │ │ ├── Route_Start.re │ │ ├── Route_Writing.re │ │ ├── Route_Writing_Id.re │ │ └── Routes.re │ ├── services │ │ ├── Service_Analytics.re │ │ ├── Service_Log.re │ │ ├── Timer.re │ │ └── error.re │ ├── static │ │ ├── json │ │ │ └── writing.json │ │ └── markdown │ │ │ ├── privacy-policy.md │ │ │ ├── subprocessors.md │ │ │ └── writing │ │ │ ├── implementing-guest-authentication-with-amplify-cognito-appsync.md │ │ │ └── prologue.md │ ├── styles.re │ └── webview.re │ └── tailwind.config.js └── secret └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | secret/ 2 | node_modules 3 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0" 6 | } 7 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/31.txt: -------------------------------------------------------------------------------- 1 | Promotes the Collections view to be a top-level screen accessible via the bottom bar, and fixes bugs affecting source archiving. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.23 -------------------------------------------------------------------------------- /metadata/en-US/changelogs/32.txt: -------------------------------------------------------------------------------- 1 | Initial F-Droid release. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.24 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/33.txt: -------------------------------------------------------------------------------- 1 | Initial F-Droid release. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.25 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/34.txt: -------------------------------------------------------------------------------- 1 | Various stability improvements to annotation creation and capture. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.26 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/35.txt: -------------------------------------------------------------------------------- 1 | Improves stability of annotation creation to reduce crashes and improve capture reliability. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.27 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/36.txt: -------------------------------------------------------------------------------- 1 | Improves the rendering of annotations within web archives. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.28 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/37.txt: -------------------------------------------------------------------------------- 1 | Fixes a bug affecting creating annotations from source from within the application and polishes the color scheme. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.29 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/38.txt: -------------------------------------------------------------------------------- 1 | Adds annotation export (text and markdown) and includes source pathname when viewing a collection. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.30 4 | -------------------------------------------------------------------------------- /metadata/en-US/changelogs/39.txt: -------------------------------------------------------------------------------- 1 | Fixes tagging on annotation creation and tweaks annotation export to broaden share targets. 2 | 3 | See the full release notes and source code for this release here: https://github.com/literal-io/literal/releases/tag/v1.1.30 4 | -------------------------------------------------------------------------------- /metadata/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Literal is a textual information browser for augmenting your reading experience; capture sources, annotations, and knowledge. 2 | 3 | CAPTURE ANNOTATIONS 4 | Share URLs, text, and images to Literal to annotate and highlight. 5 | 6 | COLLECT INFORMATION 7 | Over time, Literal becomes a personal knowledge base containing information and knowledge along with their source. 8 | 9 | VIEW ANNOTATIONS IN CONTEXT 10 | Quickly view the annotation's source context by tapping it. 11 | 12 | ORGANIZE BY TAG AND SOURCE 13 | Go beyond folders with a tag-based organization system, or view annotations by their shared source. 14 | 15 | OPEN SOURCE, OPEN STANDARDS 16 | Literal is open source (https://github.com/literal-io/literal) and implements the W3C Web Annotation standard (https://www.w3.org/TR/annotation-model/) to ensure that your data is portable. The application currently interfaces with proprietary network services hosted on Amazon Web Services, though local-only functionality may be supported in the future (https://github.com/literal-io/literal/issues/123). 17 | 18 | View https://literal.io/ for more information. 19 | -------------------------------------------------------------------------------- /metadata/en-US/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/featureGraphic.png -------------------------------------------------------------------------------- /metadata/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/icon.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/1_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/phoneScreenshots/1_en-US.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/2_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/phoneScreenshots/2_en-US.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/3_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/phoneScreenshots/3_en-US.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/4_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/phoneScreenshots/4_en-US.png -------------------------------------------------------------------------------- /metadata/en-US/images/phoneScreenshots/5_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/images/phoneScreenshots/5_en-US.png -------------------------------------------------------------------------------- /metadata/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Capture annotations, sources, and knowledge from text that you read. -------------------------------------------------------------------------------- /metadata/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Literal -------------------------------------------------------------------------------- /metadata/en-US/video.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/metadata/en-US/video.txt -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "literal", 3 | "version": "1.0.0", 4 | "description": "A textual annotation management system.", 5 | "main": "index.js", 6 | "scripts": { 7 | "lerna": "lerna", 8 | "lerna:bootstrap": "lerna bootstrap --hoist -- --legacy-peer-deps", 9 | "fastlane:supply:init": "fastlane supply init --json_key secret/fastlane-gcp-service-account.json" 10 | }, 11 | "engines": { 12 | "node": "^15" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/literal-io/literal.git" 17 | }, 18 | "author": { 19 | "name": "Daniel Ramirez", 20 | "email": "javamonn@hey.com", 21 | "url": "https://javamonn.com" 22 | }, 23 | "license": "GPL-3.0-only", 24 | "bugs": { 25 | "url": "https://github.com/literal-io/literal/issues" 26 | }, 27 | "homepage": "https://literal.io", 28 | "devDependencies": { 29 | "lerna": "^3.20.2", 30 | "prettier": "^1.19.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | app/beta 16 | app/foss 17 | app/full 18 | 19 | # Signing 20 | app/release-keystore.jks 21 | app/beta-keystore.jks 22 | signing.properties 23 | 24 | 25 | app/src/main/assets/SourceWebView* 26 | build/ 27 | dist/ 28 | node_modules/ 29 | aws-exports.js 30 | amplify-gradle-config.json 31 | amplifyxc.config 32 | 33 | 34 | app/src/main/res/raw/amplifyconfiguration.json 35 | app/src/main/res/raw/awsconfiguration.json 36 | 37 | #amplify 38 | amplify/\#current-cloud-backend 39 | amplify/.config/local-* 40 | amplify/logs 41 | amplify/mock-data 42 | amplify/backend/amplify-meta.json 43 | amplify/backend/awscloudformation 44 | amplify/backend/.temp 45 | build/ 46 | dist/ 47 | node_modules/ 48 | aws-exports.js 49 | awsconfiguration.json 50 | amplifyconfiguration.json 51 | amplify-build-config.json 52 | amplify-gradle-config.json 53 | amplifytools.xcconfig 54 | .secret-* -------------------------------------------------------------------------------- /packages/android/.graphqlconfig.yml: -------------------------------------------------------------------------------- 1 | projects: 2 | literal: 3 | schemaPath: app/src/main/graphql/schema.json 4 | includes: 5 | - app/src/main/graphql/**/*.graphql 6 | excludes: 7 | - ./amplify/** 8 | extensions: 9 | amplify: 10 | codeGenTarget: '' 11 | generatedFileName: '' 12 | docsFilePath: app/src/main/graphql/com/amazonaws/amplify/generated/graphql 13 | region: us-east-1 14 | apiId: null 15 | maxDepth: 2 16 | extensions: 17 | amplify: 18 | version: 3 19 | -------------------------------------------------------------------------------- /packages/android/.idea/.name: -------------------------------------------------------------------------------- 1 | Literal -------------------------------------------------------------------------------- /packages/android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /packages/android/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /packages/android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /packages/android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /packages/android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /packages/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /packages/android/app/src/androidTest/java/io/literal/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.literal; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("io.literal", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/android/app/src/beta/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Literal - Beta 3 | 4 | -------------------------------------------------------------------------------- /packages/android/app/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Literal - Debug 3 | 4 | -------------------------------------------------------------------------------- /packages/android/app/src/foss/java/io/literal/repository/AnalyticsRepository.java: -------------------------------------------------------------------------------- 1 | package io.literal.repository; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | import org.json.JSONObject; 6 | 7 | 8 | public class AnalyticsRepository { 9 | 10 | public static final String TYPE_GRAPH_QL_OPERATION = "GRAPH_QL_OPERATION"; 11 | public static final String TYPE_ACTIVITY_START = "ACTIVITY_START"; 12 | public static final String TYPE_DISPATCHED_WEB_EVENT = "DISPATCHED_WEB_EVENT"; 13 | public static final String TYPE_RECEIVED_WEB_EVENT = "RECEIVED_WEB_EVENT"; 14 | public static final String TYPE_CLICK = "CLICK"; 15 | public static final String TYPE_DISPATCH_SERVICE_INTENT = "DISPATCH_SERVICE_INTENT"; 16 | public static final String TYPE_HANDLE_INTENT_START = "HANDLE_INTENT_START"; 17 | public static final String TYPE_HANDLE_INTENT_COMPLETE = "HANDLE_INTENT_COMPLETE"; 18 | public static final String TYPE_ANNOTATION_RENDERER_INITIALIZED = "ANNOTATION_RENDERER_INITIALIZED"; 19 | public static final String TYPE_ANNOTATION_RENDERER_FAILED_TO_INITIALIZE = "ANNOTATION_RENDERER_FAILED_TO_INITIALIZE"; 20 | 21 | public static void initialize(Application application) { 22 | Log.d("AnalyticsRepository", "initialize"); 23 | } 24 | 25 | public static void logEvent(String type, JSONObject data) { 26 | Log.d("AnalyticsRepository", "logEvent: " + type + ", " + data.toString()); 27 | } 28 | 29 | public static void setUserId(String userId) { 30 | Log.d("AnalyticsRepository", "setUserId: " + userId); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/android/app/src/foss/res/raw/amplifyconfiguration.json: -------------------------------------------------------------------------------- 1 | {"UserAgent":"aws-amplify-cli/2.0","Version":"1.0","api":{"plugins":{"awsAPIPlugin":{"literal":{"endpointType":"GraphQL","endpoint":"https://2dxxqoarong2jd4ukbeugsg5t4.appsync-api.us-east-1.amazonaws.com/graphql","region":"us-east-1","authorizationType":"AMAZON_COGNITO_USER_POOLS"}}}},"auth":{"plugins":{"awsCognitoAuthPlugin":{"UserAgent":"aws-amplify-cli/0.1.0","Version":"0.1.0","IdentityManager":{"Default":{}},"CredentialsProvider":{"CognitoIdentity":{"Default":{"PoolId":"us-east-1:12bb0b85-e6e1-4964-9f0d-24d14b90398d","Region":"us-east-1"}}},"CognitoUserPool":{"Default":{"PoolId":"us-east-1_EWCESLVzL","AppClientId":"1geu7qgt6hdrs4a3e9tn4leg1","Region":"us-east-1"}},"Auth":{"Default":{"authenticationFlowType":"USER_SRP_AUTH"}},"AppSync":{"Default":{"ApiUrl":"https://2dxxqoarong2jd4ukbeugsg5t4.appsync-api.us-east-1.amazonaws.com/graphql","Region":"us-east-1","AuthMode":"AMAZON_COGNITO_USER_POOLS","ClientDatabasePrefix":"literal_AMAZON_COGNITO_USER_POOLS"},"literal_AWS_IAM":{"ApiUrl":"https://2dxxqoarong2jd4ukbeugsg5t4.appsync-api.us-east-1.amazonaws.com/graphql","Region":"us-east-1","AuthMode":"AWS_IAM","ClientDatabasePrefix":"literal_AWS_IAM"}},"S3TransferUtility":{"Default":{"Bucket":"literal-storage114446-production","Region":"us-east-1"}}}}},"storage":{"plugins":{"awsS3StoragePlugin":{"bucket":"literal-storage114446-production","region":"us-east-1","defaultAccessLevel":"guest"}}}} -------------------------------------------------------------------------------- /packages/android/app/src/foss/res/raw/awsconfiguration.json: -------------------------------------------------------------------------------- 1 | {"UserAgent":"aws-amplify-cli/0.1.0","Version":"0.1.0","IdentityManager":{"Default":{}},"CredentialsProvider":{"CognitoIdentity":{"Default":{"PoolId":"us-east-1:12bb0b85-e6e1-4964-9f0d-24d14b90398d","Region":"us-east-1"}}},"CognitoUserPool":{"Default":{"PoolId":"us-east-1_EWCESLVzL","AppClientId":"1geu7qgt6hdrs4a3e9tn4leg1","Region":"us-east-1"}},"Auth":{"Default":{"authenticationFlowType":"USER_SRP_AUTH"}},"AppSync":{"Default":{"ApiUrl":"https://2dxxqoarong2jd4ukbeugsg5t4.appsync-api.us-east-1.amazonaws.com/graphql","Region":"us-east-1","AuthMode":"AMAZON_COGNITO_USER_POOLS","ClientDatabasePrefix":"literal_AMAZON_COGNITO_USER_POOLS"},"literal_AWS_IAM":{"ApiUrl":"https://2dxxqoarong2jd4ukbeugsg5t4.appsync-api.us-east-1.amazonaws.com/graphql","Region":"us-east-1","AuthMode":"AWS_IAM","ClientDatabasePrefix":"literal_AWS_IAM"}},"S3TransferUtility":{"Default":{"Bucket":"literal-storage114446-production","Region":"us-east-1"}}} -------------------------------------------------------------------------------- /packages/android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/AWSConfigurationLib.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import android.content.Context; 4 | 5 | import com.amazonaws.mobile.config.AWSConfiguration; 6 | 7 | public class AWSConfigurationLib { 8 | private static volatile AWSConfiguration configuration; 9 | private static volatile AWSConfiguration guestConfiguration; 10 | 11 | public static AWSConfiguration getConfiguration(Context context) { 12 | if (configuration == null) { 13 | configuration = new AWSConfiguration(context); 14 | } 15 | return configuration; 16 | } 17 | 18 | public static AWSConfiguration getGuestConfiguration(Context context) { 19 | if (guestConfiguration == null) { 20 | guestConfiguration = new AWSConfiguration(context); 21 | guestConfiguration.setConfiguration("literal_AWS_IAM"); 22 | } 23 | return guestConfiguration; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/AmazonS3URILib.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import android.content.Context; 4 | 5 | import com.amazonaws.services.s3.AmazonS3URI; 6 | 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | 10 | import io.literal.repository.StorageRepository; 11 | 12 | public class AmazonS3URILib { 13 | 14 | /** 15 | * AmazonS3URI.getURI formats with s3:// scheme, we persist https:// equivalent for consistency 16 | */ 17 | public static URI toHTTPs(Context context, AmazonS3URI uri) { 18 | try { 19 | return new URI( 20 | "https", 21 | uri.getURI().getHost() + ".s3." + StorageRepository.getBucketRegion(context) +".amazonaws.com", 22 | uri.getURI().getPath(), 23 | null 24 | ); 25 | } catch (URISyntaxException e) { 26 | return null; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/AnnotationCollectionLib.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import io.literal.repository.ErrorRepository; 4 | 5 | public class AnnotationCollectionLib { 6 | public static String makeId(String creatorUsername, String labelText) { 7 | try { 8 | String idComponent = Crypto.sha256Hex(labelText); 9 | return WebRoutes.creatorsIdAnnotationCollectionId( 10 | WebRoutes.getAPIHost(), 11 | creatorUsername, 12 | idComponent 13 | ); 14 | } catch (java.security.NoSuchAlgorithmException ex) { 15 | ErrorRepository.captureException(ex); 16 | return null; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/AnnotationLib.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public class AnnotationLib { 4 | public static String idComponentFromId(String annotationId) { 5 | String[] parts = annotationId.split("/"); 6 | return parts[parts.length - 1]; 7 | } 8 | 9 | public static String creatorUsernameFromId(String annotationId) { 10 | String[] parts = annotationId.split("/"); 11 | return parts[parts.length - 3]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/AnnotationTargetLib.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import java.util.UUID; 4 | 5 | import io.literal.model.ExternalTarget; 6 | import io.literal.model.SpecificTarget; 7 | import io.literal.model.Target; 8 | import io.literal.model.TextualTarget; 9 | 10 | public class AnnotationTargetLib { 11 | public static String makeId(String annotationId) { 12 | return annotationId + "/targets/" + UUID.randomUUID().toString(); 13 | } 14 | 15 | public static String getID(Target target) { 16 | if (target instanceof ExternalTarget) { 17 | return ((ExternalTarget) target).getId().toString(); 18 | } else if (target instanceof SpecificTarget) { 19 | return ((SpecificTarget) target).getId(); 20 | } else { 21 | return ((TextualTarget) target).getId(); 22 | } 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/ArrayUtil.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import java.util.Arrays; 4 | 5 | public class ArrayUtil { 6 | public static T[] add(T[] arr, T item) { 7 | int idx = arr.length; 8 | T[] copy = Arrays.copyOf(arr, idx + 1); 9 | copy[idx] = item; 10 | 11 | return copy; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Box.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public class Box { 4 | private T value; 5 | 6 | public Box(T init) { 7 | this.value = init; 8 | } 9 | 10 | public T get() { 11 | return value; 12 | } 13 | 14 | public void set(T value) { 15 | this.value = value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Callback.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public interface Callback { 4 | void invoke(TError e, TData data); 5 | } 6 | 7 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Callback2.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public interface Callback2 { 4 | void invoke(Exception e, T1 data1, T2 data2); 5 | } 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Callback3.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public interface Callback3 { 4 | void invoke(Exception e, T1 data1, T2 data2, T3 data3); 5 | } 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Constants.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public class Constants { 4 | public static final String NAMESPACE = "io.literal"; 5 | public static final String LOG_TAG = "Literal"; 6 | 7 | public static final String RECENT_ANNOTATION_COLLECTION_ID_COMPONENT = "034a7e52c5c9534b709dc1dba403868399b0949f7c1933a67325c22077ffc221"; 8 | public static final String RECENT_ANNOTATION_COLLECTION_LABEL = "recent"; 9 | 10 | public static final String INTENT_MANUAL_FOR_RESULT_DATA = "MANUAL_FOR_RESULT_DATA"; 11 | } 12 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/ContentResolverLib.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.util.Log; 6 | 7 | import org.apache.commons.io.FileUtils; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | 13 | public class ContentResolverLib { 14 | public static File toFile (Context context, File directory, Uri uri, String fileName) { 15 | try { 16 | InputStream inputStream = context.getContentResolver().openInputStream(uri); 17 | File outputFile = new File(directory, fileName); 18 | FileUtils.copyInputStreamToFile(inputStream, outputFile); 19 | return outputFile; 20 | } catch (IOException e) { 21 | Log.e("", "Failed to perform ContentResolverLib.toFile", e); 22 | return null; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Crypto.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import android.os.Build; 4 | 5 | import androidx.annotation.RequiresApi; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | import java.security.MessageDigest; 9 | import java.security.NoSuchAlgorithmException; 10 | 11 | public class Crypto { 12 | public static String sha256Hex(String input) throws NoSuchAlgorithmException { 13 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 14 | 15 | byte[] hash = null; 16 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 17 | hash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); 18 | } else { 19 | hash = digest.digest(input.getBytes()); 20 | } 21 | 22 | StringBuffer output = new StringBuffer(); 23 | for (int i = 0; i < hash.length; i++) { 24 | String hex = Integer.toHexString(0xff & hash[i]); 25 | if(hex.length() == 1) output.append('0'); 26 | output.append(hex); 27 | } 28 | return output.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/DateUtil.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | import java.util.TimeZone; 7 | 8 | public class DateUtil { 9 | public static String toISO8601UTC(Date date) { 10 | TimeZone tz = TimeZone.getTimeZone("UTC"); 11 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); 12 | df.setTimeZone(tz); 13 | return df.format(date); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/FileActivityResultCallback.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import android.net.Uri; 4 | import android.util.Log; 5 | import android.webkit.ValueCallback; 6 | 7 | import androidx.activity.result.ActivityResultCallback; 8 | 9 | import java.io.File; 10 | 11 | public class FileActivityResultCallback implements ActivityResultCallback { 12 | ValueCallback callback; 13 | 14 | @Override 15 | public void onActivityResult(Uri result) { 16 | if (callback != null) { 17 | callback.onReceiveValue(new Uri[]{result}); 18 | } else { 19 | Log.i(Constants.LOG_TAG, "Received onActivityResult: " + result + ", but no callback set."); 20 | } 21 | } 22 | 23 | public void setFilePathCallback(ValueCallback callback) { 24 | this.callback = callback; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/JsonArrayUtil.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONException; 5 | import org.json.JSONObject; 6 | 7 | import java.util.Arrays; 8 | 9 | public class JsonArrayUtil { 10 | public static String[] parseJsonStringArray(JSONArray input) throws JSONException { 11 | String[] output = new String[input.length()]; 12 | for (int i = 0; i < input.length(); i++) { 13 | output[i] = input.getString(i); 14 | } 15 | return output; 16 | } 17 | 18 | public static T[] parseJsonObjectArray(JSONArray input, T[] output, JsonMapper mapper) throws JSONException { 19 | T[] sizedOutput = Arrays.copyOf(output, input.length()); 20 | for (int i = 0; i < input.length(); i++) { 21 | sizedOutput[i] = mapper.invoke(input.getJSONObject(i)); 22 | } 23 | return sizedOutput; 24 | } 25 | 26 | public static JSONArray stringifyObjectArray(T[] input, JsonMapper mapper) throws JSONException { 27 | JSONArray output = new JSONArray(); 28 | for (int i = 0; i < input.length; i++) { 29 | output.put(mapper.invoke(input[i])); 30 | } 31 | return output; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/JsonMapper.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import org.json.JSONException; 4 | 5 | public interface JsonMapper { 6 | Out invoke(In in) throws JSONException; 7 | } 8 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/JsonReaderParser.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | import android.util.JsonReader; 4 | 5 | import java.io.IOException; 6 | 7 | public interface JsonReaderParser { 8 | public T invoke(JsonReader reader) throws IOException; 9 | } 10 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/ResultCallback.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public interface ResultCallback { 4 | R invoke(Exception e, T data); 5 | } 6 | 7 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/lib/Thunk.java: -------------------------------------------------------------------------------- 1 | package io.literal.lib; 2 | 3 | public interface Thunk{ 4 | void invoke(); 5 | } -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/AnnotationType.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum AnnotationType { 4 | ANNOTATION 5 | } 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/Body.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import io.literal.repository.ErrorRepository; 7 | import type.AnnotationBodyInput; 8 | 9 | public class Body { 10 | public enum Type { 11 | TEXTUAL_BODY 12 | } 13 | 14 | protected final Type type; 15 | 16 | public Body(Type type) { 17 | this.type = type; 18 | } 19 | 20 | public Type getType() { 21 | return type; 22 | } 23 | 24 | public JSONObject toJson() throws JSONException { 25 | throw new JSONException("toJson not implemented."); 26 | }; 27 | 28 | public AnnotationBodyInput toAnnotationBodyInput() { 29 | ErrorRepository.captureException(new Exception("Attempted to call toAnnotationBodyInput, but not implemented.")); 30 | return null; 31 | } 32 | 33 | public static Body fromJson(JSONObject json) throws JSONException { 34 | String type = json.getString("type"); 35 | if (type.equals(Type.TEXTUAL_BODY.name())) { 36 | return TextualBody.fromJson(json); 37 | } else { 38 | throw new JSONException("fromJson not implemented."); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/ErrorRepositoryLevel.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum ErrorRepositoryLevel { 4 | INFO, 5 | ERROR; 6 | } 7 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/Format.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum Format { 4 | APPLICATION_X_MIMEARCHIVE, 5 | APPLICATION_OCTET_STREAM, 6 | MULTIPART_RELATED, 7 | IMAGE_PNG, 8 | TEXT_PLAIN, 9 | TEXT_HTML; 10 | 11 | public type.Format toGraphQL() { 12 | return type.Format.valueOf(this.name()); 13 | } 14 | 15 | public static Format fromMimeType(String mimeType) { 16 | switch (mimeType.toLowerCase()) { 17 | case "image/png": return IMAGE_PNG; 18 | case "application/x-mimearchive": return APPLICATION_X_MIMEARCHIVE; 19 | case "text/plain": return TEXT_PLAIN; 20 | case "text/html": return TEXT_HTML; 21 | case "multipart/related": return MULTIPART_RELATED; 22 | default: return APPLICATION_OCTET_STREAM; 23 | } 24 | } 25 | 26 | public String toMimeType() { 27 | switch (this) { 28 | case APPLICATION_X_MIMEARCHIVE: return "application/x-mimearchive"; 29 | case MULTIPART_RELATED: return "multipart/related"; 30 | case IMAGE_PNG: return "image/png"; 31 | case TEXT_PLAIN: return "text/plain"; 32 | case TEXT_HTML: return "text/html"; 33 | default: return "application/octet-stream"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/Language.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum Language { 4 | EN_US; 5 | 6 | public type.Language toGraphQL() { 7 | return type.Language.valueOf(this.name()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/Motivation.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum Motivation { 4 | TAGGING, 5 | HIGHLIGHTING; 6 | 7 | public type.Motivation toGraphQL() { 8 | return type.Motivation.valueOf(this.name()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/ResourceType.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum ResourceType { 4 | DATASET, 5 | IMAGE, 6 | VIDEO, 7 | SOUND, 8 | TEXT; 9 | 10 | public type.ResourceType toGraphQL() { 11 | return type.ResourceType.valueOf(this.name()); 12 | } 13 | } -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/Selector.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | import android.util.JsonReader; 4 | import android.util.Log; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import io.literal.repository.ErrorRepository; 10 | import type.AnnotationTargetInput; 11 | import type.SelectorInput; 12 | 13 | public class Selector { 14 | public enum Type { 15 | RANGE_SELECTOR, 16 | XPATH_SELECTOR, 17 | TEXT_POSITION_SELECTOR 18 | }; 19 | 20 | private final Type type; 21 | 22 | public Selector(Type type) { 23 | this.type = type; 24 | } 25 | 26 | public Type getType() { 27 | return type; 28 | } 29 | 30 | public JSONObject toJson() throws JSONException { 31 | throw new JSONException("toJson not implemented."); 32 | }; 33 | 34 | public SelectorInput toSelectorInput() { 35 | ErrorRepository.captureException(new Exception("Attempted to call toSelectorInput, but not implemented.")); 36 | return null; 37 | } 38 | 39 | public static Selector fromJson(JSONObject json) throws JSONException { 40 | String type = json.getString("type"); 41 | if (type.equals(Type.RANGE_SELECTOR.name())) { 42 | return RangeSelector.fromJson(json); 43 | } else if (type.equals(Type.XPATH_SELECTOR.name())) { 44 | return XPathSelector.fromJson(json); 45 | } else if (type.equals(Type.TEXT_POSITION_SELECTOR.name())) { 46 | return TextPositionSelector.fromJson(json); 47 | } 48 | 49 | return null; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/SourceInitializationStatus.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum SourceInitializationStatus { 4 | UNINITIALIZED, 5 | IN_PROGRESS, 6 | FAILED, 7 | INITIALIZED 8 | } 9 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/SourceJavaScriptConfig.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public class SourceJavaScriptConfig { 4 | public enum Reason { 5 | AUTOMATIC, 6 | USER_TOGGLED 7 | } 8 | private final boolean isEnabled; 9 | private final Reason reason; 10 | 11 | public SourceJavaScriptConfig(boolean isEnabled, Reason reason) { 12 | this.isEnabled = isEnabled; 13 | this.reason = reason; 14 | } 15 | 16 | public boolean isEnabled() { 17 | return isEnabled; 18 | } 19 | 20 | public Reason getReason() { 21 | return reason; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/SpecificResourceType.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum SpecificResourceType { 4 | SPECIFIC_RESOURCE 5 | } 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/State.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | import android.util.Log; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | import io.literal.repository.ErrorRepository; 9 | import type.StateInput; 10 | 11 | public class State { 12 | public enum Type { 13 | TIME_STATE 14 | } 15 | 16 | protected final Type type; 17 | 18 | public State(Type type) { this.type = type; } 19 | 20 | public Type getType() { return this.type; } 21 | 22 | public JSONObject toJson() throws JSONException { 23 | throw new JSONException("Serialization not implemented."); 24 | } 25 | 26 | public StateInput toStateInput() { 27 | ErrorRepository.captureException(new Exception("Attempted to call toAnnotationTarget, but not implemented.")); 28 | return null; 29 | } 30 | 31 | public static State fromJson(JSONObject json) throws JSONException { 32 | String type = json.getString("type"); 33 | if (type.equals(Type.TIME_STATE.name())) { 34 | return TimeState.fromJson(json); 35 | } 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/model/TextDirection.java: -------------------------------------------------------------------------------- 1 | package io.literal.model; 2 | 3 | public enum TextDirection { 4 | LTR, 5 | RTL, 6 | AUTO; 7 | 8 | public type.TextDirection toGraphQL() { 9 | return type.TextDirection.valueOf(this.name()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/repository/SharedPreferencesRepository.java: -------------------------------------------------------------------------------- 1 | package io.literal.repository; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | public class SharedPreferencesRepository { 7 | private static final String FILE_KEY = "io.literal.SHARED_PREFERENCES_REPOSITORY"; 8 | 9 | private static final String IS_SIGNED_OUT_KEY = "IS_SIGNED_OUT"; 10 | 11 | private static SharedPreferences getSharedPreferences(Context context) { 12 | return context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE); 13 | } 14 | 15 | public static boolean getIsSignedOut(Context context) { 16 | return getSharedPreferences(context).getBoolean(IS_SIGNED_OUT_KEY, false); 17 | } 18 | public static void setIsSignedOut(Context context, boolean hasSignedOut) { 19 | SharedPreferences.Editor editor = getSharedPreferences(context).edit(); 20 | editor.putBoolean(IS_SIGNED_OUT_KEY, hasSignedOut); 21 | editor.apply(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/repository/ToastRepository.java: -------------------------------------------------------------------------------- 1 | package io.literal.repository; 2 | 3 | import android.app.Activity; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | import android.widget.Toast; 9 | 10 | import io.literal.R; 11 | 12 | public class ToastRepository { 13 | 14 | public static int STYLE_DARK_PRIMARY = 0; 15 | public static int STYLE_DARK_ACCENT = 1; 16 | 17 | public static void show(Activity activity, int resourceId, int style) { 18 | LayoutInflater inflater = activity.getLayoutInflater(); 19 | 20 | View layout = inflater.inflate( 21 | style == STYLE_DARK_PRIMARY ? R.layout.toast_primary : R.layout.toast_accent, 22 | activity.findViewById(R.id.custom_toast_container) 23 | ); 24 | TextView textView = layout.findViewById(R.id.text); 25 | textView.setText(activity.getResources().getText(resourceId)); 26 | 27 | Toast toast = new Toast(activity.getApplicationContext()); 28 | toast.setDuration(Toast.LENGTH_LONG); 29 | toast.setView(layout); 30 | toast.show(); 31 | } 32 | 33 | public static void show(Activity activity, int textResourceId) { 34 | ToastRepository.show(activity, textResourceId, STYLE_DARK_PRIMARY); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/service/SplashService.java: -------------------------------------------------------------------------------- 1 | package io.literal.service; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.app.Activity; 6 | import android.view.View; 7 | import android.view.Window; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import io.literal.R; 12 | 13 | public class SplashService { 14 | 15 | public static void hide(Activity activity, @NotNull View view) { 16 | int shortAnimationDuration = activity.getResources().getInteger(android.R.integer.config_shortAnimTime); 17 | view.animate() 18 | .alpha(0f) 19 | .setDuration(shortAnimationDuration) 20 | .setListener(new AnimatorListenerAdapter() { 21 | @Override 22 | public void onAnimationEnd(Animator animation) { 23 | view.setVisibility(View.GONE); 24 | } 25 | }); 26 | setChromeColor(activity, R.color.colorDarkAccent); 27 | } 28 | 29 | public static boolean isVisible(@NotNull View view) { 30 | return view.getVisibility() == View.VISIBLE; 31 | } 32 | 33 | public static void show(Activity activity, @NotNull View view) { 34 | view.setVisibility(View.VISIBLE); 35 | setChromeColor(activity, R.color.colorPrimaryDark); 36 | } 37 | 38 | private static void setChromeColor(@NotNull Activity activity, int colorId) { 39 | Window window = activity.getWindow(); 40 | int color = activity.getResources().getColor(colorId, activity.getTheme()); 41 | 42 | window.setStatusBarColor(color); 43 | window.setNavigationBarColor(color); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/ui/MainApplication.java: -------------------------------------------------------------------------------- 1 | package io.literal.ui; 2 | 3 | import android.app.Application; 4 | 5 | import java.util.concurrent.BlockingQueue; 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | import java.util.concurrent.ThreadPoolExecutor; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public class MainApplication extends Application { 11 | private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); 12 | private final BlockingQueue workQueue = new LinkedBlockingQueue(); 13 | private static final int KEEP_ALIVE_TIME = 1; 14 | private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; 15 | 16 | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 17 | NUMBER_OF_CORES, 18 | NUMBER_OF_CORES, 19 | KEEP_ALIVE_TIME, 20 | KEEP_ALIVE_TIME_UNIT, 21 | workQueue 22 | ); 23 | 24 | public ThreadPoolExecutor getThreadPoolExecutor() { 25 | return threadPoolExecutor; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/android/app/src/main/java/io/literal/ui/activity/InstrumentedActivity.java: -------------------------------------------------------------------------------- 1 | package io.literal.ui.activity; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.annotation.Nullable; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import io.literal.lib.Thunk; 9 | import io.literal.repository.AnalyticsRepository; 10 | import io.literal.repository.ErrorRepository; 11 | 12 | public class InstrumentedActivity extends AppCompatActivity { 13 | 14 | private Thunk errorRepositoryCleanup; 15 | 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | errorRepositoryCleanup = ErrorRepository.initialize(); 20 | AnalyticsRepository.initialize(getApplication()); 21 | } 22 | 23 | @Override 24 | protected void onDestroy() { 25 | super.onDestroy(); 26 | 27 | errorRepositoryCleanup.invoke(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-hdpi/done_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-hdpi/done_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-hdpi/highlight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-hdpi/highlight_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-hdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-hdpi/ic_stat_name.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-hdpi/toast_accent_frame.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-mdpi/done_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-mdpi/done_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-mdpi/highlight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-mdpi/highlight_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-mdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-mdpi/ic_stat_name.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xhdpi/done_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xhdpi/done_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xhdpi/highlight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xhdpi/highlight_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xxhdpi/done_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xxhdpi/done_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xxhdpi/highlight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xxhdpi/highlight_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xxxhdpi/done_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xxxhdpi/done_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xxxhdpi/highlight_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xxxhdpi/highlight_white.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_baseline_build_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_baseline_more_vert_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/ic_logo.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/drawable/toast_primary_frame.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/font/font.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/font/roboto_mono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/font/roboto_mono.ttf -------------------------------------------------------------------------------- /packages/android/app/src/main/res/font/roboto_mono_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/app/src/main/res/font/roboto_mono_italic.ttf -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/activity_share_target_handler.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/authentication_handler.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/fragment_app_web_view.xml: -------------------------------------------------------------------------------- 1 | 8 | 14 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 16 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/toast_accent.xml: -------------------------------------------------------------------------------- 1 | 7 | 17 | 18 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/toast_primary.xml: -------------------------------------------------------------------------------- 1 | 7 | 17 | 18 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/layout/webview_toolbar_indeterminate_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/menu/source_webview_commit_edit_annotation_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/menu/source_webview_create_annotation_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/menu/source_webview_edit_annotation_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/menu/source_webview_toolbar_create.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/menu/source_webview_toolbar_read.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 14 | 19 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/values-v28/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #0C0C0C 4 | #0C0C0C 5 | #2A2A2A 6 | #2A2A2A 7 | #E0E0E0 8 | #F2FFFFFF 9 | #99FFFFFF 10 | #EB000000 11 | 12 | -------------------------------------------------------------------------------- /packages/android/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18dp 4 | 4dp 5 | 6 | -------------------------------------------------------------------------------- /packages/android/app/src/test/java/io/literal/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.literal; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /packages/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | 3 | repositories { 4 | maven { 5 | url "https://plugins.gradle.org/m2/" 6 | } 7 | google() 8 | jcenter() 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:4.0.1' 14 | classpath 'com.amazonaws:aws-android-sdk-appsync-gradle-plugin:3.1.0' 15 | 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | maven { 24 | url "https://plugins.gradle.org/m2/" 25 | } 26 | google() 27 | jcenter() 28 | mavenCentral() 29 | } 30 | } 31 | 32 | task clean(type: Delete) { 33 | delete rootProject.buildDir 34 | } 35 | -------------------------------------------------------------------------------- /packages/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /packages/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 05 13:11:17 EDT 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /packages/android/scripts/amplify-pull-production.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | amplify pull --appId d3kn1jmjzilue --envName production 4 | -------------------------------------------------------------------------------- /packages/android/scripts/amplify-pull-staging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | amplify pull --appId d3h0fixyxiyfg5 --envName staging 4 | -------------------------------------------------------------------------------- /packages/android/scripts/create-foss-amplify-and-aws-configuration.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const amplifyConfiguration = require("../app/src/main/res/raw/amplifyconfiguration.json"); 6 | const awsConfiguration = require("../app/src/main/res/raw/awsconfiguration.json"); 7 | const fossPath = path.resolve(__dirname, "../app/src/foss/res/raw"); 8 | 9 | amplifyConfiguration.auth.plugins.awsCognitoAuthPlugin.Auth.Default.OAuth = undefined; 10 | amplifyConfiguration.auth.plugins.awsCognitoAuthPlugin.CognitoUserPool.Default.AppClientSecret = undefined; 11 | awsConfiguration.Auth.Default.OAuth = undefined; 12 | awsConfiguration.CognitoUserPool.Default.AppClientSecret = undefined; 13 | 14 | fs.writeFileSync( 15 | path.resolve(fossPath, "amplifyconfiguration.json"), 16 | JSON.stringify(amplifyConfiguration) 17 | ); 18 | fs.writeFileSync( 19 | path.resolve(fossPath, "awsconfiguration.json"), 20 | JSON.stringify(awsConfiguration) 21 | ); 22 | -------------------------------------------------------------------------------- /packages/android/scripts/graphql-sync-schema-production.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | OWN_PATH=`realpath $0` 5 | ROOT_DIR=`realpath "$(dirname $OWN_PATH)/../"` 6 | AWS_PROFILE=amplify-literal 7 | API_ID=rkihsloeqbdabdpebem3fqqjuy 8 | 9 | aws appsync get-introspection-schema \ 10 | --format JSON \ 11 | --api-id $API_ID \ 12 | --profile $AWS_PROFILE \ 13 | "$ROOT_DIR/app/src/main/graphql/schema.json" 14 | 15 | # Warning: overrites any manual tuning in autogenerated files 16 | # amplify codegen 17 | -------------------------------------------------------------------------------- /packages/android/scripts/graphql-sync-schema-staging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | OWN_PATH=`realpath $0` 5 | ROOT_DIR=`realpath "$(dirname $OWN_PATH)/../"` 6 | AWS_PROFILE=amplify-literal 7 | API_ID=to6hgjbvbjb7roq46jyzhxr2my 8 | 9 | aws appsync get-introspection-schema \ 10 | --format JSON \ 11 | --api-id $API_ID \ 12 | --profile $AWS_PROFILE \ 13 | "$ROOT_DIR/app/src/main/graphql/schema.json" 14 | 15 | # Warning: overrites any manual tuning in autogenerated files 16 | # amplify codegen 17 | -------------------------------------------------------------------------------- /packages/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='Literal' 2 | include ':app' 3 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/.nvmrc: -------------------------------------------------------------------------------- 1 | v15.9.0 2 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/annotation-renderer/messenger.mjs: -------------------------------------------------------------------------------- 1 | export class Messenger { 2 | constructor() { 3 | this.handlers = new Map(); 4 | this.eventQueue = []; 5 | 6 | window.addEventListener("message", (ev) => { 7 | if (ev.ports && ev.ports.length > 0 && !window.literalMessagePort) { 8 | window.literalMessagePort = ev.ports[0]; 9 | this.eventQueue.forEach(this.postMessage); 10 | this.eventQueue = []; 11 | } 12 | 13 | this._handleMessage(ev); 14 | }); 15 | 16 | if (!window.literalMessagePort && window.literalWebview) { 17 | window.literalWebview.sendMessagePort(); 18 | } 19 | } 20 | 21 | _handleMessage(ev) { 22 | try { 23 | const data = JSON.parse(ev.data); 24 | 25 | console.log("[Literal] Receieved message", ev.data); 26 | if (this.handlers.has(data.type)) { 27 | this.handlers.get(data.type).forEach((handler) => handler(data)); 28 | } 29 | } catch (e) { 30 | console.log("[Literal] Unable to parse message", ev.data); 31 | } 32 | } 33 | 34 | postMessage(ev) { 35 | if (!window.literalMessagePort) { 36 | this.eventQueue.push(ev); 37 | return; 38 | } 39 | console.log("[Literal] postMessage: " + JSON.stringify(ev)) 40 | window.literalMessagePort.postMessage(JSON.stringify(ev)); 41 | } 42 | 43 | on(type, handler) { 44 | if (!this.handlers.has(type)) { 45 | this.handlers.set(type, [handler]); 46 | } else { 47 | this.handlers.get(type).push(handler); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/annotation-renderer/util.mjs: -------------------------------------------------------------------------------- 1 | export const isElementInViewport = (elem) => { 2 | const rect = elem.getBoundingClientRect(); 3 | return ( 4 | rect.top >= 0 && 5 | rect.left >= 0 && 6 | rect.bottom <= 7 | (window.innerHeight || document.documentElement.clientHeight) && 8 | rect.right <= (window.innerWidth || document.documentElement.clientWidth) 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" && npm run build 4 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/get-annotation-bounding-box/index.mjs: -------------------------------------------------------------------------------- 1 | import "core-js/index.js"; 2 | import "regenerator-runtime/runtime.js"; 3 | 4 | import { 5 | get as storageGet, 6 | initialize as storageInitialize, 7 | } from "../shared/storage.mjs"; 8 | const ANNOTATION = JSON.parse(process.env.PARAM_ANNOTATION); 9 | 10 | export default () => { 11 | storageInitialize(); 12 | 13 | const ranges = storageGet("annotationRanges"); 14 | if (!ranges || !ranges[ANNOTATION.id]) { 15 | console.error("[Literal] Unable to find range for annotation."); 16 | return; 17 | } 18 | 19 | const boundingBox = ranges[ANNOTATION.id].getBoundingClientRect(); 20 | return { 21 | left: boundingBox.left * window.devicePixelRatio, 22 | top: boundingBox.top * window.devicePixelRatio, 23 | right: boundingBox.right * window.devicePixelRatio, 24 | bottom: boundingBox.bottom * window.devicePixelRatio, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/get-annotation/index.mjs: -------------------------------------------------------------------------------- 1 | import "core-js/index.js"; 2 | import "regenerator-runtime/runtime.js"; 3 | 4 | import { makeAnnotationFromSelection } from "./model.mjs"; 5 | import { 6 | get as storageGet, 7 | KEY_SERVICE_HIGHLIGHTER, 8 | KEY_SERVICE_ANNOTATION_FOCUS_MANAGER, 9 | } from "../shared/storage.mjs"; 10 | 11 | export default () => { 12 | const highlighterService = storageGet(KEY_SERVICE_HIGHLIGHTER); 13 | const annotationFocusManagerService = storageGet( 14 | KEY_SERVICE_ANNOTATION_FOCUS_MANAGER 15 | ); 16 | 17 | if (!highlighterService || !annotationFocusManagerService) { 18 | console.error( 19 | "[Literal] Expected annotationFocusManager and highlighter services, but found null.", 20 | highlighterService, 21 | annotationFocusManagerService 22 | ); 23 | 24 | return null; 25 | } 26 | 27 | annotationFocusManagerService._handleBlurAnnotation({ disableNotify: true }) 28 | highlighterService.removeHighlights() 29 | 30 | const output = 31 | window.getSelection().rangeCount > 0 32 | ? makeAnnotationFromSelection({ selection: window.getSelection() }) 33 | : null; 34 | 35 | window.getSelection().removeAllRanges(); 36 | 37 | return output; 38 | }; 39 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/get-scripts/index.mjs: -------------------------------------------------------------------------------- 1 | import "core-js/index.js"; 2 | import "regenerator-runtime/runtime.js"; 3 | 4 | export default () => { 5 | return Array.from(document.scripts).map((scriptElem) => ({ 6 | attributes: Array.from(scriptElem.attributes).reduce( 7 | (agg, attr) => ({ ...agg, [attr.name]: attr.value }), 8 | {} 9 | ), 10 | text: scriptElem.text, 11 | })); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "source-webview-scripts", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "build": "npm run build:webpack && npm run build:wrap-executor", 9 | "build:webpack": "webpack", 10 | "build:wrap-executor": "node ./wrap-dist-with-executor.mjs" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/core": "^7.13.16", 16 | "@babel/preset-env": "^7.13.15", 17 | "babel-loader": "^8.2.2", 18 | "core-js": "^3.11.1", 19 | "regenerator-runtime": "^0.13.7", 20 | "webpack": "^5.24.3", 21 | "webpack-cli": "^4.5.0" 22 | }, 23 | "dependencies": { 24 | "p-wait-for": "^3.2.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/shared/storage.mjs: -------------------------------------------------------------------------------- 1 | const NAMESPACE = "__literal"; 2 | 3 | export const KEY_SERVICE_HIGHLIGHTER = "SERVICE_HIGHLIGHTER" 4 | export const KEY_SERVICE_ANNOTATION_FOCUS_MANAGER = "SERVICE_ANNOTATION_FOCUS_MANAGER" 5 | 6 | export const initialize = () => { 7 | window[NAMESPACE] = window[NAMESPACE] || {}; 8 | }; 9 | 10 | export const set = (key, value) => { 11 | window[NAMESPACE][key] = value; 12 | }; 13 | 14 | export const get = (key) => window[NAMESPACE][key]; 15 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/shared/xpath.mjs: -------------------------------------------------------------------------------- 1 | export const evaluate = (value) => 2 | document.evaluate( 3 | value, 4 | document, 5 | null, 6 | XPathResult.FIRST_ORDERED_NODE_TYPE, 7 | null 8 | ).singleNodeValue; 9 | 10 | export const xPathRangeSelectorPredicate = ({ 11 | type, 12 | startSelector, 13 | endSelector, 14 | }) => 15 | type === "RANGE_SELECTOR" && 16 | startSelector.type === "XPATH_SELECTOR" && 17 | (startSelector.refinedBy || []).some( 18 | (refinedBySelector) => refinedBySelector.type === "TEXT_POSITION_SELECTOR" 19 | ) && 20 | endSelector.type === "XPATH_SELECTOR" && 21 | (endSelector.refinedBy || []).some( 22 | (refinedBySelector) => refinedBySelector.type === "TEXT_POSITION_SELECTOR" 23 | ); 24 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/webpack.config.js: -------------------------------------------------------------------------------- 1 | const resolvePath = (path) => new URL(path, import.meta.url).pathname; 2 | 3 | const config = { 4 | mode: "production", 5 | optimization: { 6 | minimize: false, 7 | }, 8 | entry: { 9 | GetAnnotation: resolvePath("./get-annotation/index.mjs"), 10 | AnnotationRenderer: resolvePath("./annotation-renderer/index.mjs"), 11 | GetAnnotationBoundingBox: resolvePath( 12 | "./get-annotation-bounding-box/index.mjs" 13 | ), 14 | GetScripts: resolvePath("./get-scripts/index.mjs"), 15 | }, 16 | output: { 17 | filename: "SourceWebView[name].js", 18 | path: resolvePath("./dist"), 19 | library: "Literal", 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.m?js$/, 25 | exclude: [ 26 | // \\ for Windows, \/ for Mac OS and Linux 27 | /node_modules[\\\/]core-js/, 28 | /node_modules[\\\/]webpack[\\\/]buildin/, 29 | ], 30 | use: { 31 | loader: "babel-loader", 32 | options: { 33 | presets: ["@babel/preset-env"], 34 | }, 35 | }, 36 | }, 37 | ], 38 | }, 39 | }; 40 | 41 | export default config; 42 | -------------------------------------------------------------------------------- /packages/android/source-webview-scripts/wrap-dist-with-executor.mjs: -------------------------------------------------------------------------------- 1 | import { readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs"; 2 | 3 | const OUTPUT_DIR = new URL("../app/src/main/assets/", import.meta.url); 4 | const INPUT_DIR = new URL("./dist/", import.meta.url); 5 | 6 | const wrapExecutor = (contents) => contents + "\n Literal.default();"; 7 | 8 | mkdirSync(OUTPUT_DIR, { recursive: true }) 9 | 10 | readdirSync(INPUT_DIR).forEach((fileName) => { 11 | writeFileSync( 12 | new URL(fileName, OUTPUT_DIR), 13 | wrapExecutor(readFileSync(new URL(fileName, INPUT_DIR))) 14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.bs.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/apollo-link-analytics", 3 | "sources": { 4 | "dir": "src", 5 | "subdirs": true 6 | }, 7 | "reason": { 8 | "react-jsx": 3 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": [ 18 | "reason-apollo" 19 | ], 20 | "bs-devDependencies": [ 21 | "reason-react" 22 | ], 23 | "bsc-flags": [ 24 | "-bs-super-errors" 25 | ], 26 | "ppx-flags": [], 27 | "refmt": 3 28 | } 29 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/apollo-link-analytics", 3 | "version": "1.0.0", 4 | "main": "./src/ApolloLinkAnalytics.bs.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "bsb:build": "bsb -make-world", 8 | "bsb:start": "bsb -make-world -w", 9 | "bsb:clean": "bsb -clean-world", 10 | "start": "npm run bsb:start" 11 | }, 12 | "author": { 13 | "name": "Daniel Ramirez", 14 | "email": "javamonn@hey.com", 15 | "url": "https://javamonn.com" 16 | }, 17 | "repository": "https://github.com/literal-io/literal/tree/chore-fdroid/packages/apollo-link-analytics", 18 | "description": "Automatic product analytics derived from GraphQL operations executed by Apollo.", 19 | "keywords": [ 20 | "bucklescript", 21 | "reason", 22 | "apollo", 23 | "graphql", 24 | "analytics" 25 | ], 26 | "license": "MIT - Hippocratic 1.2", 27 | "dependencies": { 28 | "apollo-link": "1.2.3", 29 | "reason-apollo": "^0.20.0" 30 | }, 31 | "peerDependencies": { 32 | "bs-platform": "^8.4.2" 33 | }, 34 | "devDependencies": { 35 | "bs-platform": "^8.4.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/src/ApolloLink.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | var Observable = {}; 6 | 7 | function query(param) { 8 | return param.query; 9 | } 10 | 11 | function variables(param) { 12 | return param.variables; 13 | } 14 | 15 | function operationName(param) { 16 | return param.operationName; 17 | } 18 | 19 | function extensions(param) { 20 | return param.extensions; 21 | } 22 | 23 | function setContext(param) { 24 | return param.setContext; 25 | } 26 | 27 | function getContext(param) { 28 | return param.getContext; 29 | } 30 | 31 | function toKey(param) { 32 | return param.toKey; 33 | } 34 | 35 | exports.Observable = Observable; 36 | exports.query = query; 37 | exports.variables = variables; 38 | exports.operationName = operationName; 39 | exports.extensions = extensions; 40 | exports.setContext = setContext; 41 | exports.getContext = getContext; 42 | exports.toKey = toKey; 43 | /* No side effect */ 44 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/src/ApolloLink.re: -------------------------------------------------------------------------------- 1 | module Observable = { 2 | type t; 3 | 4 | [@bs.send] external map: (t, Js.Json.t => Js.Json.t) => t = "map"; 5 | }; 6 | 7 | [@bs.deriving accessors] 8 | type operation = { 9 | query: Js.Json.t, 10 | variables: Js.Json.t, 11 | operationName: string, 12 | extensions: Js.Json.t, 13 | setContext: Js.Json.t => Js.Json.t, 14 | getContext: unit => Js.Json.t, 15 | toKey: unit => string, 16 | }; 17 | 18 | type requestHandler = 19 | (operation, operation => Observable.t) => option(Observable.t); 20 | 21 | [@bs.new] [@bs.module "apollo-link"] 22 | external make: requestHandler => ReasonApolloTypes.apolloLink = "ApolloLink"; 23 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/src/ApolloLinkAnalytics.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var Curry = require("bs-platform/lib/js/curry.js"); 5 | var Js_option = require("bs-platform/lib/js/js_option.js"); 6 | var ApolloLink = require("apollo-link"); 7 | 8 | function make(onOperation) { 9 | return new ApolloLink.ApolloLink((function (operation, forward) { 10 | return Js_option.some(Curry._1(forward, operation).map(function (data) { 11 | Curry._2(onOperation, operation, data); 12 | return data; 13 | })); 14 | })); 15 | } 16 | 17 | exports.make = make; 18 | /* apollo-link Not a pure module */ 19 | -------------------------------------------------------------------------------- /packages/apollo-link-analytics/src/ApolloLinkAnalytics.re: -------------------------------------------------------------------------------- 1 | let make = onOperation => 2 | ApolloLink.make((operation, forward) => { 3 | forward(operation) 4 | ->ApolloLink.Observable.map(data => { 5 | onOperation(operation, data); 6 | data; 7 | }) 8 | ->Js.Option.some 9 | }); 10 | -------------------------------------------------------------------------------- /packages/bs-amazon-cognito-identity-js/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/bs-amazon-cognito-identity-js/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-amazon-cognito-identity-js", 3 | "reason": { 4 | "react-jsx": 3 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": [], 18 | "ppx-flags": [], 19 | "refmt": 3 20 | } 21 | -------------------------------------------------------------------------------- /packages/bs-amazon-cognito-identity-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-amazon-cognito-identity-js", 3 | "version": "1.0.0", 4 | "main": "src/AmazonCognitoIdentity.bs.js", 5 | "description": "BuckleScript bindings for amazon-cognito-identity-js.", 6 | "keywords": [ 7 | "bucklescript", 8 | "reason", 9 | "amazon-cognito-identity-js" 10 | ], 11 | "repository": "https://github.com/literal-io/literal/tree/master/packages/apollo-link-analytics", 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": { 16 | "name": "Daniel Ramirez", 17 | "email": "javamonn@hey.com", 18 | "url": "https://javamonn.com" 19 | }, 20 | "license": "MIT - Hippocratic 1.2", 21 | "devDependencies": { 22 | "amazon-cognito-identity-js": "^4.5.0", 23 | "bs-platform": "^8.4.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/bs-amplitude/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.bs.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/bs-amplitude/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-amplitude", 3 | "reason": { 4 | "react-jsx": 3 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": [], 18 | "ppx-flags": [], 19 | "refmt": 3 20 | } 21 | -------------------------------------------------------------------------------- /packages/bs-amplitude/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-amplitude", 3 | "version": "1.0.0", 4 | "description": "BuckleScript bindings for amplitude-js.", 5 | "repository": "https://github.com/literal-io/literal/tree/master/packages/apollo-link-analytics", 6 | "keywords": [ 7 | "bucklescript", 8 | "reason", 9 | "amplitude", 10 | "amplitude-js" 11 | ], 12 | "main": "index.js", 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1", 15 | "bsb:build": "bsb -make-world", 16 | "bsb:start": "bsb -make-world -w", 17 | "bsb:clean": "bsb -clean-world", 18 | "start": "npm run bsb:start" 19 | }, 20 | "author": "Daniel Ramirez ", 21 | "license": "MIT - Hippocratic 1.2", 22 | "peerDependencies": { 23 | "bs-platform": "^8.4.2" 24 | }, 25 | "devDependencies": { 26 | "bs-platform": "^8.4.2" 27 | }, 28 | "dependencies": { 29 | "amplitude-js": "^7.2.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/bs-amplitude/src/Amplitude.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | var getInstance = (function () { 6 | if (window) { 7 | return require("amplitude-js") 8 | } else { 9 | return { 10 | init: () => {}, 11 | logWithEvent: () => {} 12 | } 13 | } 14 | }); 15 | 16 | exports.getInstance = getInstance; 17 | /* No side effect */ 18 | -------------------------------------------------------------------------------- /packages/bs-aws-amplify/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/bs-aws-amplify/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-amplify", 3 | "reason": { 4 | "react-jsx": 3 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": ["bs-webapi", "@literal-io/bs-amazon-cognito-identity-js"], 18 | "ppx-flags": [], 19 | "refmt": 3 20 | } 21 | -------------------------------------------------------------------------------- /packages/bs-aws-amplify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-amplify", 3 | "version": "1.0.1", 4 | "description": "BuckleScript bindings for aws-amplify.", 5 | "keywords": [ 6 | "bucklescript", 7 | "reason", 8 | "aws-amplify" 9 | ], 10 | "main": "src/AwsAmplify.bs.js", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "bsb:build": "bsb -make-world", 14 | "bsb:start": "bsb -make-world -w", 15 | "bsb:clean": "bsb -clean-world", 16 | "start": "npm run bsb:start" 17 | }, 18 | "author": { 19 | "name": "Daniel Ramirez", 20 | "email": "javamonn@hey.com", 21 | "url": "https://javamonn.com" 22 | }, 23 | "repository": "https://github.com/literal-io/literal/tree/master/packages/apollo-link-analytics", 24 | "license": "MIT - Hippocratic 1.2", 25 | "dependencies": { 26 | "@aws-amplify/api": "^3.1.23", 27 | "@aws-amplify/auth": "^3.3.5", 28 | "@aws-amplify/cache": "^3.1.23", 29 | "@aws-amplify/core": "^3.4.6", 30 | "@aws-amplify/pubsub": "^3.0.24", 31 | "@aws-amplify/storage": "^3.2.13", 32 | "@literal-io/bs-amazon-cognito-identity-js": "^1.0.0", 33 | "bs-webapi": "^0.19.1" 34 | }, 35 | "peerDependencies": { 36 | "bs-platform": "^8.4.2" 37 | }, 38 | "devDependencies": { 39 | "bs-platform": "^8.4.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/bs-aws-appsync/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.bs.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/bs-aws-appsync/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-appsync", 3 | "reason": { 4 | "react-jsx": 3 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": [ 18 | "reason-react", 19 | "reason-apollo", 20 | "@literal-io/bs-aws-amplify", 21 | "@literal-io/bs-amazon-cognito-identity-js" 22 | ], 23 | "ppx-flags": [], 24 | "refmt": 3 25 | } 26 | -------------------------------------------------------------------------------- /packages/bs-aws-appsync/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-appsync", 3 | "version": "1.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@literal-io/bs-aws-appsync", 9 | "version": "1.0.1", 10 | "license": "MIT - Hippocratic 1.2", 11 | "devDependencies": { 12 | "bs-platform": "^8.2.0" 13 | }, 14 | "peerDependencies": { 15 | "bs-platform": "^8.2.0" 16 | } 17 | }, 18 | "node_modules/bs-platform": { 19 | "version": "8.2.0", 20 | "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-8.2.0.tgz", 21 | "integrity": "sha512-quvmUac/ZxGDsT7L5+6RNXrLPvLHkWFownacaqlwVoyAm770bPyupTRU49ALPGk3HpjfD5eE+lpGdOSPtuwJiA==", 22 | "dev": true, 23 | "hasInstallScript": true, 24 | "bin": { 25 | "bsb": "bsb", 26 | "bsc": "bsc", 27 | "bsrefmt": "bsrefmt", 28 | "bstracing": "lib/bstracing" 29 | } 30 | } 31 | }, 32 | "dependencies": { 33 | "bs-platform": { 34 | "version": "8.2.0", 35 | "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-8.2.0.tgz", 36 | "integrity": "sha512-quvmUac/ZxGDsT7L5+6RNXrLPvLHkWFownacaqlwVoyAm770bPyupTRU49ALPGk3HpjfD5eE+lpGdOSPtuwJiA==", 37 | "dev": true 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/bs-aws-appsync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-appsync", 3 | "version": "1.0.1", 4 | "main": "src/AwsAppSync.bs.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "bsb:build": "bsb -make-world", 8 | "bsb:start": "bsb -make-world -w", 9 | "bsb:clean": "bsb -clean-world", 10 | "start": "npm run bsb:start" 11 | }, 12 | "author": { 13 | "name": "Daniel Ramirez", 14 | "email": "javamonn@hey.com", 15 | "url": "https://javamonn.com" 16 | }, 17 | "repository": "https://github.com/literal-io/literal/tree/master/packages/bs-aws-appsync", 18 | "description": "BuckleScript bindings to aws-appsync.", 19 | "keywords": [ 20 | "bucklescript", 21 | "reason", 22 | "aws-appsync" 23 | ], 24 | "license": "MIT - Hippocratic 1.2", 25 | "dependencies": { 26 | "aws-appsync": "^4.0.0", 27 | "aws-appsync-react": "^4.0.0", 28 | "reason-apollo": "^0.20.0", 29 | "reason-react": "^0.9.1", 30 | "@literal-io/bs-aws-amplify": "1.0.0", 31 | "@literal-io/bs-amazon-cognito-identity-js": "1.0.0" 32 | }, 33 | "peerDependencies": { 34 | "bs-platform": "^8.4.2" 35 | }, 36 | "devDependencies": { 37 | "bs-platform": "^8.4.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/bs-aws-appsync/src/AwsAppSync.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | function authWithCognitoUserPools(jwtToken) { 6 | return { 7 | type: "AMAZON_COGNITO_USER_POOLS", 8 | jwtToken: jwtToken 9 | }; 10 | } 11 | 12 | function authWithIAM(credentials) { 13 | return { 14 | type: "AWS_IAM", 15 | credentials: credentials 16 | }; 17 | } 18 | 19 | var Client = { 20 | authWithCognitoUserPools: authWithCognitoUserPools, 21 | authWithIAM: authWithIAM 22 | }; 23 | 24 | var Rehydrated = {}; 25 | 26 | exports.Client = Client; 27 | exports.Rehydrated = Rehydrated; 28 | /* No side effect */ 29 | -------------------------------------------------------------------------------- /packages/bs-aws-appsync/src/AwsAppSync.re: -------------------------------------------------------------------------------- 1 | module Client = { 2 | [@bs.deriving abstract] 3 | type authOptions = { 4 | [@bs.as "type"] 5 | type_: string, 6 | [@bs.optional] 7 | jwtToken: unit => Js.Promise.t(AmazonCognitoIdentity.JwtToken.t), 8 | [@bs.optional] 9 | credentials: unit => Js.Promise.t(AwsAmplify.Credentials.t), 10 | }; 11 | 12 | let authWithCognitoUserPools = (~jwtToken) => 13 | authOptions(~type_="AMAZON_COGNITO_USER_POOLS", ~jwtToken, ()); 14 | let authWithIAM = (~credentials) => 15 | authOptions(~type_="AWS_IAM", ~credentials, ()); 16 | 17 | type appSyncLinkOptions = { 18 | url: string, 19 | region: string, 20 | auth: option(authOptions), 21 | disableOffline: bool, 22 | mandatorySignIn: bool, 23 | complexObjectsCredentials: 24 | unit => Js.Promise.t(AwsAmplify.Credentials.t), 25 | }; 26 | [@bs.module "aws-appsync"] 27 | external createAppSyncLink: 28 | appSyncLinkOptions => ReasonApolloTypes.apolloLink = 29 | "createAppSyncLink"; 30 | 31 | [@bs.new] [@bs.module "aws-appsync"] 32 | external make: appSyncLinkOptions => ApolloClient.generatedApolloClient = 33 | "default"; 34 | 35 | type makeWithApolloOptions = { 36 | link: option(ReasonApolloTypes.apolloLink), 37 | cache: option(ReasonApolloTypes.apolloCache), 38 | }; 39 | 40 | [@bs.new] [@bs.module "aws-appsync"] 41 | external makeWithOptions: 42 | (appSyncLinkOptions, makeWithApolloOptions) => 43 | ApolloClient.generatedApolloClient = 44 | "default"; 45 | }; 46 | 47 | module Rehydrated = { 48 | type renderProps = {rehydrated: bool}; 49 | [@bs.module "aws-appsync-react"] [@react.component] 50 | external make: 51 | (~render: renderProps => React.element=?, ~children: React.element=?) => 52 | React.element = 53 | "Rehydrated"; 54 | }; 55 | -------------------------------------------------------------------------------- /packages/bs-aws-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/bs-aws-sdk/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-sdk", 3 | "reason": { 4 | "react-jsx": 3 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": [], 18 | "ppx-flags": [], 19 | "refmt": 3 20 | } 21 | -------------------------------------------------------------------------------- /packages/bs-aws-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-aws-sdk", 3 | "version": "1.0.0", 4 | "main": "src/AWSSDK.bs.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "bsb:build": "bsb -make-world", 8 | "bsb:start": "bsb -make-world -w", 9 | "bsb:clean": "bsb -clean-world", 10 | "start": "npm run bsb:start", 11 | "build": "npm run bsb:build" 12 | }, 13 | "author": { 14 | "name": "Daniel Ramirez", 15 | "email": "javamonn@hey.com", 16 | "url": "https://javamonn.com" 17 | }, 18 | "repository": "https://github.com/literal-io/literal/tree/master/packages/bs-aws-sdk", 19 | "description": "BuckleScript bindings for aws-sdk.", 20 | "keywords": [ 21 | "bucklescript", 22 | "reason", 23 | "aws-sdk" 24 | ], 25 | "license": "MIT - Hippocratic 1.2", 26 | "peerDependencies": { 27 | "aws-sdk": "^2.541.0", 28 | "bs-platform": "^8.4.2" 29 | }, 30 | "devDependencies": { 31 | "aws-sdk": "^2.541.0", 32 | "bs-platform": "^8.4.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/bs-sentry/.gitignore: -------------------------------------------------------------------------------- 1 | lib/bs/ 2 | lib/ocaml/ 3 | src/**/*.js 4 | .merlin 5 | .bsb.lock 6 | node_modules 7 | 8 | -------------------------------------------------------------------------------- /packages/bs-sentry/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-sentry", 3 | "reason": { 4 | "react-jsx": 3 5 | }, 6 | "sources": { 7 | "dir": "src", 8 | "subdirs": true 9 | }, 10 | "package-specs": [ 11 | { 12 | "module": "es6-global", 13 | "in-source": true 14 | } 15 | ], 16 | "suffix": ".bs.js", 17 | "bs-dependencies": [], 18 | "ppx-flags": [], 19 | "refmt": 3 20 | } 21 | -------------------------------------------------------------------------------- /packages/bs-sentry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/bs-sentry", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "bsb:build": "bsb -make-world", 7 | "bsb:start": "bsb -make-world -w", 8 | "bsb:clean": "bsb -clean-world" 9 | }, 10 | "author": { 11 | "name": "Daniel Ramirez", 12 | "email": "javamonn@hey.com", 13 | "url": "https://javamonn.com" 14 | }, 15 | "repository": "https://github.com/literal-io/literal/tree/master/packages/bs-sentry", 16 | "description": "BuckleScript bindings for @sentry/browser.", 17 | "keywords": [ 18 | "bucklescript", 19 | "reason", 20 | "sentry" 21 | ], 22 | "license": "MIT - Hippocratic 1.2", 23 | "devDependencies": { 24 | "@sentry/browser": "^5.24.2", 25 | "bs-platform": "^8.4.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/bs-sentry/src/SentryBrowser.re: -------------------------------------------------------------------------------- 1 | [@bs.deriving abstract] 2 | type config = { 3 | dsn: string, 4 | normalizeDepth: int, 5 | }; 6 | 7 | [@bs.module "@sentry/browser"] external init: config => unit = "init"; 8 | 9 | type exceptionContext = { 10 | tags: option(Js.Dict.t(string)), 11 | extra: option(Js.Json.t), 12 | user: option(Js.Json.t), 13 | level: option(Js.Json.t), 14 | fingerprint: option(Js.Json.t), 15 | }; 16 | 17 | let makeExceptionContext = 18 | (~tags=?, ~extra=?, ~user=?, ~level=?, ~fingerprint=?, ()) => { 19 | tags, 20 | extra, 21 | user, 22 | level, 23 | fingerprint, 24 | }; 25 | 26 | [@bs.module "@sentry/browser"] 27 | external captureExceptionWithContext: (Js.Exn.t, exceptionContext) => unit = 28 | "captureException"; 29 | 30 | [@bs.module "@sentry/browser"] 31 | external captureException: Js.Exn.t => unit = "captureException"; 32 | 33 | [@bs.module "@sentry/browser"] 34 | external captureMessage: string => unit = "captureMessage"; 35 | 36 | [@bs.module "@sentry/browser"] 37 | external setContext: (string, Js.Json.t) => unit = "setContext"; 38 | 39 | [@bs.module "@sentry/browser"] 40 | external lastEventId: unit => string = "lastEventId"; 41 | -------------------------------------------------------------------------------- /packages/bs-sentry/src/SentryNode.re: -------------------------------------------------------------------------------- 1 | type config = { 2 | dsn: string, 3 | normalizeDepth: option(int), 4 | }; 5 | 6 | [@bs.module "@sentry/node"] external init: config => unit = "init"; 7 | 8 | type exceptionContext = { 9 | tags: option(Js.Dict.t(string)), 10 | extra: option(Js.Json.t), 11 | user: option(Js.Json.t), 12 | level: option(Js.Json.t), 13 | fingerprint: option(Js.Json.t), 14 | }; 15 | 16 | let makeExceptionContext = 17 | (~tags=?, ~extra=?, ~user=?, ~level=?, ~fingerprint=?, ()) => { 18 | tags, 19 | extra, 20 | user, 21 | level, 22 | fingerprint, 23 | }; 24 | 25 | [@bs.module "@sentry/node"] 26 | external captureExceptionWithContext: (Js.Exn.t, exceptionContext) => unit = 27 | "captureException"; 28 | 29 | [@bs.module "@sentry/node"] 30 | external captureException: Js.Exn.t => unit = "captureException"; 31 | 32 | [@bs.module "@sentry/node"] 33 | external captureMessage: string => unit = "captureMessage"; 34 | 35 | [@bs.module "@sentry/node"] 36 | external setContext: (string, Js.Json.t) => unit = "setContext"; 37 | 38 | [@bs.module "@sentry/node"] 39 | external lastEventId: unit => string = "lastEventId"; 40 | -------------------------------------------------------------------------------- /packages/model/.gitignore: -------------------------------------------------------------------------------- 1 | src/**/*.bs.js 2 | .merlin 3 | .bsb.lock 4 | lib 5 | node_modules 6 | -------------------------------------------------------------------------------- /packages/model/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/model", 3 | "version": "1.0.0", 4 | "reason": { 5 | "react-jsx": 3 6 | }, 7 | "sources": [ 8 | { 9 | "dir": "src", 10 | "subdirs": true 11 | } 12 | ], 13 | "package-specs": { 14 | "module": "commonjs", 15 | "in-source": true 16 | }, 17 | "suffix": ".js", 18 | "bs-dependencies": [ 19 | "decco" 20 | ], 21 | "ppx-flags": [ 22 | "decco/ppx" 23 | ], 24 | "warnings": { 25 | "error": "+101" 26 | }, 27 | "namespace": "LiteralModel", 28 | "refmt": 3 29 | } 30 | -------------------------------------------------------------------------------- /packages/model/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/model", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@literal-io/model", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "bs-platform": "8.2.0", 13 | "decco": "1.3.0" 14 | } 15 | }, 16 | "node_modules/bs-platform": { 17 | "version": "8.2.0", 18 | "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-8.2.0.tgz", 19 | "integrity": "sha512-quvmUac/ZxGDsT7L5+6RNXrLPvLHkWFownacaqlwVoyAm770bPyupTRU49ALPGk3HpjfD5eE+lpGdOSPtuwJiA==", 20 | "dev": true, 21 | "hasInstallScript": true, 22 | "bin": { 23 | "bsb": "bsb", 24 | "bsc": "bsc", 25 | "bsrefmt": "bsrefmt", 26 | "bstracing": "lib/bstracing" 27 | } 28 | }, 29 | "node_modules/decco": { 30 | "version": "1.3.0", 31 | "resolved": "https://registry.npmjs.org/decco/-/decco-1.3.0.tgz", 32 | "integrity": "sha512-pednHg+Skby+sw193hbHTRLihZskmXrPsVMlqL9XpuLm+HfwMgS76lFRAkTGAi3435q0TBygTpt8McVTyk4Sfw==", 33 | "dev": true, 34 | "peerDependencies": { 35 | "bs-platform": "6 || 7 || 8" 36 | } 37 | } 38 | }, 39 | "dependencies": { 40 | "bs-platform": { 41 | "version": "8.2.0", 42 | "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-8.2.0.tgz", 43 | "integrity": "sha512-quvmUac/ZxGDsT7L5+6RNXrLPvLHkWFownacaqlwVoyAm770bPyupTRU49ALPGk3HpjfD5eE+lpGdOSPtuwJiA==", 44 | "dev": true 45 | }, 46 | "decco": { 47 | "version": "1.3.0", 48 | "resolved": "https://registry.npmjs.org/decco/-/decco-1.3.0.tgz", 49 | "integrity": "sha512-pednHg+Skby+sw193hbHTRLihZskmXrPsVMlqL9XpuLm+HfwMgS76lFRAkTGAi3435q0TBygTpt8McVTyk4Sfw==", 50 | "dev": true, 51 | "requires": {} 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/model/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/model", 3 | "version": "1.0.0", 4 | "description": "BuckleScript bindings for the Web Annotation data model as implemented by Literal.", 5 | "repository": "https://github.com/literal-io/literal/tree/master/packages/model", 6 | "main": "index.js", 7 | "scripts": { 8 | "bsb:build": "bsb -make-world", 9 | "bsb:start": "bsb -make-world -w", 10 | "bsb:clean": "bsb -clean-world" 11 | }, 12 | "author": { 13 | "name": "Daniel Ramirez", 14 | "email": "javamonn@hey.com", 15 | "url": "https://javamonn.com" 16 | }, 17 | "license": "MIT - Hippocratic 1.2", 18 | "devDependencies": { 19 | "bs-platform": "^8.4.2", 20 | "decco": "^1.3.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/model/src/Annotation.re: -------------------------------------------------------------------------------- 1 | [@decco] 2 | type t = { 3 | created: option(Js.Json.t), 4 | modified: option(Js.Json.t), 5 | id: option(string), 6 | target: 7 | array( 8 | [@decco.codec Target.codec] Target.t, 9 | ), 10 | body: 11 | option( 12 | array( 13 | [@decco.codec Body.codec] Body.t, 14 | ), 15 | ), 16 | [@decco.default "Annotation"] [@decco.key "__typename"] 17 | typename: string, 18 | }; 19 | 20 | let make = (~id=?, ~target, ~body=?, ~created=?, ~modified=?, ()) => { 21 | id, 22 | target, 23 | body, 24 | typename: "Annotation", 25 | created: 26 | created 27 | ->Belt.Option.getWithDefault( 28 | Js.Date.(make()->toISOString)->Js.Json.string, 29 | ) 30 | ->Js.Option.some, 31 | modified: 32 | modified 33 | ->Belt.Option.getWithDefault( 34 | Js.Date.(make()->toISOString)->Js.Json.string, 35 | ) 36 | ->Js.Option.some, 37 | }; 38 | 39 | let makeAnnotationFromGraphQL = (~makeTarget, ~makeBody, annotation) => 40 | make( 41 | ~id=annotation##id, 42 | ~target=annotation##target->Belt.Array.keepMap(makeTarget), 43 | ~body=? 44 | annotation##body->Belt.Option.map(a => a->Belt.Array.keepMap(makeBody)), 45 | (), 46 | ); 47 | 48 | let encode = t_encode; 49 | let decode = t_decode; 50 | -------------------------------------------------------------------------------- /packages/model/src/Format.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | function toString(t) { 6 | if (t === "TEXT_HTML") { 7 | return "TEXT_HTML"; 8 | } else if (t === "APPLICATION_OCTET_STREAM") { 9 | return "APPLICATION_OCTET_STREAM"; 10 | } else if (t === "TEXT_PLAIN") { 11 | return "TEXT_PLAIN"; 12 | } else if (t === "APPLICATION_X_MIMEARCHIVE") { 13 | return "APPLICATION_X_MIMEARCHIVE"; 14 | } else { 15 | return "IMAGE_PNG"; 16 | } 17 | } 18 | 19 | exports.toString = toString; 20 | /* No side effect */ 21 | -------------------------------------------------------------------------------- /packages/model/src/Format.re: -------------------------------------------------------------------------------- 1 | type t = [ 2 | | `TEXT_PLAIN 3 | | `TEXT_HTML 4 | | `APPLICATION_OCTET_STREAM 5 | | `APPLICATION_X_MIMEARCHIVE 6 | | `IMAGE_PNG 7 | ]; 8 | 9 | let toString = t => 10 | switch (t) { 11 | | `TEXT_PLAIN => "TEXT_PLAIN" 12 | | `TEXT_HTML => "TEXT_HTML" 13 | | `APPLICATION_X_MIMEARCHIVE => "APPLICATION_X_MIMEARCHIVE" 14 | | `APPLICATION_OCTET_STREAM => "APPLICATION_OCTET_STREAM" 15 | | `IMAGE_PNG => "IMAGE_PNG" 16 | }; 17 | -------------------------------------------------------------------------------- /packages/model/src/Language.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | function toString(t) { 6 | return "EN_US"; 7 | } 8 | 9 | exports.toString = toString; 10 | /* No side effect */ 11 | -------------------------------------------------------------------------------- /packages/model/src/Language.re: -------------------------------------------------------------------------------- 1 | type t = [ | `EN_US]; 2 | 3 | let toString = t => 4 | switch (t) { 5 | | `EN_US => "EN_US" 6 | }; 7 | -------------------------------------------------------------------------------- /packages/model/src/Motivation.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | function toString(t) { 6 | if (t === "HIGHLIGHTING") { 7 | return "HIGHLIGHTING"; 8 | } else if (t === "BOOKMARKING") { 9 | return "BOOKMARKING"; 10 | } else if (t === "DESCRIBING") { 11 | return "DESCRIBING"; 12 | } else if (t === "COMMENTING") { 13 | return "COMMENTING"; 14 | } else if (t === "IDENTIFYING") { 15 | return "IDENTIFYING"; 16 | } else if (t === "REPLYING") { 17 | return "REPLYING"; 18 | } else if (t === "ACCESSING") { 19 | return "ACCESSING"; 20 | } else if (t === "QUESTIONING") { 21 | return "QUESTIONING"; 22 | } else if (t === "CLASSIFYING") { 23 | return "CLASSIFYING"; 24 | } else if (t === "LINKING") { 25 | return "LINKING"; 26 | } else if (t === "EDITING") { 27 | return "EDITING"; 28 | } else if (t === "TAGGING") { 29 | return "TAGGING"; 30 | } else { 31 | return "MODERATING"; 32 | } 33 | } 34 | 35 | exports.toString = toString; 36 | /* No side effect */ 37 | -------------------------------------------------------------------------------- /packages/model/src/Motivation.re: -------------------------------------------------------------------------------- 1 | let toString = t => 2 | switch (t) { 3 | | `TAGGING => "TAGGING" 4 | | `ACCESSING => "ACCESSING" 5 | | `BOOKMARKING => "BOOKMARKING" 6 | | `CLASSIFYING => "CLASSIFYING" 7 | | `COMMENTING => "COMMENTING" 8 | | `DESCRIBING => "DESCRIBING" 9 | | `EDITING => "EDITING" 10 | | `HIGHLIGHTING => "HIGHLIGHTING" 11 | | `IDENTIFYING => "IDENTIFYING" 12 | | `LINKING => "LINKING" 13 | | `MODERATING => "MODERATING" 14 | | `QUESTIONING => "QUESTIONING" 15 | | `REPLYING => "REPLYING" 16 | }; 17 | -------------------------------------------------------------------------------- /packages/model/src/ResourceType.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | var _map = {"DATASET":"DATASET","IMAGE":"IMAGE","SOUND":"SOUND","TEXT":"TEXT","VIDEO":"VIDEO"}; 6 | 7 | function tToJs(param) { 8 | return param; 9 | } 10 | 11 | function tFromJs(param) { 12 | return _map[param]; 13 | } 14 | 15 | var toJs = tToJs; 16 | 17 | exports.tToJs = tToJs; 18 | exports.tFromJs = tFromJs; 19 | exports.toJs = toJs; 20 | /* No side effect */ 21 | -------------------------------------------------------------------------------- /packages/model/src/ResourceType.re: -------------------------------------------------------------------------------- 1 | [@bs.deriving jsConverter] 2 | type t = [ | `DATASET | `IMAGE | `SOUND | `TEXT | `VIDEO]; 3 | 4 | let toJs = tToJs; 5 | -------------------------------------------------------------------------------- /packages/model/src/State.re: -------------------------------------------------------------------------------- 1 | [@decco] 2 | type timeState = { 3 | cached: option(array(string)), 4 | sourceDate: option(array(Js.Json.t)), 5 | [@decco.default "TIME_STATE"] [@decco.key "type"] 6 | type_: string, 7 | [@decco.default "TimeState"] [@decco.key "__typename"] 8 | typename: string, 9 | }; 10 | 11 | type t = 12 | | TimeState(timeState) 13 | | NotImplemented_Passthrough(Js.Json.t); 14 | 15 | let makeTimeState = (~cached, ~sourceDate, ()) => { 16 | cached, 17 | sourceDate, 18 | type_: "TIME_STATE", 19 | typename: "TimeState", 20 | }; 21 | 22 | let makeTimeStateFromGraphQL = timeState => 23 | TimeState( 24 | makeTimeState( 25 | ~cached=timeState##cached, 26 | ~sourceDate=timeState##sourceDate, 27 | (), 28 | ), 29 | ); 30 | 31 | let t_decode = json => 32 | switch (json->Js.Json.classify) { 33 | | JSONObject(_) => 34 | switch (timeState_decode(json)) { 35 | | Ok(timeState) => Ok(TimeState(timeState)) 36 | | _ => Ok(NotImplemented_Passthrough(json)) 37 | } 38 | | _ => 39 | Error({ 40 | Decco.path: "", 41 | message: "Expected JSONObject for state.", 42 | value: json, 43 | }) 44 | }; 45 | 46 | let t_encode = 47 | fun 48 | | TimeState(t) => timeState_encode(t) 49 | | NotImplemented_Passthrough(t) => t; 50 | -------------------------------------------------------------------------------- /packages/model/src/TextDirection.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | function toString(t) { 6 | if (t === "RTL") { 7 | return "RTL"; 8 | } else if (t === "AUTO") { 9 | return "AUTO"; 10 | } else { 11 | return "LTR"; 12 | } 13 | } 14 | 15 | exports.toString = toString; 16 | /* No side effect */ 17 | -------------------------------------------------------------------------------- /packages/model/src/TextDirection.re: -------------------------------------------------------------------------------- 1 | type t = [ | `LTR | `RTL | `AUTO]; 2 | 3 | let toString = t => 4 | switch (t) { 5 | | `LTR => "LTR" 6 | | `RTL => "RTL" 7 | | `AUTO => "AUTO" 8 | }; 9 | -------------------------------------------------------------------------------- /packages/pamphlet/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/pamphlet/index.ts: -------------------------------------------------------------------------------- 1 | import mjml from "mjml"; 2 | 3 | import fs from "fs"; 4 | import path from "path"; 5 | 6 | const OUT_DIR = path.resolve(__dirname, "./dist"); 7 | const MJML_OPTIONS = { 8 | fonts: { 9 | "Roboto Mono": 10 | "https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap", 11 | Domine: "https://fonts.googleapis.com/css2?family=Domine&display=swap", 12 | }, 13 | }; 14 | 15 | fs.mkdirSync(OUT_DIR, { recursive: true }) 16 | 17 | fs.readdirSync(path.resolve(__dirname, "./templates")).forEach((fileName) => { 18 | const filePath = path.resolve( 19 | __dirname, 20 | "./templates", 21 | fileName, 22 | "template.mjml" 23 | ); 24 | const fileContent = fs.readFileSync(filePath, { encoding: "utf8" }); 25 | const outPath = path.resolve(OUT_DIR, fileName + ".html"); 26 | 27 | const { html, errors } = mjml(fileContent, MJML_OPTIONS); 28 | 29 | if (errors) { 30 | errors.forEach((e) => { 31 | throw e; 32 | }); 33 | } 34 | 35 | fs.writeFileSync(outPath, html); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/pamphlet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@literal-io/pamphlet", 3 | "version": "1.0.0", 4 | "description": "Email templates", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "ts-node index.ts" 9 | }, 10 | "repository": "https://github.com/literal-io/literal/tree/master/packages/pamphlet", 11 | "author": { 12 | "name": "Daniel Ramirez", 13 | "email": "javamonn@hey.com", 14 | "url": "https://javamonn.com" 15 | }, 16 | "license": "MIT - Hippocratic 1.2", 17 | "dependencies": { 18 | "mjml": "^4.8.0", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^4.1.3" 21 | }, 22 | "devDependencies": { 23 | "@types/mjml": "^4.7.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/pamphlet/templates/android-launch/variables.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/pamphlet/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "target": "es6", 6 | "noImplicitAny": true, 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "dist", 10 | "baseUrl": ".", 11 | "paths": { 12 | "*": [ 13 | "node_modules/*", 14 | "src/types/*" 15 | ] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/web/.gitignore: -------------------------------------------------------------------------------- 1 | lib/bs 2 | src/**/*.js 3 | .merlin 4 | .bsb.lock 5 | 6 | amplify/backend 7 | 8 | .graphql_ppx_cache 9 | 10 | out 11 | .next 12 | 13 | #amplify 14 | amplify/\#current-cloud-backend 15 | amplify/.config/local-* 16 | amplify/logs 17 | amplify/mock-data 18 | amplify/backend/amplify-meta.json 19 | amplify/backend/awscloudformation 20 | amplify/backend/.temp 21 | build/ 22 | dist/ 23 | node_modules/ 24 | aws-exports.js 25 | awsconfiguration.json 26 | amplifyconfiguration.json 27 | amplify-build-config.json 28 | amplify-gradle-config.json 29 | amplifytools.xcconfig 30 | .secret-* -------------------------------------------------------------------------------- /packages/web/amplify/.config/project-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers": [ 3 | "awscloudformation" 4 | ], 5 | "projectName": "literal - production", 6 | "version": "3.0", 7 | "frontend": "javascript", 8 | "javascript": { 9 | "framework": "react", 10 | "config": { 11 | "SourceDir": "src", 12 | "DistributionDir": "out", 13 | "BuildCommand": "npm run-script build", 14 | "StartCommand": "npm run-script start" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /packages/web/amplify/team-provider-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "production": { 3 | "awscloudformation": { 4 | "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" 13 | }, 14 | "categories": { 15 | "auth": { 16 | "literalAuth": {} 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /packages/web/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "1.0.0", 4 | "reason": { 5 | "react-jsx": 3 6 | }, 7 | "sources": [ 8 | { 9 | "dir": "src", 10 | "subdirs": true 11 | } 12 | ], 13 | "package-specs": { 14 | "module": "commonjs", 15 | "in-source": true 16 | }, 17 | "suffix": ".js", 18 | "js-post-build": { 19 | "cmd": "./scripts/bsb-js-post-build.js" 20 | }, 21 | "bs-dependencies": [ 22 | "@literal-io/bs-aws-amplify", 23 | "decco", 24 | "bs-webapi", 25 | "bs-fetch", 26 | "reason-react", 27 | "re-classnames", 28 | "reason-apollo", 29 | "reason-apollo-hooks", 30 | "@literal-io/bs-aws-appsync", 31 | "@literal-io/bs-amplitude", 32 | "@literal-io/apollo-link-analytics", 33 | "@literal-io/bs-sentry", 34 | "@literal-io/bs-amazon-cognito-identity-js", 35 | "@literal-io/model", 36 | "@jsiebern/bs-material-ui" 37 | ], 38 | "ppx-flags": [ 39 | [ 40 | "@reasonml-community/graphql-ppx/ppx", 41 | "-schema ./graphql-schema.json", 42 | "-apollo-mode" 43 | ], 44 | "decco/ppx" 45 | ], 46 | "warnings": { 47 | "error": "+101" 48 | }, 49 | "namespace": true, 50 | "refmt": 3 51 | } 52 | -------------------------------------------------------------------------------- /packages/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | "tailwindcss", 4 | "postcss-flexbugs-fixes", 5 | [ 6 | "postcss-preset-env", 7 | { 8 | autoprefixer: { 9 | flexbox: "no-2009", 10 | }, 11 | stage: 3, 12 | features: { 13 | "custom-properties": false, 14 | }, 15 | }, 16 | ], 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /packages/web/public/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/favicon-32.png -------------------------------------------------------------------------------- /packages/web/public/how-it-works-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/how-it-works-1.png -------------------------------------------------------------------------------- /packages/web/public/how-it-works-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/how-it-works-2.png -------------------------------------------------------------------------------- /packages/web/public/how-it-works-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/how-it-works-3.png -------------------------------------------------------------------------------- /packages/web/public/how-it-works-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/how-it-works-4.png -------------------------------------------------------------------------------- /packages/web/public/how-it-works-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/how-it-works-5.png -------------------------------------------------------------------------------- /packages/web/public/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/logo-128.png -------------------------------------------------------------------------------- /packages/web/public/logo-opaque-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/packages/web/public/logo-opaque-128.png -------------------------------------------------------------------------------- /packages/web/scripts/amplify-pull-production.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | amplify pull --appId d3kn1jmjzilue --envName production 4 | -------------------------------------------------------------------------------- /packages/web/scripts/amplify-pull-staging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | amplify pull --appId d3h0fixyxiyfg5 --envName staging 4 | -------------------------------------------------------------------------------- /packages/web/scripts/bsb-js-post-build.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { readFileSync, mkdirSync, writeFileSync } = require("fs"); 4 | const { resolve, dirname, relative } = require("path"); 5 | 6 | if (process.argv.length === 2) { 7 | return; 8 | } 9 | 10 | const path = process.argv[2]; 11 | 12 | if (path.includes("src/routes/Route_")) { 13 | const file = readFileSync(path, { encoding: "utf-8" }); 14 | const [_, page] = file.match(/^var page \= "(.*)".*$/m) || []; 15 | 16 | if (page) { 17 | const absPagePath = resolve(__dirname, "../src/pages", page); 18 | const reExports = [ 19 | "default", 20 | /^exports\.getStaticProps/m.test(file) && "getStaticProps", 21 | /^exports\.getStaticPaths/m.test(file) && "getStaticPaths", 22 | ].filter(Boolean); 23 | 24 | const pathRelativeToOutput = relative(dirname(absPagePath), resolve(path)); 25 | const output = `export { ${reExports.join( 26 | "," 27 | )} } from "${pathRelativeToOutput}"`; 28 | mkdirSync(dirname(absPagePath), { recursive: true }); 29 | writeFileSync(absPagePath, output); 30 | } else { 31 | throw new Error( 32 | `Expected \"page\" declaration in Route ${path}, but found none.` 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/web/scripts/bsb-post-clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | find src/pages -name "*.js" -type f -delete 5 | -------------------------------------------------------------------------------- /packages/web/scripts/generate-fragment-types.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | 6 | const schema = require("../graphql-schema.json"); 7 | 8 | const fragmentTypes = schema.data.__schema.types.filter( 9 | (type) => type.possibleTypes !== null 10 | ); 11 | 12 | schema.data.__schema.types = fragmentTypes; 13 | 14 | fs.writeFileSync( 15 | path.resolve(__dirname, "../fragment-types.json"), 16 | JSON.stringify(schema.data) 17 | ); 18 | -------------------------------------------------------------------------------- /packages/web/scripts/graphql-sync-schema.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | OWN_PATH=`realpath $0` 5 | ROOT_DIR=`realpath "$(dirname $OWN_PATH)/../"` 6 | AWS_PROFILE=amplify-literal 7 | 8 | aws appsync get-introspection-schema \ 9 | --format JSON \ 10 | --api-id $API_ID \ 11 | --profile $AWS_PROFILE \ 12 | "$ROOT_DIR/graphql-schema.json" 13 | 14 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_AnnotationCollectionHeader/Containers_AnnotationCollectionHeader_GraphQL.re: -------------------------------------------------------------------------------- 1 | module GetAnnotationCollectionFragment = [%graphql 2 | {| 3 | fragment AnnotationCollectionHeader_AnnotationCollection on AnnotationCollection { 4 | label 5 | id 6 | type_: type 7 | } 8 | |} 9 | ]; 10 | 11 | let cacheAnnotationCollectionFragment = 12 | ApolloClient.gql(. 13 | {| 14 | fragment Cache_AnnotationCollectionHeader_AnnotationCollection on AnnotationCollection { 15 | label 16 | id 17 | type_: type 18 | } 19 | |}, 20 | ); 21 | 22 | module GetAnnotationFragment = [%graphql 23 | {| 24 | fragment AnnotationCollectionHeader_Annotation on Annotation { 25 | id 26 | body { 27 | ... on TextualBody { 28 | __typename 29 | id 30 | value 31 | purpose 32 | } 33 | } 34 | target { 35 | ... on ExternalTarget { 36 | __typename 37 | externalTargetId: id 38 | format 39 | } 40 | ... on SpecificTarget { 41 | __typename 42 | specificTargetId: id 43 | source { 44 | ... on ExternalTarget { 45 | externalTargetId: id, 46 | format 47 | __typename 48 | } 49 | } 50 | } 51 | ... on TextualTarget { 52 | value 53 | __typename 54 | 55 | textualTargetId: id 56 | } 57 | } 58 | } 59 | |} 60 | ]; 61 | 62 | module DeleteAnnotationMutation = [%graphql 63 | {| 64 | mutation DeleteAnnotation($input: DeleteAnnotationInput!) { 65 | deleteAnnotation(input: $input) { 66 | annotation { 67 | id 68 | } 69 | } 70 | } 71 | |} 72 | ]; 73 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_AnnotationEditor/Containers_AnnotationEditor_Tag.re: -------------------------------------------------------------------------------- 1 | type t = { 2 | id: option(string), 3 | href: option(string), 4 | text: string, 5 | }; 6 | 7 | let ensureId = (~identityId, tag) => { 8 | let id = 9 | switch (tag.id) { 10 | | Some(id) => Js.Promise.resolve(id) 11 | | None => 12 | Lib_GraphQL.AnnotationCollection.makeId(~identityId, ~label=tag.text) 13 | }; 14 | id |> Js.Promise.then_(id => Js.Promise.resolve({...tag, id: Some(id)})); 15 | }; 16 | 17 | let asTextualBody = tag => 18 | tag.id 19 | ->Belt.Option.map(id => 20 | `TextualBody({ 21 | "__typename": "TextualBody", 22 | "id": id, 23 | "format": Some(`TEXT_PLAIN), 24 | "language": Some(`EN_US), 25 | "processingLanguage": Some(`EN_US), 26 | "textDirection": Some(`LTR), 27 | "accessibility": None, 28 | "rights": None, 29 | "purpose": Some([|`TAGGING|]), 30 | "value": tag.text, 31 | "type": Some(`TEXTUAL_BODY), 32 | }) 33 | ); 34 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_NewAnnotationEditor/Containers_NewAnnotationEditor_GraphQL.re: -------------------------------------------------------------------------------- 1 | module CreateAnnotationMutation = [%graphql 2 | {| 3 | mutation CreateAnnotation($input: CreateAnnotationInput!) { 4 | createAnnotation(input: $input) { 5 | annotation { 6 | id 7 | } 8 | } 9 | } 10 | |} 11 | ]; 12 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_NewAnnotationEditor/Containers_NewAnnotationEditor_Types.re: -------------------------------------------------------------------------------- 1 | [@bs.deriving jsConverter] 2 | type phase = [ | `PhasePrompt | `PhaseTextInput]; 3 | 4 | let phase_encode = p => p->phaseToJs->Js.Json.string; 5 | let phase_decode = json => 6 | switch (json->Js.Json.decodeString->Belt.Option.flatMap(phaseFromJs)) { 7 | | Some(p) => Ok(p) 8 | | None => 9 | Error(Decco.{path: "", message: "Not a phase value.", value: json}) 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_NewAnnotationFromMessageEventHeader/Containers_NewAnnotationFromMessageEventHeader.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~onDismiss) => { 3 |
4 | onDismiss()} 8 | _TouchRippleProps={ 9 | "classes": { 10 | "child": Cn.fromList(["bg-white"]), 11 | "rippleVisible": Cn.fromList(["opacity-50"]), 12 | }, 13 | } 14 | classes={MaterialUi.IconButton.Classes.make( 15 | ~root=Cn.fromList(["p-0"]), 16 | (), 17 | )}> 18 | 22 | 23 |

32 | {React.string("Edit Annotation")} 33 |

34 |
; 35 | }; 36 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_NewAnnotationFromShareEditor/Containers_NewAnnotationFromShareEditor_GraphQL.re: -------------------------------------------------------------------------------- 1 | module PatchAnnotationMutation = [%graphql 2 | {| 3 | mutation PatchAnnotation( 4 | $input: PatchAnnotationInput! 5 | ) { 6 | patchAnnotation(input: $input) { 7 | annotation { 8 | ...Containers_AnnotationEditor_GraphQL.GetAnnotationFragment.EditorNotesAnnotationFragment @bsField(name: "editorAnnotationFragment") 9 | } 10 | } 11 | } 12 | |} 13 | ]; 14 | 15 | module GetAnnotationFragment = [%graphql 16 | {| 17 | fragment editorNewFromShareAnnotationFragment on Annotation { 18 | id 19 | modified 20 | target { 21 | ... on TextualTarget { 22 | value 23 | __typename 24 | 25 | textualTargetId: id 26 | format 27 | language 28 | processingLanguage 29 | textDirection 30 | accessibility 31 | rights 32 | } 33 | ... on ExternalTarget { 34 | __typename 35 | 36 | externalTargetId: id 37 | format 38 | language 39 | processingLanguage 40 | textDirection 41 | type_: type 42 | accessibility 43 | rights 44 | } 45 | } 46 | body { 47 | ... on TextualBody { 48 | id 49 | value 50 | purpose 51 | __typename 52 | 53 | format 54 | language 55 | processingLanguage 56 | textDirection 57 | accessibility 58 | rights 59 | } 60 | } 61 | } 62 | |} 63 | ]; 64 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_NewAnnotationFromShareHeader/Containers_NewAnnotationFromShareHeader_GraphQL.re: -------------------------------------------------------------------------------- 1 | module GetAnnotationFragment = [%graphql 2 | {| 3 | fragment headerAnnotationFragment on Annotation { 4 | id 5 | body { 6 | ... on TextualBody { 7 | __typename 8 | id 9 | value 10 | purpose 11 | } 12 | } 13 | target { 14 | ... on ExternalTarget { 15 | __typename 16 | externalTargetId: id 17 | format 18 | } 19 | ... on SpecificTarget { 20 | __typename 21 | specificTargetId: id 22 | source { 23 | ... on ExternalTarget { 24 | externalTargetId: id, 25 | format 26 | __typename 27 | } 28 | } 29 | } 30 | } 31 | } 32 | |} 33 | ]; 34 | 35 | module DeleteAnnotationMutation = [%graphql 36 | {| 37 | mutation deleteAnnotation($input: DeleteAnnotationInput!) { 38 | deleteAnnotation(input: $input) { 39 | annotation { 40 | id 41 | } 42 | } 43 | } 44 | |} 45 | ]; 46 | 47 | -------------------------------------------------------------------------------- /packages/web/src/Containers/Containers_NewAnnotationHeader/Containers_NewAnnotationHeader.re: -------------------------------------------------------------------------------- 1 | open Styles; 2 | 3 | [@react.component] 4 | let make = () => { 5 | let handleClose = () => { 6 | let _ = Next.Router.back(); 7 | (); 8 | }; 9 | 10 |
23 | { 27 | let _ = 28 | Service_Analytics.(track(Click({action: "close", label: None}))); 29 | handleClose(); 30 | }} 31 | _TouchRippleProps={ 32 | "classes": { 33 | "child": cn(["bg-white"]), 34 | "rippleVisible": cn(["opacity-50"]), 35 | }, 36 | } 37 | classes={MaterialUi.IconButton.Classes.make(~root=cn(["p-0"]), ())}> 38 | 42 | 43 |

51 | {React.string("Create Annotation")} 52 |

53 |
; 54 | }; 55 | -------------------------------------------------------------------------------- /packages/web/src/Hooks/Hooks_SearchParams.re: -------------------------------------------------------------------------------- 1 | let use = parser => { 2 | let router = Next.Router.useRouter(); 3 | 4 | router.asPath 5 | ->Js.String2.split("?") 6 | ->Belt.Array.get(1) 7 | ->Belt.Option.getWithDefault("") 8 | ->Webapi.Url.URLSearchParams.make 9 | ->parser; 10 | }; 11 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_Apollo/Lib_Apollo_Cache.re: -------------------------------------------------------------------------------- 1 | module type CacheConfig = {type t;}; 2 | 3 | module Make = (Query: ReasonApolloTypes.Config, CacheConfig: CacheConfig) => { 4 | module CacheReadQuery = ApolloClient.ReadQuery(Query); 5 | module CacheWriteQuery = ApolloClient.WriteQuery(Query); 6 | 7 | external unsafeToCache: Js.Json.t => CacheConfig.t = "%identity"; 8 | external unsafeCacheToJson: CacheConfig.t => Js.Json.t = "%identity"; 9 | 10 | /** 11 | * FIXME: Return type is not actually Query.t, as Apollo cache 12 | * does not persist fragment alias fields -- everything is 13 | * inlined on the root type. 14 | */ 15 | let readCache = (~query, ~client, ()) => { 16 | let readQueryOptions = 17 | CacheReadQuery.{ 18 | query: ApolloClient.gql(. query##query), 19 | variables: Js.Nullable.fromOption(Some(query##variables)), 20 | }; 21 | switch (CacheReadQuery.readQuery(client, readQueryOptions)) { 22 | | exception _ => None 23 | | cachedResponse => 24 | cachedResponse->Js.Nullable.toOption->Belt.Option.map(unsafeToCache) 25 | }; 26 | }; 27 | 28 | /** 29 | * Data in cache format, i.e. everything inlined on GraphQL types. Not a good 30 | * way to enforce type safety. 31 | */ 32 | external unsafeFromCache: CacheConfig.t => Query.t = "%identity"; 33 | let writeCache = (~query, ~client, ~data: CacheConfig.t, ()) => 34 | CacheWriteQuery.make( 35 | ~client, 36 | ~variables=query##variables, 37 | ~data=unsafeFromCache(data), 38 | (), 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_GraphQL/Lib_GraphQL_Agent.re: -------------------------------------------------------------------------------- 1 | let cacheAgentFragment = 2 | ApolloClient.gql(. 3 | {| 4 | fragment CacheAgent on Agent { 5 | id 6 | email 7 | email_sha1 8 | type_: type 9 | username 10 | homepage 11 | name 12 | nickname 13 | } 14 | |}, 15 | ); 16 | 17 | let makeId = (~identityId) => Constants.apiOrigin ++ "/agents/" ++ identityId; 18 | 19 | let makeCache = (~identityId) => 20 | Js.Json.object_( 21 | Js.Dict.fromList([ 22 | ("__typename", "Agent"->Js.Json.string), 23 | ("id", makeId(~identityId)->Js.Json.string), 24 | ("email", Js.Json.null), 25 | ("email_sha1", Js.Json.null), 26 | ("type", "PERSON"->Js.Json.string), 27 | ("username", identityId->Js.Json.string), 28 | ("homepage", Js.Json.null), 29 | ("name", Js.Json.null), 30 | ("nickname", Js.Json.null), 31 | ]), 32 | ); 33 | 34 | let readCache = id => { 35 | Apollo.Client.readFragment( 36 | Providers_Apollo_Client.inst^, 37 | {id: "Agent:" ++ id, fragment: cacheAgentFragment}, 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_GraphQL/Lib_GraphQL_CreateAnnotationFromExternalTargetMutation.re: -------------------------------------------------------------------------------- 1 | module Input = { 2 | let make = (~creatorUsername, ~annotationId, ~externalTarget) => { 3 | "creatorUsername": creatorUsername, 4 | "annotationId": annotationId, 5 | "externalTarget": externalTarget, 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_GraphQL/Lib_GraphQL_Language.re: -------------------------------------------------------------------------------- 1 | type t = [ | `EN_US]; 2 | 3 | let toString = t => 4 | switch (t) { 5 | | `EN_US => "EN_US" 6 | }; 7 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_GraphQL/Lib_GraphQL_Motivation.re: -------------------------------------------------------------------------------- 1 | let toString = t => 2 | switch (t) { 3 | | `TAGGING => "TAGGING" 4 | | `ACCESSING => "ACCESSING" 5 | | `BOOKMARKING => "BOOKMARKING" 6 | | `CLASSIFYING => "CLASSIFYING" 7 | | `COMMENTING => "COMMENTING" 8 | | `DESCRIBING => "DESCRIBING" 9 | | `EDITING => "EDITING" 10 | | `HIGHLIGHTING => "HIGHLIGHTING" 11 | | `IDENTIFYING => "IDENTIFYING" 12 | | `LINKING => "LINKING" 13 | | `MODERATING => "MODERATING" 14 | | `QUESTIONING => "QUESTIONING" 15 | | `REPLYING => "REPLYING" 16 | }; 17 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_GraphQL/Lib_GraphQL_ResourceType.re: -------------------------------------------------------------------------------- 1 | [@bs.deriving jsConverter] 2 | type t = [ | `DATASET | `IMAGE | `SOUND | `TEXT | `VIDEO]; 3 | 4 | let toJs = tToJs; 5 | -------------------------------------------------------------------------------- /packages/web/src/Lib/Lib_GraphQL/Lib_GraphQL_TextDirection.re: -------------------------------------------------------------------------------- 1 | type t = [ | `LTR | `RTL | `AUTO]; 2 | 3 | let toString = t => 4 | switch (t) { 5 | | `LTR => "LTR" 6 | | `RTL => "RTL" 7 | | `AUTO => "AUTO" 8 | }; 9 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_Apollo/Providers_Apollo_AppSync.re: -------------------------------------------------------------------------------- 1 | let getWebviewCredentials = () => 2 | Timer.thunkP(~label="AUTH_GET_CREDENTIALS", () => 3 | Webview.postMessageForResult( 4 | Webview.WebEvent.make(~type_="AUTH_GET_CREDENTIALS", ()), 5 | ) 6 | ) 7 | |> Js.Promise.then_(credentialsJson => { 8 | credentialsJson 9 | ->Belt.Option.flatMap(credentialsJson => 10 | switch (Providers_Apollo_Credentials.decode(credentialsJson)) { 11 | | Ok(credentials) => Some(credentials) 12 | | Error(e) => 13 | let _ = Error.(report(DeccoDecodeError(e))); 14 | None; 15 | } 16 | ) 17 | ->Js.Promise.resolve 18 | }); 19 | 20 | let makeAuthenticatedClientAuthOptions = cognitoUserSession => 21 | AwsAppSync.Client.authWithCognitoUserPools(~jwtToken=() => 22 | cognitoUserSession 23 | ->AmazonCognitoIdentity.UserSession.getIdToken 24 | ->AmazonCognitoIdentity.IdToken.getJwtToken 25 | ->Js.Promise.resolve 26 | ); 27 | 28 | let makeGuestClientAuthOptions = credentials => 29 | AwsAppSync.Client.authWithIAM(~credentials=() => 30 | credentials 31 | ->Providers_Authentication_Credentials.toAmplifyCredentials 32 | ->Js.Promise.resolve 33 | ); 34 | 35 | let makeAppSyncLinkOptions = (~auth) => 36 | AwsAppSync.Client.{ 37 | url: 38 | AwsAmplify.( 39 | Constants.awsAmplifyConfig->Config.appSyncGraphqlEndpointGet 40 | ), 41 | region: AwsAmplify.(Constants.awsAmplifyConfig->Config.appSyncRegionGet), 42 | auth, 43 | disableOffline: true, 44 | complexObjectsCredentials: () => 45 | AwsAmplify.Auth.(inst->currentCredentials), 46 | mandatorySignIn: false, 47 | }; 48 | 49 | let makeLink = appSyncLinkOptions => 50 | AwsAppSync.Client.createAppSyncLink(appSyncLinkOptions); 51 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_Apollo/Providers_Apollo_Client.re: -------------------------------------------------------------------------------- 1 | let fragmentTypes = [%raw "require('../../../fragment-types.json')"]; 2 | 3 | let appSyncCache = 4 | ApolloInMemoryCache.createInMemoryCache( 5 | ~fragmentMatcher= 6 | ApolloInMemoryCache.introspectionFragmentMatcher({ 7 | "introspectionQueryResultData": fragmentTypes, 8 | }), 9 | (), 10 | ); 11 | 12 | let makeClientFromUser = user => { 13 | let auth = 14 | switch (user) { 15 | | Providers_Authentication_User.GuestUser({credentials}) => 16 | credentials 17 | ->Providers_Apollo_AppSync.makeGuestClientAuthOptions 18 | ->Js.Option.some 19 | | SignedInUser({cognitoUserSession}) 20 | | SignedInUserMergingIdentites({cognitoUserSession}) => 21 | cognitoUserSession 22 | ->Providers_Apollo_AppSync.makeAuthenticatedClientAuthOptions 23 | ->Js.Option.some 24 | | SignedOutPromptAuthentication 25 | | Unknown => None 26 | }; 27 | 28 | let appSyncLinkOptions = 29 | Providers_Apollo_AppSync.makeAppSyncLinkOptions(~auth); 30 | let appSyncLink = Providers_Apollo_AppSync.(makeLink(appSyncLinkOptions)); 31 | 32 | AwsAppSync.Client.( 33 | makeWithOptions( 34 | appSyncLinkOptions, 35 | { 36 | link: 37 | Some( 38 | ApolloLinks.from([| 39 | Providers_Apollo_AnalyticsLink.link, 40 | appSyncLink, 41 | |]), 42 | ), 43 | cache: Some(appSyncCache), 44 | }, 45 | ) 46 | ); 47 | }; 48 | 49 | // FIXME: This should get exposed via react context. 50 | let inst = ref(makeClientFromUser(Providers_Authentication_User.Unknown)); 51 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_Apollo/Providers_Apollo_Credentials.re: -------------------------------------------------------------------------------- 1 | [@decco] 2 | type tokens = { 3 | idToken: string, 4 | refreshToken: string, 5 | accessToken: string, 6 | }; 7 | 8 | [@decco] 9 | type awsCredentials = { 10 | accessKeyId: string, 11 | secretAccessKey: string, 12 | identityId: string, 13 | sessionToken: string, 14 | }; 15 | 16 | external fromAmplifyCredentials: AwsAmplify.Credentials.t => awsCredentials = 17 | "%identity"; 18 | 19 | external toAmplifyCredentials: awsCredentials => AwsAmplify.Credentials.t = 20 | "%identity"; 21 | 22 | [@decco] 23 | type t = { 24 | tokens: option(tokens), 25 | awsCredentials: option(awsCredentials), 26 | }; 27 | 28 | let decode = t_decode; 29 | let encode = t_encode; 30 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_Authentication/Providers_Authentication_Credentials.re: -------------------------------------------------------------------------------- 1 | [@decco] 2 | type tokens = { 3 | idToken: string, 4 | refreshToken: string, 5 | accessToken: string, 6 | }; 7 | 8 | [@decco] 9 | type awsCredentials = { 10 | accessKeyId: string, 11 | secretAccessKey: string, 12 | identityId: string, 13 | sessionToken: string, 14 | }; 15 | 16 | external fromAmplifyCredentials: AwsAmplify.Credentials.t => awsCredentials = 17 | "%identity"; 18 | 19 | external toAmplifyCredentials: awsCredentials => AwsAmplify.Credentials.t = 20 | "%identity"; 21 | 22 | [@decco] 23 | type t = { 24 | tokens: option(tokens), 25 | awsCredentials: option(awsCredentials), 26 | }; 27 | 28 | let decode = t_decode; 29 | let encode = t_encode; 30 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_Authentication/Providers_Authentication_GraphQL.re: -------------------------------------------------------------------------------- 1 | module MergeUserIdentitiesMutation = [%graphql 2 | {| 3 | mutation MergeUserIdentities($input: MergeUserIdentitiesInput!) { 4 | mergeUserIdentities(input: $input) { 5 | _ 6 | } 7 | } 8 | |} 9 | ]; 10 | 11 | module Input = { 12 | let make = 13 | ( 14 | ~sourceGuestUserCredentials: Providers_Authentication_Credentials.awsCredentials, 15 | ~targetUserCredentials: AmazonCognitoIdentity.UserSession.t, 16 | ) => { 17 | "targetUserIdToken": 18 | AmazonCognitoIdentity.( 19 | targetUserCredentials 20 | ->UserSession.getIdToken 21 | ->IdToken.getJwtToken 22 | ->JwtToken.toString 23 | ), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_BottomNavigation/Providers_BottomNavigation.re: -------------------------------------------------------------------------------- 1 | type context = { 2 | setIsVisible: bool => unit, 3 | isVisible: bool, 4 | }; 5 | 6 | let context = React.createContext({setIsVisible: _ => (), isVisible: false}); 7 | 8 | module ContextProvider = { 9 | include React.Context; 10 | 11 | let makeProps = (~value, ~children, ()) => { 12 | "value": value, 13 | "children": children, 14 | }; 15 | let make = React.Context.provider(context); 16 | }; 17 | 18 | [@react.component] 19 | let make = (~children) => { 20 | let (isVisible, setIsVisible) = React.useState(_ => false); 21 | let router = Next.Router.useRouter(); 22 | 23 | let _ = 24 | React.useEffect3( 25 | () => { 26 | let _ = setIsVisible(_ => true); 27 | None; 28 | }, 29 | (router.asPath, router.route, router.query), 30 | ); 31 | 32 | let handleSetIsVisible = newIsVisible => setIsVisible(_ => newIsVisible); 33 | 34 | 35 | children 36 | ; 37 | }; 38 | -------------------------------------------------------------------------------- /packages/web/src/Providers/Providers_ModalNavigation/Providers_ModalNavigation.re: -------------------------------------------------------------------------------- 1 | type context = { 2 | onNext: (unit => unit) => unit, 3 | setOnNext: option(unit => unit) => unit, 4 | }; 5 | 6 | let context = React.createContext({onNext: _ => (), setOnNext: _ => ()}); 7 | 8 | module ContextProvider = { 9 | include React.Context; 10 | 11 | let makeProps = (~value, ~children, ()) => { 12 | "value": value, 13 | "children": children, 14 | }; 15 | let make = React.Context.provider(context); 16 | }; 17 | 18 | [@react.component] 19 | let make = (~children) => { 20 | let preparedNavigation = React.useRef(false); 21 | let (onNext, setOnNext) = React.useState(_ => None); 22 | let router = Next.Router.useRouter(); 23 | 24 | let handleOnNext = defaultAction => { 25 | let cb = onNext->Belt.Option.getWithDefault(defaultAction); 26 | let _ = setOnNext(_ => None); 27 | let _ = cb(); 28 | (); 29 | }; 30 | let _ = 31 | React.useEffect3( 32 | () => { 33 | if (!preparedNavigation.current) { 34 | let _ = setOnNext(_ => None); 35 | (); 36 | }; 37 | preparedNavigation.current = false; 38 | None; 39 | }, 40 | (router.asPath, router.route, router.query), 41 | ); 42 | 43 | let handleSetOnNext = action => { 44 | setOnNext(_ => action); 45 | preparedNavigation.current = true; 46 | (); 47 | }; 48 | 49 | 50 | children 51 | ; 52 | }; 53 | -------------------------------------------------------------------------------- /packages/web/src/QueryRenderers/QueryRenderers_NewAnnotation/QueryRenderers_NewAnnotation.re: -------------------------------------------------------------------------------- 1 | open Styles; 2 | 3 | [@react.component] 4 | let make = (~identityId, ~initialPhaseState=?) => 5 | <> 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/web/src/QueryRenderers/QueryRenderers_NewAnnotationFromShare/QueryRenderers_NewAnnotationFromShare_GraphQL.re: -------------------------------------------------------------------------------- 1 | module GetAnnotationQuery = [%graphql 2 | {| 3 | query GetAnnotation($creatorUsername: String!, $id: String!) { 4 | getAnnotation(creatorUsername: $creatorUsername, id: $id) { 5 | id 6 | created 7 | __typename 8 | ...Containers_AnnotationEditor_GraphQL.GetAnnotationFragment.EditorNotesAnnotationFragment @bsField(name: "editorAnnotationFragment") 9 | ...Containers_NewAnnotationFromShareHeader_GraphQL.GetAnnotationFragment.HeaderAnnotationFragment @bsField(name: "headerAnnotationFragment") 10 | } 11 | } 12 | |} 13 | ]; 14 | 15 | module CreateAnnotationMutation = [%graphql 16 | {| 17 | mutation NewAnnotationFromShare_CreateAnnotation($input: CreateAnnotationInput!) { 18 | createAnnotation(input: $input) { 19 | annotation { 20 | id 21 | created 22 | __typename 23 | ...Containers_AnnotationEditor_GraphQL.GetAnnotationFragment.EditorNotesAnnotationFragment @bsField(name: "editorAnnotationFragment") 24 | ...Containers_NewAnnotationFromShareHeader_GraphQL.GetAnnotationFragment.HeaderAnnotationFragment @bsField(name: "headerAnnotationFragment") 25 | } 26 | } 27 | } 28 | |} 29 | ]; 30 | -------------------------------------------------------------------------------- /packages/web/src/QueryRenderers/QueryRenderers_TagsFilter/QueryRenderers_TagsFilter_GraphQL.re: -------------------------------------------------------------------------------- 1 | module AnnotationCollectionLabelAutocomplete = [%graphql 2 | {| 3 | query annotationCollectionLabelAutocomplete( 4 | $creatorUsername: String!, 5 | $labelBeginsWith: String! 6 | ) { 7 | listAnnotationCollectionsByLabel( 8 | creatorUsername: $creatorUsername, 9 | label: { 10 | beginsWith: $labelBeginsWith 11 | } 12 | ) { 13 | items { 14 | id 15 | label 16 | } 17 | } 18 | } 19 | |} 20 | ]; 21 | -------------------------------------------------------------------------------- /packages/web/src/QueryRenderers/QueryRenderers_WebView_EditAnnotationTags/QueryRenderers_WebView_EditAnnotationTags.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~identityId, ~annotation, ~onAnnotationChange, ~onCollapse) => { 3 | let handleAnnotationChange = newAnnotation => { 4 | let _ = onAnnotationChange(newAnnotation); 5 | 6 | let allBodiesHaveId = 7 | newAnnotation.LiteralModel.Annotation.body 8 | ->Belt.Option.getWithDefault([||]) 9 | ->Belt.Array.every( 10 | fun 11 | | TextualBody({id}) when Js.String2.startsWith(id, "https") => true 12 | | TextualBody(_) => false 13 | | _ => true, 14 | ); 15 | 16 | let _ = 17 | if (allBodiesHaveId) { 18 | let _ = 19 | Webview.( 20 | postMessage( 21 | WebEvent.make( 22 | ~type_="EDIT_ANNOTATION_TAGS_RESULT", 23 | ~data=LiteralModel.Annotation.encode(newAnnotation), 24 | (), 25 | ), 26 | ) 27 | ); 28 | (); 29 | }; 30 | (); 31 | }; 32 | 33 |
43 | onCollapse()} 45 | /> 46 | 51 |
; 52 | }; 53 | -------------------------------------------------------------------------------- /packages/web/src/app.css: -------------------------------------------------------------------------------- 1 | /* purgecss start ignore */ 2 | @tailwind base; 3 | @tailwind components; 4 | /* purgecss end ignore */ 5 | 6 | @tailwind utilities; 7 | 8 | /* base */ 9 | 10 | :root { 11 | --root-height: 100vh; 12 | } 13 | 14 | html { 15 | font-size: 16px; 16 | -moz-osx-font-smoothing: grayscale; 17 | -webkit-font-smoothing: antialiased; 18 | -webkit-tap-highlight-color: transparent; 19 | } 20 | 21 | #__next { 22 | width: 100vw; 23 | height: var(--root-height); 24 | background-color: #0C0C0C; 25 | position: relative; 26 | overflow-y: auto; 27 | display: flex; 28 | flex-direction: column; 29 | flex-grow: 0; 30 | } 31 | 32 | .awsappsync { 33 | width: 100%; 34 | height: 100%; 35 | } 36 | 37 | .awsappsync--rehydrating { 38 | display: none; 39 | } 40 | 41 | 42 | button:focus, 43 | input:focus { 44 | outline: none; 45 | } 46 | 47 | ::selection { 48 | background-color: #FFF; 49 | color: rgba(0, 0, 0, .92); 50 | } 51 | 52 | /* utility classes */ 53 | 54 | .box-decoration-break-clone { 55 | -webkit-box-decoration-break: clone; 56 | box-decoration-break: clone; 57 | } 58 | 59 | .touch-none { 60 | touch-action: none; 61 | } 62 | -------------------------------------------------------------------------------- /packages/web/src/components/AddTagInput/AddTagInput.module.css: -------------------------------------------------------------------------------- 1 | .underline:before { 2 | border-bottom: none !important; 3 | } 4 | .underline:after { 5 | border-bottom-color: rgba(255, 255, 255, .72) !important; 6 | } 7 | -------------------------------------------------------------------------------- /packages/web/src/components/AuthenticationFields/AuthenticationFields.module.css: -------------------------------------------------------------------------------- 1 | .underline:after { 2 | border-bottom-color: rgba(255, 255, 255, .92) !important; 3 | } 4 | 5 | .underlineError { 6 | border: none !important; 7 | } 8 | 9 | .underlineError:after { 10 | border-bottom-color: #B00020 !important; 11 | } 12 | -------------------------------------------------------------------------------- /packages/web/src/components/BottomAlert.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~text) => 3 |
14 |

15 | {React.string(text)} 16 |

17 | 26 |
; 27 | -------------------------------------------------------------------------------- /packages/web/src/components/ErrorBoundary/ErrorBoundary.re: -------------------------------------------------------------------------------- 1 | module RawErrorBoundary = { 2 | [@bs.module "./raw-error-boundary.js"] [@react.component] 3 | external make: 4 | ( 5 | ~onComponentDidCatch: Js.Exn.t => unit, 6 | ~renderErrorBoundary: unit => React.element, 7 | ~children: React.element 8 | ) => 9 | React.element = 10 | "default"; 11 | }; 12 | 13 | [@react.component] 14 | let make = (~children) => { 15 | } 17 | onComponentDidCatch={error => { 18 | let _ = SentryBrowser.captureException(error); 19 | (); 20 | }}> 21 | children 22 | ; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/web/src/components/ExternalTargetCard/ExternalTargetCard.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = 3 | ( 4 | ~externalTargetFragment as externalTarget, 5 | ~textualTargetFragment as textualTarget, 6 | ~onClick, 7 | ) => { 8 | let host = 9 | externalTarget##externalTargetId->Webapi.Url.make->Webapi.Url.host; 10 | 11 | 34 | ()} 36 | disabled=true 37 | value=textualTarget##value 38 | inputClasses={MaterialUi.Input.Classes.make( 39 | ~root=Cn.fromList(["p-4"]), 40 | ~inputMultiline=Cn.fromList(["px-0"]), 41 | (), 42 | )} 43 | /> 44 |
53 | 60 | {React.string(host)} 61 | 62 |
63 |
; 64 | }; 65 | -------------------------------------------------------------------------------- /packages/web/src/components/ExternalTargetCard/ExternalTargetCard_GraphQL.re: -------------------------------------------------------------------------------- 1 | module ExternalTargetFragment = [%graphql 2 | {| 3 | fragment externalTargetCard_ExternalTargetFragment on ExternalTarget { 4 | externalTargetId: id 5 | format 6 | __typename 7 | } 8 | |} 9 | ]; 10 | 11 | module TextualTargetFragment = [%graphql 12 | {| 13 | fragment externalTargetCard_TextualTargetFragment on TextualTarget { 14 | textualTargetId: id 15 | value 16 | __typename 17 | } 18 | |} 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/web/src/components/FeatureIndicator/FeatureIndicator.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~enabled, ~className=?) => 3 |
; 15 | -------------------------------------------------------------------------------- /packages/web/src/components/FeatureListItem/FeatureListItem.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~enabled, ~title, ~description, ~className=?) => 3 |
  • 4 | 5 |
    6 |

    13 | {React.string(title)} 14 |

    15 |

    21 | {React.string(description)} 22 |

    23 |
    24 |
  • ; 25 | -------------------------------------------------------------------------------- /packages/web/src/components/FloatingActionButton.re: -------------------------------------------------------------------------------- 1 | open Styles; 2 | 3 | [@react.component] 4 | let make = (~onClick=?, ~className=?, ~disabled=?, ~children) => 5 |
    6 | 10 | children 11 |
    25 | 26 |
    ; 27 | -------------------------------------------------------------------------------- /packages/web/src/components/FooterLink/FooterLink.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~href, ~title, ~description) => 3 | 4 | 12 |
    19 |
    20 |

    26 | {React.string(title)} 27 |

    28 |

    34 | {React.string(description)} 35 |

    36 |
    37 | 46 |
    47 |
    48 |
    ; 49 | -------------------------------------------------------------------------------- /packages/web/src/components/Head.re: -------------------------------------------------------------------------------- 1 | let makeMeta = 2 | ( 3 | ~title="Literal", 4 | ~description="Literal is a textual annotation management system. The act of annotation is only the beginning. Leverage your highlights to enhance your reading experience.", 5 | ~image=Constants.apiOrigin ++ "/logo-opaque-128.png", 6 | (), 7 | ) => 8 | <> 9 | {React.string(title)} 10 | 11 | 12 | 13 | 14 | ; 15 | 16 | [@react.component] 17 | let make = (~children=React.null) => 18 | 19 | 24 | 25 | 26 | 27 | {makeMeta()} 28 | children 29 | ; 30 | -------------------------------------------------------------------------------- /packages/web/src/components/Loading.re: -------------------------------------------------------------------------------- 1 | open Styles; 2 | 3 | [@react.component] 4 | let make = () =>
    ; 5 | -------------------------------------------------------------------------------- /packages/web/src/components/Markdown/Markdown.module.css: -------------------------------------------------------------------------------- 1 | .md { 2 | @apply text-lightPrimary; 3 | } 4 | .md > h1 { 5 | @apply mb-8 font-serif text-xl; 6 | } 7 | .md > h2 { 8 | @apply pb-2 mt-12 mb-8 font-serif text-xl border-b border-dotted border-lightDisabled; 9 | } 10 | .md > h3 { 11 | @apply mt-8 mb-4 font-serif text-xl; 12 | } 13 | .md > p { 14 | @apply mb-8 font-sans text-lg leading-relaxed; 15 | color: rgba(255, 255, 255, 0.92); 16 | } 17 | 18 | .md > details { 19 | @apply mt-12; 20 | } 21 | 22 | .md ol li, 23 | .md ul li { 24 | @apply mb-4 font-sans text-lg leading-relaxed; 25 | color: rgba(255, 255, 255, 0.92); 26 | } 27 | .md a { 28 | @apply underline; 29 | } 30 | .md > ul { 31 | @apply mb-10 ml-6 list-inside list-square; 32 | } 33 | 34 | .md > blockquote { 35 | @apply pl-4 mt-8 mb-8 font-serif text-lg leading-relaxed border-l-2 text-lightPrimary; 36 | } 37 | 38 | .md > figure > figcaption { 39 | @apply ml-4 italic text-lightSecondary; 40 | } 41 | 42 | .md > figure { 43 | @apply mb-10; 44 | } 45 | 46 | .md > iframe { 47 | @apply w-full mt-8 mb-8; 48 | } 49 | 50 | .md > div { 51 | @apply mb-8 !important; 52 | } 53 | 54 | .md > p > code, 55 | .md > ul > li > code { 56 | @apply p-1 rounded-sm bg-lightPrimary; 57 | color: rgba(0, 0, 0, .92); 58 | } 59 | 60 | .md > details > summary { 61 | @apply mb-4; 62 | } 63 | 64 | .md > details > p { 65 | @apply text-lg text-lightPrimary; 66 | } 67 | -------------------------------------------------------------------------------- /packages/web/src/components/Markdown/Markdown.re: -------------------------------------------------------------------------------- 1 | let styles = [%raw "require('./Markdown.module.css')"]; 2 | 3 | [@react.component] 4 | let make = (~html) => { 5 |
    ; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/web/src/components/PromptIconButton/PromptIconButton.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~icon, ~label, ~className=?, ~onClick=?, ~href=?) => { 3 | 27 | 31 |
    42 | 50 | {React.string(label)} 51 | 52 |
    53 |
    ; 54 | }; 55 | -------------------------------------------------------------------------------- /packages/web/src/components/Redirect.re: -------------------------------------------------------------------------------- 1 | let encodeSearch = [%raw 2 | {| 3 | function encodeSearch(o) { 4 | return "?" + (new URLSearchParams(o)).toString() 5 | } 6 | |} 7 | ]; 8 | 9 | [@react.component] 10 | let make = (~path, ~staticPath, ~search=?, ~children) => { 11 | let _ = 12 | React.useEffect0(_ => { 13 | let stringifiedSearch = 14 | search 15 | ->Belt.Option.map(encodeSearch) 16 | ->Belt.Option.getWithDefault(""); 17 | let _ = 18 | Next.Router.(replaceWithAs(staticPath, path ++ stringifiedSearch)); 19 | None; 20 | }); 21 | children; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/web/src/components/Skeleton/Skeleton.module.css: -------------------------------------------------------------------------------- 1 | .skeleton { 2 | background-color: rgba(255, 255, 255, .25) !important; 3 | } 4 | -------------------------------------------------------------------------------- /packages/web/src/components/Skeleton/Skeleton.re: -------------------------------------------------------------------------------- 1 | let styles = [%raw "require('./Skeleton.module.css')"]; 2 | 3 | [@react.component] 4 | let make = (~variant, ~className=?) => 5 | ; 9 | -------------------------------------------------------------------------------- /packages/web/src/components/SourceList/SourceList.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~data, ~renderItem, ~itemKey, ~itemClassName, ~className=?) => 3 |
      10 | {data 11 | ->Belt.Array.mapWithIndex((idx, item) => 12 |
    • 14 | {renderItem(~item, ~idx)} 15 |
    • 16 | ) 17 | ->React.array} 18 |
    ; 19 | -------------------------------------------------------------------------------- /packages/web/src/components/SourceListItem/SourceListItem.module.css: -------------------------------------------------------------------------------- 1 | .pathname { 2 | word-break: break-word; 3 | } 4 | -------------------------------------------------------------------------------- /packages/web/src/components/SourceListItem/SourceListItem_GraphQL.re: -------------------------------------------------------------------------------- 1 | module AnnotationCollectionFragment = [%graphql 2 | {| 3 | fragment SourceListItem_AnnotationCollection on AnnotationCollection { 4 | id 5 | label 6 | total 7 | } 8 | |} 9 | ]; 10 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/add-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/add.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/android.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/article.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/back.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/delete-black.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/done.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/error-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/help-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/highlight.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/label.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/language.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/manage-accounts.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/more.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/remove-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/share-black.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/source.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/text-fields.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/text-snippet.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/Svg/waves.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/web/src/components/TagList/TagList.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~data, ~renderItem, ~itemKey, ~itemClassName, ~className=?) => 3 |
      10 | {data 11 | ->Belt.Array.mapWithIndex((idx, item) => 12 |
    • 14 | {renderItem(~item, ~idx)} 15 |
    • 16 | ) 17 | ->React.array} 18 |
    ; 19 | -------------------------------------------------------------------------------- /packages/web/src/components/TagListItem/TagListItem_GraphQL.re: -------------------------------------------------------------------------------- 1 | module AnnotationCollectionFragment = [%graphql 2 | {| 3 | fragment TagListItem_AnnotationCollection on AnnotationCollection { 4 | id 5 | label 6 | total 7 | } 8 | |} 9 | ]; 10 | -------------------------------------------------------------------------------- /packages/web/src/components/TagsList/TagsList.module.css: -------------------------------------------------------------------------------- 1 | .underline { 2 | padding: 4px 0 7px; 3 | } 4 | 5 | .underline:after { 6 | border-bottom-color: rgba(255, 255, 255, .92) !important; 7 | } 8 | -------------------------------------------------------------------------------- /packages/web/src/components/TextInput/TextInput.re: -------------------------------------------------------------------------------- 1 | module Basic = TextInput_Basic; 2 | module Annotation = TextInput_Annotation; 3 | -------------------------------------------------------------------------------- /packages/web/src/components/TextInput/TextInput_Annotation.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = 3 | ( 4 | ~onChange, 5 | ~value, 6 | ~onFocus=?, 7 | ~onBlur=?, 8 | ~disabled=?, 9 | ~autoFocus=?, 10 | ~placeholder=?, 11 | ~tagsInputRef=?, 12 | ~textInputRef=?, 13 | ~inputClasses=? 14 | ) => { 15 | /** Reuse the ref prop if one was passed in, otherwise use our own **/ 16 | let textInputRef = { 17 | let ownRef = React.useRef(Js.Nullable.null); 18 | switch (textInputRef) { 19 | | Some(textInputRef) => textInputRef 20 | | None => ownRef 21 | }; 22 | }; 23 | let tagsInputRef = { 24 | let ownRef = React.useRef(Js.Nullable.null); 25 | switch (tagsInputRef) { 26 | | Some(tagsInputRef) => tagsInputRef 27 | | None => ownRef 28 | }; 29 | }; 30 | 31 | let handleTextKeyDown = ev => { 32 | let handled = 33 | switch (ev->ReactEvent.Keyboard.keyCode) { 34 | | 51 /*** # **/ => 35 | switch (tagsInputRef.current->Js.Nullable.toOption) { 36 | | Some(inputElem) => 37 | let _ = 38 | inputElem 39 | ->Webapi.Dom.Element.unsafeAsHtmlElement 40 | ->Webapi.Dom.HtmlElement.focus; 41 | true; 42 | | None => false 43 | } 44 | | _ => false 45 | }; 46 | let _ = 47 | if (handled) { 48 | let _ = ev->ReactEvent.Keyboard.preventDefault; 49 | let _ = ev->ReactEvent.Keyboard.stopPropagation; 50 | (); 51 | }; 52 | (); 53 | }; 54 | 55 | ; 67 | }; 68 | -------------------------------------------------------------------------------- /packages/web/src/components/TextInput/TextInput_Basic.module.css: -------------------------------------------------------------------------------- 1 | .underline:before { 2 | border-bottom-width: 0px !important; 3 | } 4 | .underline:after { 5 | border-bottom-color: rgba(255, 255, 255, .92) !important; 6 | } 7 | -------------------------------------------------------------------------------- /packages/web/src/components/TextInput/TextInput_Loading.re: -------------------------------------------------------------------------------- 1 | open Styles; 2 | 3 | [@react.component] 4 | let make = (~className=?, ~lineCount=8) => { 5 | let (margins, _setMargins) = 6 | React.useState(_ => { 7 | let margins = [|"", "mr-1", "mr-2", "mr-3", "mr-4", "mr-5"|]; 8 | Belt.Array.make(lineCount, 0) 9 | ->Belt.Array.map(_ => 10 | margins[Js.Math.random_int(0, Js.Array2.length(margins))] 11 | ); 12 | }); 13 | 14 |
    16 |
    17 | {Belt.Array.make(lineCount, 0) 18 | ->Belt.Array.mapWithIndex((idx, _) => { 19 | 24 | }) 25 | ->React.array} 26 |
    27 |
    38 | 42 |
    43 |
    ; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/web/src/components/UnderlineLink/UnderlineLink.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let make = (~text, ~href) => 3 | 4 | 15 | {React.string(text)} 16 | 17 | ; 18 | -------------------------------------------------------------------------------- /packages/web/src/components/header.re: -------------------------------------------------------------------------------- 1 | open Styles; 2 | 3 | [@react.component] 4 | let make = (~children=React.null, ~className=?, ~style=?) => 5 | ; 15 | -------------------------------------------------------------------------------- /packages/web/src/externals/Apollo.re: -------------------------------------------------------------------------------- 1 | module Client = { 2 | type readFragmentArgs('a) = { 3 | fragment: ReasonApolloTypes.queryString, 4 | id: string, 5 | }; 6 | 7 | [@bs.send] [@bs.return nullable] 8 | external readFragment: 9 | (ApolloClient.generatedApolloClient, readFragmentArgs('a)) => 10 | option(Js.Json.t) = 11 | "readFragment"; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/web/src/externals/Externals_Crypto.re: -------------------------------------------------------------------------------- 1 | type textEncoder; 2 | 3 | [@bs.new] external makeTextEncoder: unit => textEncoder = "TextEncoder"; 4 | [@bs.send] 5 | external encode: (textEncoder, string) => Js.TypedArray2.Uint8Array.t = 6 | "encode"; 7 | 8 | [@bs.val] [@bs.scope ("window", "crypto", "subtle")] 9 | external digest: 10 | (string, Js.TypedArray2.Uint8Array.t) => 11 | Js.Promise.t(Js.TypedArray2.array_buffer) = 12 | "digest"; 13 | -------------------------------------------------------------------------------- /packages/web/src/externals/Externals_Error.re: -------------------------------------------------------------------------------- 1 | [@bs.new] external make: string => Js.Exn.t = "Error"; 2 | -------------------------------------------------------------------------------- /packages/web/src/externals/Externals_URLSearchParams.re: -------------------------------------------------------------------------------- 1 | type t; 2 | 3 | [@bs.new] external make: string => t = "URLSearchParams"; 4 | [@bs.new] external makeWithJson: Js.Json.t => t = "URLSearchParams"; 5 | [@bs.send] external toString: t => string = "toString"; 6 | -------------------------------------------------------------------------------- /packages/web/src/externals/FontFaceSet.re: -------------------------------------------------------------------------------- 1 | type t; 2 | 3 | include Webapi.Dom.EventTarget.Impl({ 4 | type nonrec t = t; 5 | }); 6 | 7 | [@bs.scope "document"] [@bs.val] external inst: t = "fonts"; 8 | 9 | [@bs.deriving jsConverter] 10 | type status = [ | [@bs.as "loading"] `Loading | [@bs.as "loaded"] `Loaded]; 11 | [@bs.get] external _status: t => string = "status"; 12 | let status = inst => inst->_status->statusFromJs->Belt.Option.getExn; 13 | 14 | [@bs.get] external ready: t => Js.Promise.t(unit) = "ready"; 15 | 16 | module LoadingDoneEvent = { 17 | [@bs.deriving accessors] 18 | type fontface = { 19 | family: string, 20 | status: string, 21 | style: string, 22 | }; 23 | 24 | [@bs.deriving accessors] 25 | type t = {fontfaces: array(fontface)}; 26 | 27 | include Webapi.Dom.Event.Impl({ 28 | type nonrec t = t; 29 | }); 30 | 31 | external unsafeOfEvent: Dom.event => t = "%identity"; 32 | }; 33 | 34 | [@bs.send] external check: (t, string) => bool = "check"; 35 | -------------------------------------------------------------------------------- /packages/web/src/externals/LiteralWebview.re: -------------------------------------------------------------------------------- 1 | type literalWebview; 2 | [@bs.get] [@bs.return nullable] 3 | external inst: Dom.window => option(literalWebview) = "literalWebview"; 4 | 5 | [@bs.send] external isWebview: literalWebview => bool = "isWebview"; 6 | [@bs.send] external getVersionName: literalWebview => string = "getVersionName"; 7 | [@bs.send] external isFlavorFoss: literalWebview => bool = "isFlavorFoss"; 8 | 9 | [@bs.send] 10 | external sendMessagePort: literalWebview => unit = "sendMessagePort"; 11 | -------------------------------------------------------------------------------- /packages/web/src/externals/MaterialUiLab.re: -------------------------------------------------------------------------------- 1 | module Skeleton = { 2 | [@bs.module "@material-ui/lab/Skeleton/index.js"] [@react.component] 3 | external make: 4 | ( 5 | ~variant: [@bs.string] [ | `text | `circle | `rect], 6 | ~classes: {. "root": string}=? 7 | ) => 8 | React.element = 9 | "default"; 10 | }; 11 | -------------------------------------------------------------------------------- /packages/web/src/externals/bowser.re: -------------------------------------------------------------------------------- 1 | 2 | type t; 3 | 4 | [@bs.deriving abstract] 5 | type navigator = {userAgent: string}; 6 | 7 | external asNavigator: Webapi.Dom.Window.navigator => navigator = "%identity"; 8 | 9 | [@bs.deriving abstract] 10 | type browser = { 11 | name: string, 12 | version: string, 13 | }; 14 | 15 | [@bs.deriving jsConverter] 16 | type browserName = [ | `Firefox | `Chrome | `Safari]; 17 | 18 | let getBrowserName = browser => browser |> nameGet |> browserNameFromJs; 19 | 20 | [@bs.val] [@bs.module "bowser"] external make: string => t = "getParser"; 21 | 22 | [@bs.send] external getBrowser: t => browser = "getBrowser"; 23 | 24 | // https://github.com/lancedikson/bowser/blob/master/src/constants.js#L93 25 | [@bs.deriving jsConverter] 26 | type osName = [ 27 | | [@bs.as "macOS"] `MacOS 28 | | `Android 29 | | [@bs.as "iOS"] `iOS 30 | ]; 31 | 32 | type os = { 33 | name: string, 34 | version: string 35 | }; 36 | 37 | [@bs.send] external getOS: t => os = "getOS"; 38 | 39 | let getOSName = os => osNameFromJs(os.name); 40 | -------------------------------------------------------------------------------- /packages/web/src/externals/lodash.re: -------------------------------------------------------------------------------- 1 | type debounced1('a, 'b) = (. 'a) => 'b; 2 | type debounced2('a, 'b, 'c) = (. 'a, 'b) => 'c; 3 | type debounced3('a, 'b, 'c, 'd) = (. 'a, 'b, 'c) => 'd; 4 | [@bs.send] external flush1: debounced1('a, 'b) => unit = "flush"; 5 | [@bs.send] external flush2: debounced2('a, 'b, 'c) => unit = "flush"; 6 | [@bs.send] external flush3: debounced3('a, 'b, 'c, 'd) => unit = "flush"; 7 | [@bs.module] 8 | external debounce1: ((. 'a) => 'b, int) => debounced1('a, 'b) = 9 | "lodash/debounce"; 10 | [@bs.module] 11 | external debounce2: ((. 'a, 'b) => 'c, int) => debounced2('a, 'b, 'c) = 12 | "lodash/debounce"; 13 | [@bs.module] 14 | external debounce3: ((. 'a, 'b, 'c) => 'd, int) => debounced3('a, 'b, 'c, 'd) = 15 | "lodash/debounce"; 16 | 17 | module Throttled3 = { 18 | type t('a, 'b, 'c, 'd) = (. 'a, 'b, 'c) => 'd; 19 | [@bs.send] external flush: t('a, 'b, 'c, 'd) => unit = "flush"; 20 | [@bs.module] 21 | external make: ((. 'a, 'b, 'c) => 'd, int) => t('a, 'b, 'c, 'd) = 22 | "lodash/throttle"; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/web/src/externals/ramda.re: -------------------------------------------------------------------------------- 1 | [@bs.module] 2 | external mergeDeepLeft: ('a, 'b) => 'c = "ramda/src/mergeDeepLeft"; 3 | 4 | [@bs.module] external _assoc: (string, 'a, 'b) => 'b = "ramda/src/assoc"; 5 | [@bs.module] 6 | external _sortBy: ('a => 'b, Js.Array.t('a)) => Js.Array.t('a) = 7 | "ramda/src/sortBy"; 8 | [@bs.module] 9 | external _sort: ((. 'a, 'a) => int, Js.Array.t('a)) => Js.Array.t('a) = 10 | "ramda/src/sortBy"; 11 | [@bs.module] 12 | external _uniqBy: ('a => 'b, Js.Array.t('a)) => Js.Array.t('a) = 13 | "ramda/src/uniqBy"; 14 | 15 | /** pipe-first variants, ramda is pipe-last **/ 16 | let sortBy = (arr, pred) => _sortBy(pred, arr); 17 | let sort = (arr, pred) => _sort(pred, arr); 18 | let assoc = (obj, key, data) => _assoc(key, data, obj); 19 | let uniqBy = (arr, pred) => _uniqBy(pred, arr); 20 | -------------------------------------------------------------------------------- /packages/web/src/externals/raw.re: -------------------------------------------------------------------------------- 1 | let merge = [%raw {| 2 | function(a, b) { return {...a, ...b} } 3 | |}]; 4 | 5 | let unsafeEq = [%raw 6 | {| function(a, b) { return a === b } |} 7 | ]; 8 | 9 | let global = [%raw {| 10 | function () { 11 | if (typeof self !== 'undefined') { return self; } 12 | if (typeof window !== 'undefined') { return window; } 13 | if (typeof global !== 'undefined') { return global; } 14 | throw new Error('unable to locate global object'); 15 | } 16 | |}]; 17 | 18 | let isBrowser = [%raw {| 19 | function () { 20 | return typeof window !== 'undefined' 21 | } 22 | |}]; 23 | 24 | let maybeScrollTo = [%raw {| 25 | function (elem, params) { 26 | if (elem.scrollTo) { 27 | elem.scrollTo(params) 28 | } 29 | } 30 | |}]; 31 | -------------------------------------------------------------------------------- /packages/web/src/externals/uuid.re: -------------------------------------------------------------------------------- 1 | [@bs.val] [@bs.module "uuid"] external makeV4: unit => string = "v4"; 2 | -------------------------------------------------------------------------------- /packages/web/src/routes/Route_Creators.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let default = (~rehydrated) => { 3 | let Providers_Authentication.{user} = 4 | React.useContext(Providers_Authentication.authenticationContext); 5 | 6 | let _ = 7 | React.useEffect1( 8 | () => { 9 | let _ = 10 | switch (user) { 11 | | SignedInUser({identityId}) 12 | | GuestUser({identityId}) => 13 | Routes.CreatorsIdAnnotationCollectionsId.( 14 | Next.Router.replaceWithAs( 15 | staticPath, 16 | path( 17 | ~identityId, 18 | ~annotationCollectionIdComponent=Lib_GraphQL.AnnotationCollection.recentAnnotationCollectionIdComponent, 19 | ), 20 | ) 21 | ) 22 | | SignedOutPromptAuthentication => 23 | Next.Router.replace(Routes.Authenticate.path()) 24 | | Unknown => () 25 | }; 26 | 27 | None; 28 | }, 29 | [|user|], 30 | ); 31 | 32 | ; 33 | }; 34 | 35 | let page = "creators.js" 36 | -------------------------------------------------------------------------------- /packages/web/src/routes/Route_Creators_Id_Annotation_Collections.re: -------------------------------------------------------------------------------- 1 | [@react.component] 2 | let default = (~rehydrated) => { 3 | let Providers_Authentication.{user} = 4 | React.useContext(Providers_Authentication.authenticationContext); 5 | let router = Next.Router.useRouter(); 6 | 7 | let _ = 8 | React.useEffect1( 9 | () => { 10 | let _ = 11 | switch (user) { 12 | | SignedOutPromptAuthentication => 13 | Next.Router.replace(Routes.Authenticate.path()) 14 | | _ => () 15 | }; 16 | None; 17 | }, 18 | [|user|], 19 | ); 20 | 21 | switch ( 22 | Routes.CreatorsIdAnnotationCollections.params_decode(router.Next.query) 23 | ) { 24 | | Ok(_) => 25 | | _ => 26 | ()} 29 | /> 30 | }; 31 | }; 32 | 33 | let page = "creators/[identityId]/annotation-collections.js"; 34 | -------------------------------------------------------------------------------- /packages/web/src/services/Service_Log.re: -------------------------------------------------------------------------------- 1 | let log = (tag, message) => 2 | if (Constants.Env.nodeEnv != "production") { 3 | Js.log2("[" ++ tag ++ "]", message); 4 | }; 5 | 6 | let log2 = (tag, message1, message2) => 7 | if (Constants.Env.nodeEnv != "production") { 8 | Js.log3("[" ++ tag ++ "]", message1, message2); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/web/src/services/Timer.re: -------------------------------------------------------------------------------- 1 | let thunkP = (~label, fn) => { 2 | let begin_ = Js.Date.now(); 3 | fn() 4 | |> Js.Promise.then_(result => { 5 | let end_ = Js.Date.now(); 6 | Js.log3("timing", label, Js.Float.toString(end_ -. begin_) ++ "ms"); 7 | Js.Promise.resolve(result); 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/web/src/static/json/writing.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Implementing Guest Authentication with AWS Amplify", 4 | "subtitle": "A technical walkthrough of Literal's guest authentication implementation.", 5 | "date": "2021-06-08", 6 | "filename": "implementing-guest-authentication-with-amplify-cognito-appsync" 7 | }, 8 | { 9 | "title": "Prologue", 10 | "subtitle": "Introducing Literal, today and tomorrow.", 11 | "date": "2020-12-26", 12 | "filename": "prologue" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /packages/web/src/static/markdown/subprocessors.md: -------------------------------------------------------------------------------- 1 | # Subprocessors 2 | 3 | We use third party subprocessors, such as cloud computing providers and analytics software, to run Literal. We establish GDPR-compliant data processing agreements with each subprocessor, extending GDPR safeguards everywhere personal data is processed. 4 | 5 | The following is a list of personal data subprocessors we use. These subprocessors are all located in the United States: 6 | 7 | - [Amazon Web Services](https://aws.amazon.com/compliance/gdpr-center/). Cloud services provider. 8 | - [Google Cloud Platform](https://cloud.google.com/security/gdpr/resource-center/). Cloud services provider. 9 | - [Sentry](https://blog.sentry.io/2018/03/14/gdpr-sentry-and-you). Error reporting software. 10 | - [Amplitude](https://blog.amplitude.com/amplitude-gdpr). Product analytics software. 11 | -------------------------------------------------------------------------------- /packages/web/src/styles.re: -------------------------------------------------------------------------------- 1 | let cn = Cn.make; 2 | 3 | let style = ReactDOMRe.Style.make; 4 | 5 | let rem = r => Js.Float.toString(r) ++ "rem"; 6 | 7 | let unsafeStyle = (style, unsafeList) => 8 | List.fold_left( 9 | (acc, (prop, value)) => 10 | ReactDOMRe.Style.unsafeAddProp(acc, prop, value), 11 | style, 12 | unsafeList, 13 | ); 14 | -------------------------------------------------------------------------------- /secret/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/literal-io/literal/dcdb90ae3eacb7939e15bb3437cc761cf1be6702/secret/.gitkeep --------------------------------------------------------------------------------