Skip to content

Commit

Permalink
Merge branch 'beta' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer authored Apr 12, 2024
2 parents 8f735e5 + 89b6e78 commit 8143779
Show file tree
Hide file tree
Showing 61 changed files with 7,752 additions and 1,487 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["./tsconfig.json", "./example/tsconfig.json"]
},
plugins: ["@typescript-eslint"],
extends: ['universe/native', 'universe/web'],
ignorePatterns: ['build'],
plugins: ['prettier'],
globals: {
__dirname: true,
},
rules: {
"@typescript-eslint/no-floating-promises": ["error"],
},
}
13 changes: 13 additions & 0 deletions .github/workflows/tsc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Typescript
on:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v3
- run: yarn
- run: yarn tsc
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ android/keystores/debug.keystore

# Typedocs
docs/
**/.yarn/*
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,3 +437,7 @@ The `env` parameter accepts one of three valid values: `dev`, `production`, or `
- `local`: Use to have a client communicate with an XMTP node you are running locally. For example, an XMTP node developer can set `env` to `local` to generate client traffic to test a node running locally.

The `production` network is configured to store messages indefinitely. XMTP may occasionally delete messages and keys from the `dev` network, and will provide advance notice in the [XMTP Discord community](https://discord.gg/xmtp).

## Enabling group chat

Coming soon...
11 changes: 11 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,15 @@ dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.facebook.react:react-native:0.71.3'
implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1"
// xmtp-android local testing setup below (comment org.xmtp:android above)
// implementation files('<PATH TO XMTP-ANDROID>/xmtp-android/library/build/outputs/aar/library-debug.aar')
// implementation 'com.google.crypto.tink:tink-android:1.7.0'
// implementation 'io.grpc:grpc-kotlin-stub:1.3.0'
// implementation 'io.grpc:grpc-okhttp:1.51.1'
// implementation 'io.grpc:grpc-protobuf-lite:1.51.0'
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
// implementation 'org.web3j:crypto:5.0.0'
// implementation "net.java.dev.jna:jna:5.13.0@aar"
// implementation 'com.google.protobuf:protobuf-kotlin-lite:3.22.3'
// implementation 'org.xmtp:proto-kotlin:3.40.1'
}
508 changes: 489 additions & 19 deletions android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import org.xmtp.android.library.codecs.description
import org.xmtp.android.library.codecs.getReactionAction
import org.xmtp.android.library.codecs.getReactionSchema
import org.xmtp.android.library.codecs.id
import uniffi.xmtpv3.org.xmtp.android.library.codecs.ContentTypeGroupMembershipChange
import uniffi.xmtpv3.org.xmtp.android.library.codecs.GroupMembershipChangeCodec
import uniffi.xmtpv3.org.xmtp.android.library.codecs.GroupMembershipChanges
import java.net.URL

class ContentJson(
Expand All @@ -51,6 +54,7 @@ class ContentJson(
Client.register(RemoteAttachmentCodec())
Client.register(ReplyCodec())
Client.register(ReadReceiptCodec())
Client.register(GroupMembershipChangeCodec())
}

fun fromJsonObject(obj: JsonObject): ContentJson {
Expand Down Expand Up @@ -171,6 +175,21 @@ class ContentJson(
"readReceipt" to ""
)

ContentTypeGroupMembershipChange.id -> mapOf(
"groupChange" to mapOf(
"membersAdded" to (content as GroupMembershipChanges).membersAddedList.map {
mapOf(
"address" to it.accountAddress,
"initiatedByAddress" to it.initiatedByAccountAddress
)},
"membersRemoved" to content.membersRemovedList.map {
mapOf(
"address" to it.accountAddress,
"initiatedByAddress" to it.initiatedByAccountAddress
)},
)
)

else -> {
val json = JsonObject()
encodedContent?.let {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package expo.modules.xmtpreactnativesdk.wrappers

import android.util.Base64
import com.google.gson.GsonBuilder
import org.xmtp.android.library.Client
import org.xmtp.android.library.Conversation

class ConversationContainerWrapper {

companion object {
fun encodeToObj(client: Client, conversation: Conversation): Map<String, Any> {
when (conversation.version) {
Conversation.Version.GROUP -> {
val group = (conversation as Conversation.Group).group
return GroupWrapper.encodeToObj(client, group)
}
else -> {
return ConversationWrapper.encodeToObj(client, conversation)
}
}
}

fun encode(client: Client, conversation: Conversation): String {
val gson = GsonBuilder().create()
val obj = ConversationContainerWrapper.encodeToObj(client, conversation)
return gson.toJson(obj)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ConversationWrapper {
"context" to context,
"topic" to conversation.topic,
"peerAddress" to conversation.peerAddress,
"version" to if (conversation.version == Conversation.Version.V1) "v1" else "v2",
"version" to "DIRECT",
"conversationID" to (conversation.conversationId ?: ""),
"keyMaterial" to (conversation.keyMaterial?.let { Base64.encodeToString(it, Base64.NO_WRAP) } ?: "")
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package expo.modules.xmtpreactnativesdk.wrappers

import android.util.Base64
import android.util.Base64.NO_WRAP
import com.google.gson.GsonBuilder
import org.xmtp.android.library.Client
import org.xmtp.android.library.Conversation
import org.xmtp.android.library.Group
import org.xmtp.android.library.toHex
import uniffi.xmtpv3.GroupPermissions

class GroupWrapper {

companion object {
fun encodeToObj(client: Client, group: Group): Map<String, Any> {
val permissionString = when (group.permissionLevel()) {
GroupPermissions.EVERYONE_IS_ADMIN -> "everyone_admin"
GroupPermissions.GROUP_CREATOR_IS_ADMIN -> "creator_admin"
}
return mapOf(
"clientAddress" to client.address,
"id" to group.id.toHex(),
"createdAt" to group.createdAt.time,
"peerAddresses" to Conversation.Group(group).peerAddresses,
"version" to "GROUP",
"topic" to group.id.toHex(),
"permissionLevel" to permissionString,
"adminAddress" to group.adminAddress()
)
}

fun encode(client: Client, group: Group): String {
val gson = GsonBuilder().create()
val obj = encodeToObj(client, group)
return gson.toJson(obj)
}
}
}
6 changes: 6 additions & 0 deletions example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { XmtpProvider } from 'xmtp-react-native-sdk'

import ConversationCreateScreen from './src/ConversationCreateScreen'
import ConversationScreen from './src/ConversationScreen'
import GroupScreen from './src/GroupScreen'
import HomeScreen from './src/HomeScreen'
import LaunchScreen from './src/LaunchScreen'
import { Navigator } from './src/Navigation'
Expand Down Expand Up @@ -90,6 +91,11 @@ export default function App() {
options={{ title: 'Conversation' }}
initialParams={{ topic: '' }}
/>
<Navigator.Screen
name="group"
component={GroupScreen}
options={{ title: 'Group' }}
/>
<Navigator.Screen
name="conversationCreate"
component={ConversationCreateScreen}
Expand Down
16 changes: 14 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ PODS:
- React-Core
- react-native-encrypted-storage (4.0.3):
- React-Core
- react-native-get-random-values (1.10.0):
- react-native-get-random-values (1.11.0):
- React-Core
- react-native-mmkv (2.11.0):
- MMKV (>= 1.2.13)
Expand All @@ -344,6 +344,8 @@ PODS:
- RCTTypeSafety
- React-Core
- ReactCommon/turbomodule/core
- react-native-sqlite-storage (6.0.1):
- React-Core
- react-native-webview (13.8.1):
- RCT-Folly (= 2021.07.22.00)
- React-Core
Expand Down Expand Up @@ -433,6 +435,8 @@ PODS:
- React-perflogger (= 0.71.14)
- RNCAsyncStorage (1.21.0):
- React-Core
- RNFS (2.20.0):
- React-Core
- RNScreens (3.20.0):
- React-Core
- React-RCTImage
Expand Down Expand Up @@ -505,6 +509,7 @@ DEPENDENCIES:
- react-native-quick-crypto (from `../node_modules/react-native-quick-crypto`)
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
Expand All @@ -520,6 +525,7 @@ DEPENDENCIES:
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- RNFS (from `../node_modules/react-native-fs`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`)
- XMTPReactNative (from `../../ios`)
Expand Down Expand Up @@ -634,6 +640,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-randombytes"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-sqlite-storage:
:path: "../node_modules/react-native-sqlite-storage"
react-native-webview:
:path: "../node_modules/react-native-webview"
React-perflogger:
Expand Down Expand Up @@ -664,6 +672,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNFS:
:path: "../node_modules/react-native-fs"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSVG:
Expand Down Expand Up @@ -724,13 +734,14 @@ SPEC CHECKSUMS:
react-native-blob-util: d8fa1a7f726867907a8e43163fdd8b441d4489ea
react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8
react-native-encrypted-storage: db300a3f2f0aba1e818417c1c0a6be549038deb7
react-native-get-random-values: 384787fd76976f5aec9465aff6fa9e9129af1e74
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-mmkv: e97c0c79403fb94577e5d902ab1ebd42b0715b43
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
react-native-quick-base64: 777057ea4286f806b00259ede65dc79c7c706320
react-native-quick-crypto: 455c1b411db006dba1026a30681ececb19180187
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
react-native-webview: bdc091de8cf7f8397653e30182efcd9f772e03b3
React-perflogger: 4987ad83731c23d11813c84263963b0d3028c966
React-RCTActionSheet: 5ad952b2a9740d87a5bd77280c4bc23f6f89ea0c
Expand All @@ -746,6 +757,7 @@ SPEC CHECKSUMS:
React-runtimeexecutor: ffe826b7b1cfbc32a35ed5b64d5886c0ff75f501
ReactCommon: 7f3dd5e98a9ec627c6b03d26c062bf37ea9fc888
RNCAsyncStorage: 618d03a5f52fbccb3d7010076bc54712844c18ef
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
Expand Down
5 changes: 4 additions & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@
"react-native-config": "^1.5.1",
"react-native-crypto": "^2.2.0",
"react-native-encrypted-storage": "^4.0.3",
"react-native-get-random-values": "^1.10.0",
"react-native-fs": "^2.20.0",
"react-native-get-random-values": "^1.11.0",
"react-native-mmkv": "^2.8.0",
"react-native-modal-selector": "^2.1.2",
"react-native-quick-base64": "^2.0.8",
"react-native-quick-crypto": "^0.6.1",
"react-native-randombytes": "^3.6.1",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"react-native-sqlite-storage": "^6.0.1",
"react-native-svg": "^13.9.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-webview": "^13.8.1",
Expand Down
37 changes: 30 additions & 7 deletions example/src/ConversationCreateScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { useState } from 'react'
import { Button, ScrollView, Text, TextInput } from 'react-native'
import { Button, ScrollView, Switch, Text, TextInput, View } from 'react-native'
import { useXmtp } from 'xmtp-react-native-sdk'

import { NavigationParamList } from './Navigation'
Expand All @@ -13,19 +13,33 @@ export default function ConversationCreateScreen({
const [alert, setAlert] = useState<string>('')
const [isCreating, setCreating] = useState<boolean>(false)
const { client } = useXmtp()
const [groupsEnabled, setGroupsEnabled] = useState(false)

const startNewConversation = async (toAddress: string) => {
if (!client) {
setAlert('Client not initialized')
return
}
const canMessage = await client.canMessage(toAddress)
if (!canMessage) {
setAlert(`${toAddress} is not on the XMTP network yet`)
return
if (groupsEnabled) {
const toAddresses = toAddress.split(',')
const canMessage = await client.canGroupMessage(toAddresses)
if (!canMessage) {
setAlert(`${toAddress} cannot be added to a group conversation yet`)
return
}
const group = await client.conversations.newGroup(toAddresses)
navigation.navigate('group', { id: group.id })
} else {
const canMessage = await client.canMessage(toAddress)
if (!canMessage) {
setAlert(`${toAddress} is not on the XMTP network yet`)
return
}
const convo = await client.conversations.newConversation(toAddress)
navigation.navigate('conversation', { topic: convo.topic })
}
const convo = await client.conversations.newConversation(toAddress)
navigation.navigate('conversation', { topic: convo.topic })
}

return (
<>
<ScrollView>
Expand All @@ -48,6 +62,15 @@ export default function ConversationCreateScreen({
opacity: isCreating ? 0.5 : 1,
}}
/>
<View>
<Switch
value={groupsEnabled}
onValueChange={() =>
setGroupsEnabled((previousState) => !previousState)
}
/>
<Text>Create Group: {groupsEnabled ? 'ON' : 'OFF'}</Text>
</View>
<Button
title="Start conversation"
onPress={() => {
Expand Down
Loading

0 comments on commit 8143779

Please sign in to comment.