CodeSandbox: https://codesandbox.io/s/stupefied-heisenberg-fery5
HeaderMenu.js
import {Component} from 'react';
import {
Menu,
Container,
Button,
Label,
Loader,
List,
Image,
Icon,
Dropdown
} from 'semantic-ui-react';
import Head from 'next/head';
import web3 from '../ethereum/web3';
import Constant from '../support/Constant';
import Config from '../support/Config';
import appDispatcher from '../core/AppDispatcher';
import contractManager from '../core/ContractManager';
class HeaderMenu extends Component {
constructor(props) {
super(props);
this.account = props.account;
this.contractManager = contractManager;
// console.log(contractManager);
this.transactionDispatcher = props.transactionDispatcher;
this.state = {address: "", balance: "", name: "",
avatarUrl: "", isLoading: true, isJoinButtonLoading: false,
isJoined: false, numPendingTx: 0};
this.reloadCount = 0;
}
clearAllData = () => {
window.localStorage.clear();
}
componentDidMount() {
if (this.account) {
this.getAccountInfo();
appDispatcher.register((payload) => {
if (payload.action == Constant.EVENT.ACCOUNT_BALANCE_UPDATED) {
this.setState({balance: this.account.balance});
} else if (payload.action == Constant.EVENT.ACCOUNT_INFO_UPDATED) {
this.setState({name: payload.profile.name, avatarUrl: payload.profile.avatarUrl, isJoined: payload.profile.isJoined});
}
});
this.transactionDispatcher.register((payload) => {
if (payload.action == Constant.EVENT.PENDING_TRANSACTION_UPDATED) {
this.setState({numPendingTx: payload.numPendingTx});
}
});
}
}
getAccountInfo = () => {
var address = this.account.getAddress();
if (address) {
this.setState({address: address, balance: this.account.balance, isLoading: false, isJoined: this.account.isJoined});
} else {
if (this.reloadCount == 1) {
this.setState({isLoading: false});
} else {
this.reloadCount++;
setTimeout(this.getAccountInfo, 800);
}
}
}
handleDropdownClicked = (event, data) => {
if (data.name == 'updateProfile') {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_UPDATE_PROFILE
});
} else if (data.name == 'logOutItem') {
this.clearAllData();
window.location.reload();
} else if (data.name == 'settingsItem') {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_SETTINGS_MODAL
})
}
else if (data.name == 'changeEthNetwork') {
if (data.networkid != Config.ENV.EthNetworkId) {
Config.ENV.EthNetworkId = data.networkid;
this.removeNetworkDependentData();
window.location.reload();
}
}
}
removeNetworkDependentData = () => {
this.account.storageManager.removeNetworkDependentData();
}
handleJoinClicked = () => {
var publicKeyBuffer = this.account.getPublicKeyBuffer();
this.contractManager.joinContract(publicKeyBuffer, (resultEvent) => {
if (resultEvent == Constant.EVENT.ON_REJECTED || resultEvent == Constant.EVENT.ON_ERROR) {
this.setState({isJoinButtonLoading: false});
} else if (resultEvent == Constant.EVENT.ON_RECEIPT) {
window.location.reload();
}
});
this.setState({isJoinButtonLoading: true});
}
handleImportPrivateKeyClicked = () => {
appDispatcher.dispatch({
action: Constant.ACTION.OPEN_PRIVATE_KEY_MODAL
});
}
render() {
var accountInfo = (<div></div>);
if (this.account) {
if (this.state.isLoading == false) {
if (this.state.address) {
var addressExplorerUrl = Config.ENV.ExplorerUrl + 'address/' + this.state.address;
var dropdownTrigger;
if (this.state.avatarUrl) {
dropdownTrigger = (
<span><Image src={this.state.avatarUrl} avatar/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
);
} else {
dropdownTrigger = (
<span><Icon name='user' size='large'/>{ this.state.name ? this.state.name : this.state.address.substr(0,10)}</span>
);
}
var networkItems = [];
for (var i=0;i<Config.NETWORK_LIST.length;i++) {
networkItems.push(
<Dropdown.Item key={'networkItem' + i} networkid={Config.NETWORK_LIST[i].id} name='changeEthNetwork' onClick={this.handleDropdownClicked}>
{Config.NETWORK_LIST[i].name}
</Dropdown.Item>
);
}
var memberInfo;
if (this.account.isJoined) {
memberInfo = (
<Dropdown item trigger={dropdownTrigger}>
<Dropdown.Menu>
<Dropdown.Item name='updateProfile' onClick={this.handleDropdownClicked}>
<Icon name='write'/>Update profile
</Dropdown.Item>
<Dropdown.Item name='settingsItem' onClick={this.handleDropdownClicked}>
<Icon name='settings'/>Settings
</Dropdown.Item>
<Dropdown.Item name='logOutItem' onClick={this.handleDropdownClicked}>
<Icon name='log out'/>Log out
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
} else {
memberInfo = (
<Button color='orange' onClick={this.handleJoinClicked}
loading={this.state.isJoinButtonLoading}
disabled={this.state.isJoinButtonLoading}>Join {Constant.APP_NAME}</Button>
);
}
var pendingTxItem;
if (this.state.numPendingTx > 0) {
pendingTxItem = (
<Label as='a' color='yellow' href={addressExplorerUrl} target='_blank'>
<Icon name='spinner' loading/>
{this.state.numPendingTx} pending tx
</Label>
);
}
accountInfo = (
<Menu.Menu position='right'>
<Menu.Item>
<Dropdown item text={Config.ENV.NetworkName}>
<Dropdown.Menu>
{networkItems}
</Dropdown.Menu>
</Dropdown>
</Menu.Item>
<Menu.Item>
<List>
<List.Item>
<a href={addressExplorerUrl} target='_blank'>
{this.state.address}
</a>
</List.Item>
<List.Item>
Balance: <Label as='a' href={addressExplorerUrl} target='_blank' color='orange'>{parseFloat(web3.utils.fromWei("" +this.state.balance, 'ether')).toFixed(8) + ' ETH' }</Label>
{pendingTxItem}
</List.Item>
</List>
</Menu.Item>
<Menu.Item>
{memberInfo}
</Menu.Item>
</Menu.Menu>
);
} else {
accountInfo = (
<Menu.Menu position='right'>
<Menu.Item>
<Button onClick={this.handleImportPrivateKeyClicked} color='blue'>Import private key</Button>
</Menu.Item>
</Menu.Menu>
);
}
} else {
accountInfo = (<Loader inverted active />);
}
}
return (
<Menu fixed='top' color='grey' inverted>
<Head>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.12/semantic.min.css"></link>
</Head>
<Container>
<Menu.Item>
<a href='/'><Image src='static/images/blockchat.png' height={55} /></a>
</Menu.Item>
{this.account ? accountInfo: (<div></div>)}
</Container>
</Menu>
);
}
}
export default HeaderMenu;
ContractManager.js
import web3 from '../ethereum/web3';
import compiledContract from '../ethereum/build/EtherChat.json';
import TransactionsManager from './TransactionManager';
import appDispatcher from './AppDispatcher';
import Config from '../support/Config';
import Constant from '../support/Constant';
import utils from '../support/Utils';
import crypto from 'crypto';
/**
* Responsible for interacting with the Ethereum smart contract
*/
export class ContractManager {
constructor(accountManager, storageManager) {
this.getContract();
this.accountManager = accountManager;
this.storageManager = storageManager;
this.transactionManager = new TransactionsManager(accountManager);
}
// Create a web3 contract object that represent the ethereum smart contract
getContract = async () => {
this.contract = await new web3.eth.Contract(JSON.parse(compiledContract.interface),
Config.ENV.ContractAddress);
appDispatcher.dispatch({
action: Constant.EVENT.CONTRACT_READY
})
}
// Get current account profile from EtherChat contract's storage
getProfile = async (address) => {
var result = await this.contract.methods.members(this.accountManager.getAddress()).call();
var profile = {};
if (result.isMember == 1) {
profile.isJoined = true;
profile.avatarUrl = utils.hexStringToAsciiString(result.avatarUrl);
profile.name = utils.hexStringToAsciiString(result.name);
this.storageManager.setJoinedStatus(true);
this.storageManager.setName(this.name);
this.storageManager.setAvatarUrl(this.avatarUrl);
appDispatcher.dispatch({
action: Constant.EVENT.ACCOUNT_INFO_UPDATED,
profile: profile
})
}
return profile;
}
getMemberInfo = async (address, relationship) => {
var memberInfo = await this.contract.methods.members(address).call();
if (memberInfo.isMember) {
var publicKey = '04' + memberInfo.publicKeyLeft.substr(2) + memberInfo.publicKeyRight.substr(2);
var name = utils.hexStringToAsciiString(memberInfo.name);
var avatarUrl = utils.hexStringToAsciiString(memberInfo.avatarUrl);
this.storageManager.updateContact(address, publicKey, name, avatarUrl, relationship);
}
}
getPastEvents = async (eventName, filters) => {
return await this.contract.getPastEvents(eventName, filters);
}
joinContract = async(publicKeyBuffer, callback) => {
var publicKeyLeft = '0x' + publicKeyBuffer.toString('hex', 0, 32);
var publicKeyRight = '0x' + publicKeyBuffer.toString('hex', 32, 64);
this.transactionManager.executeMethod(this.contract.methods.join(publicKeyLeft, publicKeyRight))
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_REJECTED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_REJECTED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
// joinContract = async (publicKeyBuffer, callback) => {
addContact = async (address, callback) => {
console.log(address);
var method = this.contract.methods.addContact(address);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
acceptContactRequest = async (address, callback) => {
var method = this.contract.methods.acceptContactRequest(address);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
updateProfile = async (name, avatarUrl, callback) => {
var nameHex = '0x' + Buffer.from(name, 'ascii').toString('hex');
var avatarUrlHex = '0x' + Buffer.from(avatarUrl, 'ascii').toString('hex');
var method = this.contract.methods.updateProfile(nameHex, avatarUrlHex);
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
if (callback) callback(Constant.EVENT.ON_APPROVED);
})
.on(Constant.EVENT.ON_RECEIPT, (receipt) => {
if (callback) callback(Constant.EVENT.ON_RECEIPT);
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
appDispatcher.dispatch({
action: Constant.EVENT.ENCOUNTERED_ERROR,
message: error.message,
title: "Error"
});
if (callback) callback(Constant.EVENT.ON_ERROR);
});
}
// A message will be encrypted locally before sending to the smart contract
sendMessage = async (toAddress, publicKey, message) => {
var publicKeyBuffer = Buffer.from(publicKey, 'hex');
var encryptedRaw = utils.encrypt(message, this.accountManager.computeSecret(publicKeyBuffer));
var encryptedMessage = '0x' + encryptedRaw.toString('hex');
var method = this.contract.methods.sendMessage(toAddress, encryptedMessage, utils.getEncryptAlgorithmInHex());
this.transactionManager.executeMethod(method)
.on(Constant.EVENT.ON_APPROVED, (txHash) => {
this.storageManager.addMyLocalMessage(encryptedMessage, toAddress, utils.getEncryptAlgorithm(), txHash);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
})
.on(Constant.EVENT.ON_REJECTED, (data) => {
// do nothing
})
.on(Constant.EVENT.ON_RECEIPT, (receipt, ) => {
this.storageManager.updateLocalMessage(toAddress, receipt.transactionHash, Constant.SENT_STATUS.SUCCESS);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
})
.on(Constant.EVENT.ON_ERROR, (error, txHash) => {
this.storageManager.updateLocalMessage(toAddress, txHash, Constant.SENT_STATUS.FAILED);
appDispatcher.dispatch({
action: Constant.EVENT.MESSAGES_UPDATED,
data: toAddress
});
});
}
}
export default ContractManager;
enter image description here
Here, I'm trying to call the function 'joinContract' {the function of ContractManager class} in HeaderMenu.js using the function handleJoinClicked().
And,boom... code is getting crash after clicking the join button. It's showing the error. This[coontractManager.joinContract] is not a function.
Please help.
Related
I have a problem with the following exportHtml function, this must export the html content of a react component but I can't understand why it doesn't enter the function to extrapolate html, how can I solve it?
Editor.js
import EmailEditor.js
const Example = () => {
const emailEditorRef = useRef(null);
const [titolo, setTitolo] = useState('');
const [getHtml, setHtml] = useState('');
const [newsProfileValue, setNewsProfileSet] = useState('');
const [profileList, setNewsProfileList] = useState([]);
const [premiumValue, setPremium] = useState(false);
const [publicValue, setPublic] = useState(false);
const arr = [];
const newsCollectionRef = collection(db, 'news');
const saveDesign = () => {
emailEditorRef.current.editor.saveDesign((design) => {
exportData(design);
});
};
const getList = async () => {
try {
var id = await localStorage.getItem('uid');
console.log(id);
var data = [];
const querySnapshot = await getDocs(collection(db, 'news_profiles'));
querySnapshot.forEach((doc) => {
if (doc.data().about.uid == id) {
data.push({ title: doc.data().about.title, id: doc.id });
arr.push({ doc: doc.data().about.title, id: doc.id });
}
});
} catch (error) {
console.log('Error: ' + error);
}
var options = arr.map((e) => {
return `<option value="${e.id}">${e.doc}</option>`;
});
document.getElementById('selectNumber').innerHTML = options;
setNewsProfileList(data);
};
const exportData = (design) => {
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
JSON.stringify(design)
)}`;
const link = document.createElement('a');
link.href = jsonString;
link.download = 'data.json';
link.click();
};
const exportHtml = () => {
console.log('Sono in html');
emailEditorRef.current.editor.exportHtml((data) => {
const { design, html } = data;
setHtml(html);
});
addElementToFireStoreNews(getHtml);
};
const addElementToFireStoreNews = async (html) => {
try {
console.log('Sono in addElementToFireStoreNews ');
var uuid = await localStorage.getItem('uid');
var jsonFile;
var templateid = await localStorage.getItem('templateid');
if (templateid == 1) {
jsonFile = sample;
} else if (templateid == 2) {
jsonFile = sample3;
} else {
jsonFile = sample2;
}
console.log('Setup options');
const options = {
method: 'POST',
url: 'http://link',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer kjsdgu6523kjbnap!w',
},
data: {
internal: true,
online: publicValue,
premium: premiumValue,
html: html,
template: jsonFile,
subject: html,
email: uuid,
},
};
console.log('Chiamo il rest');
axios
.request(options)
.then(function (response) {
console.log('data: ' + response.data);
})
.catch(function (error) {
console.error('Errore axios: ' + error);
});
} catch (err) {
console.log('Errore inserimnento: ' + err);
}
};
const onDesignLoad = (data) => {
getList();
console.log('onDesignLoad', data);
};
const onLoad = async () => {
var templateid = await localStorage.getItem('templateid');
if (templateid == 1) {
emailEditorRef.current.editor.loadDesign(sample);
} else if (templateid == 2) {
emailEditorRef.current.editor.loadDesign(sample3);
} else {
emailEditorRef.current.editor.loadDesign(sample2);
}
emailEditorRef.current.editor.addEventListener(
'design:loaded',
onDesignLoad
);
};
const onReady = () => {};
const changeText = (text) => {
setTitolo(text);
};
const clickValue = (e) => {
setNewsProfileSet(e.target.value);
};
const changeValue = () => {
setPremium(!premium);
};
const changeValuePublic = () => {
setPublic(!publicValue);
};
return (
<Container>
<Bar>
<img src="firebase.png" width="100" height="50" />
<label style={{ color: 'white' }}>
<b>Titolo News: </b>
<input
type="text"
name="name"
onChange={(value) => changeText(value.target.value)}
style={{ marginRight: 200 }}
/>
Premium:{' '}
<Checkbox
{...label}
unchecked
onClick={() => changeValue()}
style={{ color: 'white' }}
/>
Online:{' '}
<Checkbox
{...label}
unchecked
onClick={() => changeValuePublic()}
style={{ color: 'white' }}
/>
</label>
<form id="myForm">
<select id="selectNumber" onClick={clickValue}>
<option>Scegli il news profile</option>
</select>
<button onClick={() => saveDesign}> Salva Design </button>
<button onClick={() => exportHtml()}> Salva Articolo </button>{' '}
</form>
</Bar>
<React.StrictMode>
<EmailEditor ref={emailEditorRef} onLoad={onLoad} onReady={onReady} />{' '}
</React.StrictMode>{' '}
</Container>
);
};
export default Example;
EmailEditor.js
import React, { Component } from 'react';
import { loadScript } from './loadScript';
import pkg from '../package.json';
let lastEditorId = 0;
export default class extends Component {
constructor(props) {
super(props);
this.editorId = props.editorId || `editor-${++lastEditorId}`;
}
componentDidMount() {
loadScript(this.loadEditor, this.props.scriptUrl);
}
render() {
let {
props: { minHeight = 500, style = {} },
} = this;
return (
<div
style={{
flex: 1,
display: 'flex',
minHeight: minHeight,
}}
>
<div id={this.editorId} style={{ ...style, flex: 1 }} />
</div>
);
}
loadEditor = () => {
if (this.loaded) return;
this.loaded = true;
const options = this.props.options || {};
if (this.props.projectId) {
options.projectId = this.props.projectId;
}
if (this.props.tools) {
options.tools = this.props.tools;
}
if (this.props.appearance) {
options.appearance = this.props.appearance;
}
if (this.props.locale) {
options.locale = this.props.locale;
}
this.editor = unlayer.createEditor({
...options,
id: this.editorId,
displayMode: 'email',
source: {
name: pkg.name,
version: pkg.version,
},
});
for (const [key, value] of Object.entries(this.props)) {
if (/^on/.test(key) && key !== 'onLoad' && key !== 'onReady') {
this.addEventListener(key, value);
}
}
const { onLoad, onReady } = this.props;
// #deprecated
onLoad && onLoad();
if (onReady) this.editor.addEventListener('editor:ready', onReady);
};
registerCallback = (type, callback) => {
this.editor.registerCallback(type, callback);
};
addEventListener = (type, callback) => {
this.editor.addEventListener(type, callback);
};
loadDesign = (design) => {
this.editor.loadDesign(design);
};
saveDesign = (callback) => {
this.editor.saveDesign(callback);
};
exportHtml = (callback) => {
this.editor.exportHtml(callback);
};
setMergeTags = (mergeTags) => {
this.editor.setMergeTags(mergeTags);
};
}
Im tryin to get my queries to update using the subscribeToMore function. Im getting lost on why the code is a failing to execute. so far all operations query, mutation and subscribe will work independently.
FRONTEND
export const QUERY_MESSAGES = gql`
query Messages($convoId: ID!) {
messages(convoId: $convoId) {
text
senderId
convoId
createdAt
}
}
`;
export const SUBSCRIBE_MESSAGES = gql`
subscription Messages($convoId: ID!) {
messages(convoID: $convoID) {
text
convoId
}
}
`;
const ActiveChat = ({ currentConvo, me }: any) => {
const { subscribeToMore, data } = useQuery(QUERY_MESSAGES, {
variables: {
convoId: currentConvo,
},
});
return (
<section className="chat-wrapper">
<div className="messages-container">
<Messages
messages={data.messages}
me={me}
subscribeToMessages={() => {
subscribeToMore({
document: SUBSCRIBE_MESSAGES,
variables: { convoId: currentConvo },
updateQuery: (prev, { subscriptionData }) => {
console.log("hit");
if (!subscriptionData.data) return prev;
const newFeedItem = subscriptionData.data.messages;
return Object.assign({}, prev, {
messages: [newFeedItem, ...prev.messages],
});
},
});
}}
/>
<Input currentConvo={currentConvo} />
</div>
</section>
);
};
const Messages = ({ messages, me, subscribeToMessages }: any) => {
useEffect(() => {
subscribeToMessages();
console.log("use effect ran");
});
const formatTime = (time: number) =>
new Date(time * 1000).toLocaleTimeString();
console.log(messages);
return (
messages && (
<div>
{messages.map((message: any, i: number) => {
return message.senderId === me?._id ? (
<SenderBubble
key={i}
text={message.text}
time={message.createdAt}
formatTime={formatTime}
/>
) : (
<OtherUserBubble
key={i}
text={message.text}
time={message.createdAt}
formatTime={formatTime}
/>
);
})}
</div>
)
);
};
BACKEND
const pubsub = new PubSub();
Query: {
messages: async (parent, { convoId }, context) => {
if (context.user) {
return Message.find({ convoId }).sort({ createdAt: "asc" });
}
},
},
Mutation: {
/// creates a new message using sender id and emits MESSAGE_SENT event
sendMessage: async (parent, { text, convoId }, context) => {
pubsub.publish("MESSAGE_SENT", {
message: { text, convoId },
});
return Message.create({ text, convoId, senderId: context.user._id });
},
},
Subscription: {
userActive: {
subscribe: () => pubsub.asyncIterator("ACTIVE_USERS"),
},
message: {
subscribe: withFilter(
() => pubsub.asyncIterator("MESSAGE_SENT"),
(payload, variables) => {
return payload.message.convoId === variables.convoId;
}
),
},
},
tdlr: I think subscribeToMore.updateQuery isn't firing but i don't how to debug
SOLVED: turns out I placed the wrong url and port number while building the client so the ws wasn't connecting.
I have updated my store by using mapDispatchToProps like so:
function mapDispatchToProps(dispatch){
return{
addFirstImageUrl: (firstUrl) => dispatch({
type: "ADD_FIRST_IMAGE_URL",
firstUrl
})
}
}
I know that this works because when I run mapStateToProps I log the state like so:
function mapStateToProps(state){
console.log(state)
return{
firstImageUrl: state.firstImageUrl
}
}
This returns:
}
Object {
"posts": Object {
"firstImageTitle": "",
"firstImageUrl": "https://firebasestorage.MYURL",
"secondImageTitle": "",
"secondImageUrl": "",
},
}
however when I call this.props.firstImageUrl it returns undefined. I feel like this should return the url above, is this thinking wrong?
component function:
uploadImage = async (uri) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(new Date().toString());
const snapshot = await ref.put(blob)
const url = await snapshot.ref.getDownloadURL();
this.props.addFirstImageUrl(url);
console.log("First Image Url: " + this.props.firstImageUrl)
};
import React from "react";
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from "react-native";
import { Camera } from "expo-camera";
import * as Permissions from "expo-permissions";
import { FontAwesome } from "#expo/vector-icons";
import * as firebase from "firebase";
import { connect } from "react-redux";
import { addFirstImageUrl } from "../store/actions/posts";
import { bindActionCreators } from "redux";
function mapStateToProps(state){
console.log(state)
return{
firstImageUrl: state.firstImageUrl
}
}
function mapDispatchToProps(dispatch){
return{
addFirstImageUrl: (firstUrl) => dispatch({
type: "ADD_FIRST_IMAGE_URL",
firstUrl
})
}
}
class FirstCameraScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
hasCameraPermissions: null,
type: Camera.Constants.Type.back,
isFlashLIghtOn: Camera.Constants.FlashMode.off,
firstImageUrl: " "
};
}
static navigationOptions = {
title: "FirstCamera"
};
//ask for camera permission
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({
hasCameraPermissions: status === "granted"
});
}
// Flip the camera
flipCamera = () => {
this.setState({
type:
this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
});
};
//Toggle Flashlight
flashLight = () => {
this.setState({
isFlashLIghtOn:
this.state.isFlashLIghtOn === Camera.Constants.FlashMode.off
? Camera.Constants.FlashMode.on
: Camera.Constants.FlashMode.off
});
};
//Take Picture and send to home
takePicture = async () => {
if (this.camera) {
let photo = await this.camera.takePictureAsync();
if (!photo.cancelled) {
await this.uploadImage(photo.uri);
}
this.props.navigation.navigate("FirstNamingScreen");
}
};
uploadImage = async (uri) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(new Date().toString());
const snapshot = await ref.put(blob)
const url = await snapshot.ref.getDownloadURL();
this.props.addFirstImageUrl(url);
console.log("First Image Url: " + props.posts)
};
render() {
const { hasCameraPermissions } = this.state;
const { navigate } = this.props.navigation;
if (hasCameraPermissions === null) {
return <View />;
} else if (hasCameraPermissions === false) {
return (
<View>
<Text>No access to Camera</Text>
</View>
);
} else if (hasCameraPermissions === true) {
return (
<View style={styles.container}>
<Camera
style={styles.cameraView}
type={this.state.type}
flashMode={this.state.isFlashLIghtOn}
ref={ref => {
this.camera = ref;
}}
>
<View style={styles.actionContainer}>
<TouchableOpacity
onPress={() => this.flipCamera()}
style={styles.iconHolder}
>
<FontAwesome name="camera" size={35} style={styles.icon} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.takePicture();
}}
style={styles.iconHolder}
>
<FontAwesome name="circle" size={35} style={styles.icon} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.flashLight()}
style={styles.iconHolder}
>
<FontAwesome name="flash" size={35} style={styles.icon} />
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(FirstCameraScreen)
Change the mapStateToProps to this.
function mapStateToProps(state) {
console.log(state);
return {
firstImageUrl: state.posts.firstImageUrl
};
}
And your uploadImage method.
uploadImage() {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(new Date().toString());
const snapshot = await ref.put(blob);
const url = await snapshot.ref.getDownloadURL();
this.props.addFirstImageUrl(url);
console.log("First Image Url: " + this.props.firstImageUrl);
};
constructor
constructor(props) {
super(props);
this.uploadImage = this.uploadImage.bind(this);
this.state = {
hasCameraPermissions: null,
type: Camera.Constants.Type.back,
isFlashLIghtOn: Camera.Constants.FlashMode.off,
firstImageUrl: " "
};
}
I'm developing a React native application. I'm using the React native elements library.
I'm using the Search bar. But when I type faster than the keyboard, the search is not working properly.
Example;
I'm writing "Jack," but it's called "Ja".
I hope I can explain my problem. Because my English is not very good. Thanks in advance for your help.
handleRefresh = () => {
this.setState({
offset: 0,
maxSize: 10,
isSearch: false
}, () => {
this.loadData();
});
};
handleLoadMore = () => {
this.setState({
maxSize: this.state.maxSize + 10
}, () => {
this.loadData();
});
};
loadData = async () => {
try {
const { username, token, offset, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = `https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&offset=${offset}&maxSize=${maxSize}`;
await axios.get(URL, {headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSuccess.bind(this))
.catch(this.dataFail.bind(this));
}catch (error) {
Alert.alert(
'Hata',
'Bir hata meydana geldi. Lütfen yöneticiye başvurunuz.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
};
searchLead = async (text) => {
try {
if(text) {
this.setState({ searchText: text, isSearch: true, isLoading: true });
const { username, token, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = "https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&where[0][type]=textFilter&where[0][value]=" + text;
await axios.get(URL, { headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSearch.bind(this))
.catch(this.dataFail.bind(this));
}else {
this.setState({ searchText: '' });
this.handleRefresh();
}
}catch (error) {
Alert.alert(
'Hata',
'Arama başarısız oldu. Lütfen yöneticiniz ile görüşün.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
}
dataSuccess(response) {
this.setState({ isLoading: false, leadList: response.data.list });
}
dataSearch(response) {
this.setState({ isLoading: false, searchData: response.data.list });
}
dataFail(error) {
this.setState({ isLoading: false });
Alert.alert(
'Hata',
'Beklenmedik bir hata oluştu',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
render() {
const { isLoading, isRefreshing, searchText, isSearch, leadList, searchData } = this.state;
return(
<View style={styles.container}>
<SearchBar
placeholder="Bir lead arayın..."
searchIcon={<Icon
name="search"
color="white"
size={21}
/>}
onChangeText={this.searchLead.bind(this)}
onClear={this.handleRefresh.bind(this)}
onCancel={this.handleRefresh.bind(this)}
value={searchText}
/>
{ isLoading ? <ActivityIndicator style={styles.loading} size="large" color="orange" /> :
isSearch ?
<ScrollView>
<FlatList
data={searchData}
showLoading={true}
renderItem={({item}) =>
<ListItem
title={item.name}
subtitle={item.status}
bottomDivider={true}
/>
}
keyExtractor={this.keyExtractor}
refreshing={isRefreshing}
onRefresh={this.handleRefresh}
/>
</ScrollView> :
<FlatList
data={leadList}
renderItem={({item}) =>
<ListItem
title={item.name}
subtitle={item.status}
bottomDivider={true}
/>
}
keyExtractor={this.keyExtractor}
refreshing={isRefreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.2}
/>
}
</View>
)
}
}
Let me start by saying I don't know reactjs very well, but I think this may work (there may be far better solutions though)
searchLead = (() => {
let req;
const delayedRequest = callback => {
const timeout = setTimeout(callback, 500);
return {
cancel() {
clearTimeout(timeout);
}
};
};
return (text) => {
req && req.cancel();
req = delayedRequest(async () => {
try {
if(text) {
this.setState({ searchText: text, isSearch: true, isLoading: true });
const { username, token, maxSize } = this.state;
var credentials = Base64.btoa(username + ':' + token);
var URL = "https://crm.example.com/api/v1/Lead?select=name,status&sortBy=createdAt&asc=false&where[0][type]=textFilter&where[0][value]=" + text;
await axios.get(URL, { headers : { 'Espo-Authorization' : credentials }})
.then(this.dataSearch.bind(this))
.catch(this.dataFail.bind(this));
} else {
this.setState({ searchText: '' });
this.handleRefresh();
}
}catch (error) {
Alert.alert(
'Hata',
'Arama başarısız oldu. Lütfen yöneticiniz ile görüşün.',
[
{ text: 'Tamam', onPress: () => null }
]
);
}
});
};
})();
I am experiencing a very weird react behavior. There is this component which gets message and emailSubmitedText. In render method based on some condition it should render either first or the other one.
Now at first it is message. I click on the submit of the form. All the functions happen.
The component rerenders. In the console log I can see this time it should render emailSubmitedText. In react devtools it show the right text.
However in the actual html and html inspector it still shows the previos text.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Details from './Details'
class DefaultMessage extends Component {
inputRef = null
renderEmailForm = () => {
return (
<form
className='b2c_email-form input-field'
onSubmit={e => {
e.preventDefault()
const { projectId, visitSessionId } = this.props
this.setState({ email: this.inputRef.value })
this.props.onSubmitEmail({
email: this.inputRef.value,
convertedPage: window.location.href, projectId, visitSessionId
})
}}
>
<div className="input-field">
<input ref={elem => this.inputRef = elem} id='email' type='email' className='validate' value={this.props.email} />
<label htmlFor='email' data-error='Invalid email address'>E-mail</label>
</div>
<button
className='b2c_email-form-button waves-effect waves-light btn'
type='submit'
style={{
backgroundColor: this.props.companyColor || '#63bc78'
}}
>Submit</button>
</form>
)
}
render = () => {
console.log('...> ', this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText)
return (
<div className='b2c_chat-message'>
<Details
classNames='b2c_chat-message-details__admin'
avatar={this.props.avatar}
name={this.props.name}
date={this.props.date}
/>
<div className='b2c_chat-message-text b2c_chat-message-text__admin b2c_chat-message-default'>
<div className='b2c_chat-message-after b2c_chat-message-after__admin' />
{this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText}
{!this.props.contactId && this.renderEmailForm()}
</div>
</div>
)
}
}
DefaultMessage.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default DefaultMessage
Here is the direct parent of the component.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import $ from 'jquery'
import moment from 'moment'
import randomstring from 'randomstring'
import DefaultMessage from './DefaultMessage'
import Message from './Message'
import UserTypingIndicator from '../UserTypingIndicator'
import TypingIndicator from './TypingIndicator'
class Messages extends Component {
chatRef = null
componentDidUpdate () {
this.scrollToTheLastMessage()
}
scrollToTheLastMessage = () => {
const $chat = $(this.chatRef)
const scrollTop = $chat.prop('scrollHeight') - $chat.innerHeight()
$chat.scrollTop(scrollTop)
}
renderDefaultMessage = () => (
<DefaultMessage
contactId={this.props.contactId}
companyColor={this.props.companyColor}
error={this.props.error}
date={moment().format('h:mm A')}
name={this.props.adminName}
avatar={this.props.adminAvatar}
message={this.props.welcomeMessage}
emailSubmitedText={this.props.emailSubmitedText}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
onSubmitEmail={this.props.onSubmitEmail}
/>
)
renderMessages = () => {
let checkConversationDate = null
const {messages, contactName, adminName, adminAvatar} = this.props
const compareConversationDates = (createdAt) => {
checkConversationDate = moment(createdAt).format("DD.MM.YYYY")
return (
<div key={randomstring.generate()} className='conversationDayDate'>
<span>{checkConversationDate}</span>
</div>
)
}
if (!messages) return null
return messages.map((message, index) => {
return (
<div>
{checkConversationDate !== moment(message.createdAt.$date).format("DD.MM.YYYY") ? compareConversationDates(message.createdAt.$date) : ''}
{/* {index === 0 ? this.renderDefaultMessage() : ''} */}
<Message
isAdmin={message.userId ? true : false}
imageFile={message.imageFile}
key={randomstring.generate()}
companyColor={this.props.companyColor}
contactName={contactName}
adminName={adminName}
avatar={adminAvatar}
message={message.message}
date={moment(message.createdAt.$date).format('h:mm A')}
/>
</div>
)
})
}
renderTypingIndicators = () => {
const arrayToRender = [
this.props.isAdminTyping && <AdminTypingIndicator />,
this.props.isUserTyping && <UserTypingIndicator />
]
return arrayToRender
}
render = () => <div ref={elem => this.chatRef = elem} id='chat' className='chat-body' style={{
height: 'calc(100% - 190px - 3rem)',
overflowY: 'scroll',
margin: '30px 10px 10px 0',
boxSizing: 'border-box'
}}>
{this.renderDefaultMessage()}
{this.renderMessages()}
{this.renderTypingIndicators()}
</div>
}
Messages.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
messages: PropTypes.array.isRequired,
adminName: PropTypes.string.isRequired,
contactName: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default Messages
And here is where Container with states
import React, { Component } from 'react'
import Sound from 'react-sound'
import ddp from '../../ddp'
import Cookies from 'js-cookie'
import randomstring from 'randomstring'
import ChatContainer from './ChatContainer'
import Icon from './Icon'
import { connect, makeArrayCollectionFromObjectCollection, getNewMessages } from '../../functions'
class View extends Component {
defaultDocumentTitle = null
state = {
contactId: '',
chat: null,
show: false,
newMessagesCount: null,
notStatus: 'STOPPED'
}
newMessageNotification = newMessages => {
if (newMessages.length && newMessages.length > this.state.newMessagesCount) {
this.setState({ notStatus: 'PLAYING' })
document.title = `(${newMessages.length}) ${this.defaultDocumentTitle}`
} else if (!newMessages.length) {
document.title = this.defaultDocumentTitle
}
if (this.state.newMessagesCount !== newMessages.length) {
this.setState({ newMessagesCount: newMessages.length })
}
}
componentWillMount () {
this.defaultDocumentTitle = document.title
}
componentDidMount = async () => {
this.setContactIdFromCookies()
await connect(ddp)
}
setContactIdFromCookies = () => {
window.Cookies = Cookies
console.warn('setContactIdFromCookies')
const contactId = Cookies.get('b2cContactId')
console.log('contactId', contactId)
if (contactId) this.setState({contactId})
}
componentDidUpdate () {
console.warn('componentDidUpdate', this.props)
if (this.state.contactId && !this.state.chat) {
this.getChat(this.state.contactId)
}
if (this.state.chat && this.state.chat.length) {
let newMessages = getNewMessages(this.state.chat)
this.newMessageNotification(newMessages)
}
}
componentWillReceiveProps = (nextProps) => {
console.warn('componentWillReceiveProps', nextProps)
if (!nextProps.contactId) return
if (this.state.chat == null) this.getChat(nextProps.contactId)
}
getChat = async (contactId) => {
console.log('getChat', contactId)
await ddp.subscribe('Messages', {contactId})
const messagesColl = ddp.getCollection('Messages')
console.log('messagesColl', messagesColl)
this.setState({chat: this.getMessages(messagesColl)})
ddp.watch('Messages', (changedDoc, message) => {
console.log('Messages collection item changed', changedDoc, message)
const messagesColl = ddp.getCollection('Messages')
this.setState({chat: this.getMessages(messagesColl)})
})
}
getMessages = collection => {
let messages = []
if (collection) {
messages = makeArrayCollectionFromObjectCollection(collection)
}
console.log('messages', messages)
return messages
}
submitEmail = ({ email, convertedPage, projectId, visitSessionId }) => ddp.call('chat.init', { email, convertedPage, projectId, visitSessionId })
.then(contactId => {
Cookies.set('b2cContactId', contactId, { expires: 90 })
this.setState({ contactId, error: '' })
})
.catch(error => {
console.error('Error >', error)
})
readMessages = () => ddp.call('readMessages', {contactId: this.state.contactId, userId: !null})
.then(res => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
submitMessage = ({message, visitSessionId, imageFile}) => ddp.call('chat.submitContactMessage', { message, visitSessionId, contactId: this.state.contactId, projectId: this.props.projectId, imageFile: imageFile || null})
.then((res) => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
this.setState({error})
})
toggleChat = () => this.setState((state) => ({show: !state.show}))
sendFileToServer = (base64File, resolve, reject) => {
ddp.call('uploadToDropbox', base64File)
.then((res) => {
this.submitMessage({message: '', visitSessionId: this.props.visitSessionId, imageFile: res})
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
}
getBase64 = (file, resolve, reject) => {
const self = this
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function () {
self.sendFileToServer(reader.result, resolve, reject)
}
reader.onerror = function (error) {
console.error('FileReader Error: ', error)
}
}
onFileDrop = files => {
let self = this
files.forEach(file => {
return new Promise((resolve, reject) => {
self.getBase64(file, resolve, reject)
})
})
}
render () {
return (
<div>
<ChatContainer
onFileDrop={this.onFileDrop}
contactId={this.state.contactId}
show={this.state.show}
error={this.state.error && <span style={{color: 'red'}}>{this.state.error}</span>}
chatSettings={this.props.chatSettings}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
defaultAdminUserName='default defaultAdminUserName'
contactName='You'
supportName='Our Support'
messages={this.state.chat}
onSend={this.submitMessage}
onSubmitEmail={this.submitEmail}
toggleChat={this.toggleChat}
readMessages={this.readMessages}
/>
<Icon
companyColor={this.props.chatSettings.companyColor}
onClick={this.toggleChat}
newMessagesCount={this.state.newMessagesCount}
/>
<Sound
url='https://www.incredo.co/hubfs/b2c/Not%201.wav'
playStatus={this.state.notStatus}
playFromPosition={0}
onFinishedPlaying={() => this.setState({ notStatus: 'STOPPED' })}
/>
</div>
)
}
}
export default View