Skip to content

Commit

Permalink
Load webcocket after long period (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
KoalaSat authored Jan 9, 2023
2 parents 7bdd51a + e62eb1c commit 72733ed
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 66 deletions.
2 changes: 2 additions & 0 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import App from './frontend'
import { Buffer as SafeBuffer } from 'safe-buffer'
import { randomBytes } from '@noble/hashes/utils'
import 'text-encoding-polyfill'

global.Buffer = SafeBuffer
global.randomBytes = randomBytes

export default App
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ Local setup: https://github.com/KoalaSat/nostros/blob/main/SETUP.md

# Some Features to Work On

### Bugs

- [ ] Websocket connections closed when the app goes to background for too long

### Home

- [ ] Public Room
Expand Down
27 changes: 22 additions & 5 deletions android/app/src/main/java/com/nostros/classes/Websocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.neovisionaries.ws.client.HostnameUnverifiedException;
import com.neovisionaries.ws.client.OpeningHandshakeException;
import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketAdapter;
import com.neovisionaries.ws.client.WebSocketException;
import com.neovisionaries.ws.client.WebSocketFactory;
import com.neovisionaries.ws.client.WebSocketFrame;
import com.nostros.modules.DatabaseModule;
Expand All @@ -19,12 +16,12 @@
import org.json.JSONObject;

import java.io.IOException;
import java.util.ArrayList;

public class Websocket {
private WebSocket webSocket;
private DatabaseModule database;
private String url;
private String pubKey;
private ReactApplicationContext context;

public Websocket(String serverUrl, DatabaseModule databaseModule, ReactApplicationContext reactContext) {
Expand All @@ -35,6 +32,13 @@ public Websocket(String serverUrl, DatabaseModule databaseModule, ReactApplicati

public void send(String message) {
Log.d("Websocket", "SEND URL:" + url + " __ " + message);
if (!webSocket.isOpen()) {
try {
this.connect(pubKey);
} catch (IOException e) {
e.printStackTrace();
}
}
webSocket.sendText(message);
}

Expand All @@ -44,6 +48,7 @@ public void disconnect() {

public void connect(String userPubKey) throws IOException {
WebSocketFactory factory = new WebSocketFactory();
pubKey = userPubKey;
webSocket = factory.createSocket(url);
webSocket.setMissingCloseFrameAllowed(true);
webSocket.setPingInterval(25 * 1000);
Expand All @@ -53,10 +58,13 @@ public void connect(String userPubKey) throws IOException {
public void onTextMessage(WebSocket websocket, String message) throws Exception {
Log.d("Websocket", "RECEIVE URL:" + url + " __ " + message);
JSONArray jsonArray = new JSONArray(message);
if (jsonArray.get(0).toString().equals("EVENT")) {
String messageType = jsonArray.get(0).toString();
if (messageType.equals("EVENT")) {
JSONObject data = jsonArray.getJSONObject(2);
database.saveEvent(data, userPubKey);
reactNativeEvent(data.getString("id"));
} else if (messageType.equals("OK")) {
reactNativeConfirmation(jsonArray.get(1).toString());
}
}
@Override
Expand All @@ -75,4 +83,13 @@ public void reactNativeEvent(String eventId) {
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("WebsocketEvent", payload);
}

public void reactNativeConfirmation(String eventId) {
Log.d("Websocket", "reactNativeConfirmation" + eventId);
WritableMap payload = Arguments.createMap();
payload.putString("eventId", eventId);
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("WebsocketConfirmation", payload);
}
}
5 changes: 2 additions & 3 deletions frontend/Components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const LoadingIndicator = (): ReactElement => (

/**
* Extension of the UI-Kitten button, with more features.
* @param param0
* @returns
* @param param0
* @returns
*/
export const Button: React.FC<ButtonProps> = ({
disabled,
Expand All @@ -25,7 +25,6 @@ export const Button: React.FC<ButtonProps> = ({
accessoryLeft,
...otherProps
}) => {
console.log('Is loading', loading)
return (
<UIKButton
disabled={disabled ?? loading}
Expand Down
22 changes: 17 additions & 5 deletions frontend/Contexts/RelayPoolContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface RelayPoolContextProps {
privateKey?: string
setPrivateKey: (privateKey: string | undefined) => void
lastEventId?: string
setLastEventId: (lastEventId: string) => void
lastConfirmationtId?: string
}

export interface WebsocketEvent {
Expand All @@ -32,7 +32,6 @@ export const initialRelayPoolContext: RelayPoolContextProps = {
setPublicKey: () => {},
setPrivateKey: () => {},
setRelayPool: () => {},
setLastEventId: () => {},
}

export const RelayPoolContextProvider = ({
Expand All @@ -48,17 +47,30 @@ export const RelayPoolContextProvider = ({
initialRelayPoolContext.loadingRelayPool,
)
const [lastEventId, setLastEventId] = useState<string>('')
const [lastConfirmationtId, setLastConfirmationId] = useState<string>('')
const [lastPage, setLastPage] = useState<string>(page)

const changeHandler: (event: WebsocketEvent) => void = (event) => {
const changeEventIdHandler: (event: WebsocketEvent) => void = (event) => {
setLastEventId(event.eventId)
}
const changeConfirmationIdHandler: (event: WebsocketEvent) => void = (event) => {
console.log('changeConfirmationIdHandler', event)
setLastConfirmationId(event.eventId)
}

const debouncedEventIdHandler = useMemo(() => debounce(changeHandler, 1000), [setLastEventId])
const debouncedEventIdHandler = useMemo(
() => debounce(changeEventIdHandler, 1000),
[setLastEventId],
)
const debouncedConfirmationHandler = useMemo(
() => debounce(changeConfirmationIdHandler, 500),
[setLastConfirmationId],
)

const loadRelayPool: () => void = async () => {
if (database && publicKey) {
DeviceEventEmitter.addListener('WebsocketEvent', debouncedEventIdHandler)
DeviceEventEmitter.addListener('WebsocketConfirmation', debouncedConfirmationHandler)
const initRelayPool = new RelayPool([], privateKey)
initRelayPool.connect(publicKey, (eventId: string) => setLastEventId(eventId))
setRelayPool(initRelayPool)
Expand Down Expand Up @@ -121,7 +133,7 @@ export const RelayPoolContextProvider = ({
privateKey,
setPrivateKey,
lastEventId,
setLastEventId,
lastConfirmationtId,
}}
>
{children}
Expand Down
6 changes: 3 additions & 3 deletions frontend/Locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@
"profilePublished": "Профиль опубликован",
"profilePublishError": "При публикации профиля произошла ошибка",
"invoiceCopied": "Инвойс копирован",
"invoiceError": "Произошла ошибка при попытке получить инвойса",
"sendNoteSuccess": "Примечание отправлено",
"invoiceError": "Произошла ошибка при попытке получить инвойс",
"sendNoteSuccess": "Заметка отправлена",
"sendNoteError": "При отправке заметки произошла ошибка",
"sendGetNotesError": "При получении заметок произошла внутренняя ошибка",
"sendGetNotesError": "При загрузке заметок произошла внутренняя ошибка",
"contactAddError": "При добавлении контакта произошла ошибка",
"privateMessageEncryptError": "При шифровании сообщения произошла ошибка",
"privateMessageSendError": "При отправке сообщения произошла ошибка"
Expand Down
4 changes: 3 additions & 1 deletion frontend/Pages/ContactsPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getUsers, updateUserContact, User } from '../../Functions/DatabaseFunct
import { Button, UserCard } from '../../Components'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { populatePets } from '../../Functions/RelayFunctions/Users'
import { getNip19Key } from '../../lib/nostr/Nip19'

export const ContactsPage: React.FC = () => {
const { database, goBack } = useContext(AppContext)
Expand Down Expand Up @@ -85,7 +86,8 @@ export const ContactsPage: React.FC = () => {
const onPressAddContact: () => void = () => {
if (contactInput && relayPool && database && publicKey) {
setIsAddingContact(true)
updateUserContact(contactInput, database, true)
const hexKey = getNip19Key(contactInput)
updateUserContact(hexKey, database, true)
.then(() => {
populatePets(relayPool, database, publicKey)
setShowAddContact(false)
Expand Down
42 changes: 21 additions & 21 deletions frontend/Pages/ConversationPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const ConversationPage: React.FC = () => {
if (user) setOtherUser(user)
})
getDirectMessages(database, { conversationId, order: 'ASC' }).then((results) => {
if (results && results.length > 0) {
if (privateKey && results && results.length > 0) {
setSendingMessages([])
setDirectMessages(
results.map((message) => {
Expand Down Expand Up @@ -101,29 +101,29 @@ export const ConversationPage: React.FC = () => {
}
setSendingMessages((prev) => [...prev, event as DirectMessage])
setInput('')

const encryptedcontent = encrypt(privateKey, otherPubKey, input)
encrypt(privateKey, otherPubKey, input).then((content) => {
relayPool
?.sendEvent({
...event,
content: encryptedcontent,
})
.catch((err) => {
showMessage({
message: t('alerts.privateMessageSendError'),
description: err.message,
type: 'danger',

encrypt(privateKey, otherPubKey, input)
.then((encryptedcontent) => {
relayPool
?.sendEvent({
...event,
content: encryptedcontent,
})
.catch((err) => {
showMessage({
message: t('alerts.privateMessageSendError'),
description: err.message,
type: 'danger',
})
})
})
.catch((err) => {
showMessage({
message: t('alerts.privateMessageEncryptError'),
description: err.message,
type: 'danger',
})
})
.catch((err) => {
showMessage({
message: t('alerts.privateMessageEncryptError'),
description: err.message,
type: 'danger',
})
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/Pages/HomePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const HomePage: React.FC = () => {
}

const subscribeNotes: (users: User[], past?: boolean) => void = async (users, past) => {
if (!database || !publicKey || users.length === 0) return
if (!database || !publicKey) return

const lastNotes: Note[] = await getMainNotes(database, publicKey, initialPageSize)
const lastNote: Note = lastNotes[lastNotes.length - 1]
Expand Down
2 changes: 1 addition & 1 deletion frontend/Pages/LandingPage/Loader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const Loader: React.FC = () => {
relayPool?.subscribe('main-channel', {
kinds: [EventKind.meta, EventKind.textNote],
authors,
since: moment().unix() - 86400,
since: moment().unix() - 86400 * 2,
})
}
})
Expand Down
37 changes: 19 additions & 18 deletions frontend/Pages/SendPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
TopNavigation,
useTheme,
} from '@ui-kitten/components'
import React, { useContext, useRef, useState } from 'react'
import React, { useContext, useEffect, useRef, useState } from 'react'
import { StyleSheet } from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import Icon from 'react-native-vector-icons/FontAwesome5'
Expand All @@ -25,7 +25,7 @@ import { Avatar, Button } from '../../Components'
export const SendPage: React.FC = () => {
const theme = useTheme()
const { goBack, page, database } = useContext(AppContext)
const { relayPool, publicKey } = useContext(RelayPoolContext)
const { relayPool, publicKey, lastConfirmationtId } = useContext(RelayPoolContext)
const { t } = useTranslation('common')
// state
const [content, setContent] = useState<string>('')
Expand All @@ -39,6 +39,17 @@ export const SendPage: React.FC = () => {
const breadcrump = page.split('%')
const eventId = breadcrump[breadcrump.length - 1].split('#')[1]

useEffect(() => {
if (isSending) {
showMessage({
message: t('alerts.sendNoteSuccess'),
type: 'success',
})
setIsSending(false) // restore sending status
goBack()
}
}, [lastConfirmationtId])

const styles = StyleSheet.create({
container: {
flex: 1,
Expand Down Expand Up @@ -106,23 +117,13 @@ export const SendPage: React.FC = () => {
pubkey: publicKey,
tags,
}
relayPool
?.sendEvent(event)
.then(() => {
showMessage({
message: t('alerts.sendNoteSuccess'),
type: 'success',
})
setIsSending(false) // restore sending status
goBack()
})
.catch((err) => {
showMessage({
message: t('alerts.sendNoteError'),
description: err.message,
type: 'danger',
})
relayPool?.sendEvent(event).catch((err) => {
showMessage({
message: t('alerts.sendNoteError'),
description: err.message,
type: 'danger',
})
})
})
.catch((err) => {
// error with getNotes
Expand Down
12 changes: 8 additions & 4 deletions frontend/lib/nostr/Nip04/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Buffer } from 'buffer'
import { randomBytes } from '@noble/hashes/utils'
import { generateSecureRandom } from 'react-native-securerandom'
import * as secp256k1 from '@noble/secp256k1'
// @ts-expect-error
import aes from 'browserify-cipher'

export function encrypt(privkey: string, pubkey: string, text: string): string {
export const encrypt: (privkey: string, pubkey: string, text: string) => Promise<string> = async (
privkey,
pubkey,
text,
) => {
const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)
const normalizedKey = getNormalizedX(key)
const normalizedKey = Buffer.from(key.slice(1, 33)).toString('hex')

const iv = Uint8Array.from(randomBytes(16))
const iv = await generateSecureRandom(16)
const cipher = aes.createCipheriv('aes-256-cbc', Buffer.from(normalizedKey, 'hex'), iv)
let encryptedMessage = cipher.update(text, 'utf8', 'base64')
encryptedMessage += cipher.final('base64')
Expand Down

0 comments on commit 72733ed

Please sign in to comment.