Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
root: true,
extends: ['universe/native', 'universe/web'],
ignorePatterns: ['build'],
};
32 changes: 32 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ node_modules/
npm-debug.log
yarn-error.log
lib/
# VSCode
.vscode/
jsconfig.json

# Xcode
#
Expand Down Expand Up @@ -41,3 +44,32 @@ local.properties
buck-out/
\.buckd/
*.keystore
# Android/IJ
#
.classpath
.cxx
.gradle
.idea
.project
.settings
local.properties
android.iml
android/app/libs
android/keystores/debug.keystore

# Cocoapods
#
example/ios/Pods

# Ruby
example/vendor/

# node.js
#
node_modules/
npm-debug.log
yarn-debug.log
yarn-error.log

# Expo
.expo/*
20 changes: 12 additions & 8 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
build/
android/build/
android/BUCK
android/src/main/res/
*.iml
# Exclude all top-level hidden directories by convention
/.*/

yarn.lock
node_modules/
# Exclude tarballs generated by `npm pack`
/*.tgz

.idea
__mocks__
__tests__

/babel.config.js
/android/src/androidTest/
/android/src/test/
/android/build/
/example/
162 changes: 37 additions & 125 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,135 +1,47 @@
// android/build.gradle

// based on:
//
// * https://github.com/facebook/react-native/blob/0.60-stable/template/android/build.gradle
// original location:
// - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/build.gradle
//
// * https://github.com/facebook/react-native/blob/0.60-stable/template/android/app/build.gradle
// original location:
// - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/app/build.gradle

def DEFAULT_MIN_SDK_VERSION = 21
def DEFAULT_TARGET_SDK_VERSION = 35
def DEFAULT_COMPILE_SDK_VERSION = 35
def DEFAULT_BUILD_TOOLS_VERSION = '35.0.0'

def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

apply plugin: 'com.android.library'
apply plugin: 'maven-publish'

buildscript {
// The Android Gradle plugin is only required when opening the android folder stand-alone.
// This avoids unnecessary downloads and potential conflicts when the library is included as a
// module dependency in an application project.
// ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies
if (project == rootProject) {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
}
}
}

android {
compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
group = 'expo.modules.helpscout'
version = '0.1.0'

def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useExpoPublishing()

// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
// Most of the time, you may like to manage the Android SDK versions yourself.
def useManagedAndroidSdkVersions = false
if (useManagedAndroidSdkVersions) {
useDefaultAndroidSdkVersions()
} else {
buildscript {
// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
}
project.android {
compileSdkVersion safeExtGet("compileSdkVersion", 36)
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}

repositories {
mavenCentral()
// ref: https://www.baeldung.com/maven-local-repository
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
maven {
// Android JSC is installed from npm
url "$rootDir/../node_modules/jsc-android/dist"
minSdkVersion safeExtGet("minSdkVersion", 24)
targetSdkVersion safeExtGet("targetSdkVersion", 36)
}
google()
}
}

dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+' // From node_modules
implementation "com.helpscout:beacon:6.0.1"
}

def configureReactNativePom(def pom) {
def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text)

pom.project {
name packageJson.title
artifactId packageJson.name
version = packageJson.version
group = "com.driversnote"
description packageJson.description
url packageJson.repository.baseUrl

licenses {
license {
name packageJson.license
url packageJson.repository.baseUrl + '/blob/master/' + packageJson.licenseFilename
distribution 'repo'
}
}

developers {
developer {
id packageJson.author.username
name packageJson.author.name
}
}
}
implementation "com.helpscout:beacon:6.0.1"
}

afterEvaluate { project ->
task androidSourcesJar(type: Jar) {
archiveClassifier = 'sources'
from android.sourceSets.main.java.srcDirs
include '**/*.java'
}

android.libraryVariants.all { variant ->
def name = variant.name.capitalize()
def javaCompileTask = variant.javaCompileProvider.get()

task "jar${name}"(type: Jar, dependsOn: javaCompileTask) {
from javaCompileTask.destinationDir
}
}

artifacts {
archives androidSourcesJar
}

publishing {
publications {
maven(MavenPublication) {
artifact androidSourcesJar
}
}
}
android {
namespace "expo.modules.helpscout"
defaultConfig {
versionCode 1
versionName "0.1.0"
}
lintOptions {
abortOnError false
}
}
4 changes: 1 addition & 3 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.driversnote">

<manifest>
</manifest>
139 changes: 139 additions & 0 deletions android/src/main/java/expo/modules/helpscout/ExpoHelpscoutModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package expo.modules.helpscout

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
import java.net.URL
import android.text.TextUtils
import android.util.Log
import android.content.Context
import com.helpscout.beacon.Beacon
import com.helpscout.beacon.ui.BeaconActivity
import com.helpscout.beacon.model.BeaconScreens
import com.helpscout.beacon.model.SuggestedArticle


class ExpoHelpscoutModule : Module() {
private val TAG = "com.driversnote.helpscoutbeacon"
private var beacon: Beacon? = null

// Each module class must implement the definition function. The definition consists of components
// that describes the module's functionality and behavior.
// See https://docs.expo.dev/modules/module-api for more details about available components.
override fun definition() = ModuleDefinition {
// Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
// Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
// The module will be accessible from `requireNativeModule('ExpoHelpscout')` in JavaScript.
Name("ExpoHelpscout")

Function("init") { beaconID: String? ->
if (beaconID.isNullOrEmpty()) {
Log.w(TAG, "[init] Missing argument: beaconID")
return@Function
}
beacon = Beacon.Builder()
.withBeaconId(beaconID)
.build()
}

Function("identify") { email: String?, name: String? ->
if (beacon == null || email.isNullOrEmpty() || name.isNullOrEmpty()) {
if (beacon == null) Log.w(TAG, "[identifyWithEmailAndName] Not initialized - did you forget to call 'init'?")
if (email.isNullOrEmpty()) Log.w(TAG, "[identifyWithEmailAndName] Missing argument: email")
if (name.isNullOrEmpty()) Log.w(TAG, "[identifyWithEmailAndName] Missing argument: name")
return@Function
}
Beacon.identify(email, name)
}

Function("logout") {
if (beacon == null) {
Log.w(TAG, "[logout] Not initialized - did you forget to call 'init'?")
return@Function null
}
Beacon.logout()
null
}

Function("addAttributeWithKey") { key: String?, value: String? ->
if (beacon == null || key.isNullOrEmpty() || value.isNullOrEmpty()) {
if (beacon == null) Log.w(TAG, "[addAttributeWithKey] Not initialized - did you forget to call 'init'?")
if (key.isNullOrEmpty()) Log.w(TAG, "[addAttributeWithKey] Missing argument: key")
if (value.isNullOrEmpty()) Log.w(TAG, "[addAttributeWithKey] Missing argument: value")
return@Function
}
Beacon.addAttributeWithKey(key, value)
}

Function("open") { signature: String? ->
if (beacon == null) {
Log.w(TAG, "[open] Not initialized - did you forget to call 'init'?")
return@Function
}
val context = appContext.reactContext ?: return@Function
if (signature.isNullOrEmpty()) {
BeaconActivity.open(context)
} else {
BeaconActivity.openInSecureMode(context, signature)
}
}
//
Function("openArticle") { articleID: String?, signature: String? ->
if (beacon == null || articleID.isNullOrEmpty()) {
if (beacon == null) Log.w(TAG, "[openArticle] Not initialized - did you forget to call 'init'?")
if (articleID.isNullOrEmpty()) Log.w(TAG, "[openArticle] Missing argument: articleID")
return@Function
}

val context = appContext.reactContext ?: return@Function
val articleList = arrayListOf(articleID)

if (signature.isNullOrEmpty()) {
BeaconActivity.open(context, BeaconScreens.ARTICLE_SCREEN, articleList)
} else {
BeaconActivity.openInSecureMode(context, signature, BeaconScreens.ARTICLE_SCREEN, articleList)
}
}

Function("navigate") { path: String? ->
if (beacon == null) {
Log.w(TAG, "[navigate] Not initialized - did you forget to call 'init'?")
return@Function
}

val context = appContext.reactContext ?: return@Function
when (path) {
"/ask/message/" -> BeaconActivity.open(context, BeaconScreens.CONTACT_FORM_SCREEN, arrayListOf())
"/ask/chat/" -> BeaconActivity.open(context, BeaconScreens.CHAT, arrayListOf())
"/answers/" -> BeaconActivity.open(context, BeaconScreens.PREVIOUS_MESSAGES, arrayListOf())
else -> Log.w(TAG, "[navigate] Path '${path ?: "null"}' not supported")
}
}

Function("suggestArticles") { articleIds: List<String>? ->
if (beacon == null) {
Log.w(TAG, "[suggestArticles] Not initialized - did you forget to call 'init'?")
return@Function
}

if (articleIds.isNullOrEmpty()) {
Log.w(TAG, "[suggestArticles] Missing or empty articleIds")
return@Function
}

val suggestedArticles = articleIds.take(5).map {
SuggestedArticle.SuggestedArticleWithId(it)
}

Beacon.setOverrideSuggestedArticlesOrLinks(suggestedArticles)
}

Function("resetSuggestions") {
if (beacon == null) {
Log.w(TAG, "[resetSuggestions] Not initialized - did you forget to call 'init'?")
return@Function null
}
Beacon.setOverrideSuggestedArticlesOrLinks(emptyList())
}

}
}
Loading