React-native - Upload image from Camera to Apollo-Server using Apollo-client - reactjs

I am capturing image from my camera and I am saving it in variable:
const [image,setImage] = useState(null)
.................
let photo = await cameraRef.takePictureAsync();
setImage(photo);
This sets image file like and it looks-like:
file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540borislavvr%252Fnative-voe/Camera/78b65450-378e-4e8f-85f5-3bfe38e9f8bd.jpg
As this is stored in my image constant I am trying now to send this image to my Apollo Server.
I have my fileType.ts:
const { gql } = require("apollo-server-express");
const otherType = gql`
scalar Upload
type File {
url: String
}
`;
export default otherType;
So this image which I am going to upload is part of type called Case.
When I am creating case it looks-like:
extend type Mutation {
createCase(input: CreateCase!, attachments: Upload): Case
}
In my React-Native App I perform this mutation like this:
const SEND_CASE = gql`
mutation CreateCase($input: CreateCase!, $attachments: Upload) {
createCase(input: $input, attachments: $attachments) {
_id
}
}
`;
..............
const [
createCase,
{ data: caseData, loading: caseLoading, error: caseError },
] = useMutation(SEND_CASE);
..........
const data = new FormData();
data.append("file", {
name: "problem.jpg",
uri: image,
type: "image.jpg",
});
createCase({
variables: {
input: {
description: description,
date: new Date().toDateString(),
priority: isChecked,
userId: userData.getSingleUserByUsername[0]._id,
categoryId: categories,
},
attachments: data,
},
});
}}
I know I am passing probably the file wrong but what I console log in my back-end is:
Mutation: {
createCase: async (
parentValue: any,
{ input, attachments }: { input: CaseInterface; attachments: any },
context: any
) => {
console.log(attachments);
},
},
which is giving me:
{ _parts: [ [ 'file', [Object] ] ] }
and if I console.log(attachments._parts):
[
[
'file',
{
name: 'problem.jpg',
uri: 'file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540borislavvr%252Fnative-voe/Camera/2c87803d-ba3f-43cc-b674-22dfafe04d58.jpg',
type: 'image.jpg'
}
]
]
So my question is "Is it possible to upload this data and visualize image"? When I do the same with React and use input button to upload file I am able to have:
const { createReadStream, filename, mimetype, endcoding } = await args
.file
from file so I have this createReadStream. How can I do the same for picture taken from react-native camera?

Related

Pushing data to an array in already existing object with axios

i have a object which looks like this:
{
"title": "675756",
"release_date": "2022-01-16",
"series": "Better Call Saul",
"img": "https://upload.wikimedia.org/wikipedia/en/0/03/Walter_White_S5B.png",
"characters": [],
"id": 1
}
to an characters array i want to add the id of characters.
I do it by form and then i handle submit like this:
const handleSubmit = (values) => {
console.log("dodano aktora do filmu!");
console.log(values);
addActorToMovie(values);
history.goBack();
};
the addActorToMovie action:
export const addActorToMovie = (resp) => ({
type: types.ADD_CHAR_TO_MOVIE,
payload: resp,
});
and the reducer:
case types.ADD_CHAR_TO_MOVIE:
console.log(action.payload);
return {
...state,
...state.episodes.map(function (item) {
return item.id === action.payload.episodeId
? {
id: item.id,
title: item.title,
release_date: item.release_date,
series: item.series,
img: item.img,
characters: [...item.characters, action.payload.actor],
}
: { ...item };
}),
};
It all works, but the problem is that i dont want to do it loccaly. Im using an database with json-server, and I want to do an Axios Request so that it would add a data to the database.
And i don't know how to do this, when i use axios.post it adds an object to my episodes array, if im using axios.put it changes an object. Is there any possibility to push the data to an array as i do it with the code above, but with axios so that it would be added to database?
My approach looked like this:
export const addActorToMovieAxios = (value) => {
console.log(value);
return async (dispatch) => {
try {
const response = await axios.post(
`http://localhost:3000/episodes/`,
value
);
console.log(response);
dispatch(addActorToMovie(response.data));
} catch (ex) {
console.log(ex);
}
};
};
but as I said this does add a new object to an array.....
"episodes": [
{
"title": "675756",
"release_date": "2022-01-16",
"series": "Better Call Saul",
"img": "https://upload.wikimedia.org/wikipedia/en/0/03/Walter_White_S5B.png",
"characters": [],
"id": 1
},
{
"episodeId": 1,
"actor": "1",
"id": 2
}
]
So just to be clear I understand your question, you have an object that already exists in your DB, and you want to push something onto the 'characters' array in that existing object, without creating a new object, correct?
To do this, I would use Mongo for your DB and define two Mongoose Schemas, one for the existing object (let's call it TVShow) and one for the Characters within that object. Your two Schemas will look like this:
TVShowModel.js:
const mongoose = require('mongoose');
const CharacterModel = require('./CharacterModel')
const TVShowScheme = new mongoose.Schema({
title: {
type: String,
},
release_date: {
type: Date,
},
series: {
type: String,
},
img: {
type: String,
},
characters:[
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Student'
},
],
examQuestions: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'CharacterModel'
}
]
})
module.exports = mongoose.model('TVShowModel', TVShowScheme )
CharacterModel.js:
const mongoose = require('mongoose');
const CharacterModel= new mongoose.Schema({
characterName: {
type: String,
},
actorName: {
type: String,
},
}) // add any other fields you want here
module.exports = mongoose.model('CharacterModel', CharactModelScheme )
Then, create your Axios post request. Make sure you send when you send the 'value' variable to your server, it contains the id (or perhaps the unique title) of the object you'll be 'pushing' to. Push won't work in axios/react, so we'll use the 'spread' opperator instead.
Your router will look like this:
const CharacterModel= require ('../models/CharacterModel');
const TVShowModel= require ('../models/TVShowModel');
const router = express.Router();
router.post('/episodes', async function(req,res){
try{
const tvshow = await TVShowModel.find({title: req.body.title})
// edit as needed
console.log("FOUND TV Show: "+tvshow )
const characterName= req.body.characterName
const actorName = req.body.actorName
const newCharacter = new CharacterModel({
characterName,
actorName,
})
console.log("new character created: "+newCharacter)
tvshow[0].CharacterModel = [...tvshow[0].CharacterModel,newCharacter];
await tvshow[0].save()
.then(()=>res.json('New Character Added to DB'))
.catch(err=>res.status(400).json('Error: ' + err))
} catch(e){
console.log(e)
}
})
Hope this was clear!

React Apollo Client - query results mixing up in cache

I use apollo client to fetch book graphs and use relay style pagination. Both of the following NEW_BOOKS query and ALL_BOOKS query works fine independently.
Currently, I am using NEW_BOOKS in the home page and ALL_BOOKS in a popup in the home page.
When the Homepage is opened NEW_BOOKS gets loaded fine.
When the popup is opened and ALL_BOOKS is fetched, newBooks become undefined or the result of ALL_BOOKS query.
Why is this happening?
const { loading, data: newBooks, fetchMore, networkStatus } = useQuery(NEW_BOOKS, {
variables: {
first: PAGE_SIZE,
after: endCursor
},
notifyOnNetworkStatusChange: true
});
const NEW_BOOKS = gql`query GetNewBooks($first:Int!, $after:String){
books(
first: $first, after: $after,
filters: [
{
path: "isNew",
value: true
}
]
) {
totalCount
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
name
author {
id
name
}
}
}
}
}`;
-All books query filterable by name
const { loading, data: filteredBooks, fetchMore, networkStatus } = useQuery(ALL_BOOKS, {
variables: {
first: PAGE_SIZE,
after: endCursor,
name: nameFilter
},
notifyOnNetworkStatusChange: true
});
const ALL_BOOKS = gql`query GetAllBooks($first:Int!, $after:String, $name:String){
books(
first: $first, after: $after,
filters: [
{
path: "name",
value: $name,
type: "contains"
}
]
) {
totalCount
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
name
copiesSold
author {
id
name
}
}
}
}
}`;
The cache being used looks like this,
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
books: relayStylePagination(),
},
}
},
});
We have to pass keyArgs explictly when relayStylePagination or similar pagination is used.
A keyArgs: ["type"] field policy configuration means type is the only argument the cache should consider (in addition to the field name and the identity of the enclosing object) when accessing values for this field. A keyArgs: false configuration disables the whole system of differentiating field values by arguments, so the field's value will be identified only by the field's name (within some StoreObject), without any serialized arguments appended to it.
KeyArgs documentation here.
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
books: relayStylePagination(["name"]),
},
}
},
});

Passing Arguments to GraphQL with Next.JS and Apollo

A lot of this is very new to me since I have been a PHP developer for so long.
I am having a couple of issues that I know are easy to fix. I just have limited experience and am learning.
I cannot seem to pass the argument $token_id. I am using next JS, and I just want to take the number on the [id].js page, and pass it as the token ID.
I can't seem to get data from traitType, which is an object named "traits" which has an array of objects inside of it.
export async function getStaticProps(context) {
const client = new ApolloClient({
uri: 'http://localhost:3000/api/graphql',
cache: new InMemoryCache()
});
const token_id = String(context.params.id)
const { data } = await client.query({
query: gql`
query myQuery($token_id: String!) {
getData(token_id: $token_id) {
token_id
name
}
}
`
},{ variables: { token_id } });
props: {
data: data,
}
}
export async function getStaticPaths() {
return {
paths: [], //indicates that no page needs be created at build time
fallback: 'blocking' //indicates the type of fallback
}
}
My Type Definition
type Blah {
token_id: String
name: String
#traits: [traitType] #TODO: returns nothing since data is array
}
type traitType {
trait_type: String
value: String
}
I have to hard code the token
getData(token_id: "6") {
token_id
name
traits {
trait_type
}
}
I have tried several variations in the graphql query
query getDataQuery($token_id: String! = "6") {
getData(token_id: $token_id) {
Returns Null on traits, and $token id is null, and nothing is found
"traits": [
{
"trait_type": null,
"value": null
}
],
And my JSON from mongo db is an array with an object.
"traits" : [
{
"trait_type" : "birthday",
"value" : 1627423029.0,
}
]
I can post more than just fragments of code. Sorry for the formatting.

Convert uri to file react native js

Above is the request that I am trying to make in react native.
But I have URI
How shall I convert this to a file while sending to backend(PHP) server
This's my data to be sent in body:
const profileImageToBeSentToUpdate = {
data: {
id: authContext.user.id,
token: authContext.user.oauth_token.access_token,
profile_image: source.uri //Need help here, need to convert uri to file in order to send to php
},
type: 'POST',
url: 'update_profile_image',
success: profileImageUpdateSuccess,
error: profileImageUpdateError
};
authContext.setLoader();
console.log('***********profile data binded: ', profileImageToBeSentToUpdate);
NetworkAdaptation.postData(profileImageToBeSentToUpdate);
Now, backend (PHP) wants file format, I have URI format in react
React and React Native have no idea about what file system is. So they cannot make a file.
A workaround could be download a new file by using this code:
downloadprofileImageFile = () => {
const element = document.createElement("a");
const file = new Blob([source.uri], {type: 'text/plain'});
element.href = URL.createObjectURL(file);
element.download = "profile_image.txt";
document.body.appendChild(element);
element.click();
}
Then, pass the file to your profileImageToBeSentToUpdate function.
use rn-fetch-blob
import { Platform } from 'react-native';
const postImage = (localImageUri, remoteUrl) =>
RNFetchBlob.fetch(
'POST',
remoteUrl,
{ 'Content-Type': 'multipart/form-data' },
[
{
name: 'myimage',
filename: 'myimage',
type: 'image/jpeg',
data: RNFetchBlob.wrap(Platform.OS === 'ios' ? localImageUri.replace('file://', '') : localImageUri),
},
],
).then(response => {
// do something
}).catch(error => {
// do something
});
I used image picker and from imageResponse during showImagePicker method got the following details and used it in data to send
profile_image: { uri: imageResponse.uri, name: imageResponse.fileName, type: imageResponse.type }

Handling Graphql Mutation update, cache read and writeQuery, if the query is dynamic?

Doing nightlife app on freecodecamp https://learn.freecodecamp.org/coding-interview-prep/take-home-projects/build-a-nightlife-coordination-app/
I am trying to implement 'Go' button, similarly 'Like' button on Youtube or Instagram. Users click the button the number(counting how many users go) goes up meaning users will go there and click again, it revokes, the number decreases, users will not go there.
It seems like working well except the issue, I have to refresh the page and then, the number has increased or decreased and throws the error like below so:
Invariant Violation: Can't find field getBars({}) on object {
"getBars({\"location\":\"vancouver\"})": [
{
"type": "id",
"generated": false,
"id": "Bar:uNgTjA9ADe_6LWby20Af8g",
"typename": "Bar"
},
{
"type": "id",
"generated": false,
"id": "Bar:CwL5jwXhImT_7K5IB7mOvA",
"typename": "Bar"
},
{
"type": "id",
"generated": false,
"id": "Bar:mdt1tLbkZcOS2CsEbVF9Xg",
"typename": "Bar"
},
.
.
.
I am assuming handling update function will fix this issue but unlike the example from Apollo documentation:
// GET_TODOS is not dynamic query
// nothing to pass as variables to fetch TODO list
<Mutation
mutation={ADD_TODO}
update={(cache, { data: { addTodo } }) => {
const { todos } = cache.readQuery({ query: GET_TODOS });
cache.writeQuery({
query: GET_TODOS,
data: { todos: todos.concat([addTodo]) },
});
}}
>
My query is dynamic:
// I have to pass location variable, otherwise it won't fetch anything.
const GET_BARS_QUERY = gql`
query getBars($location: String!) {
getBars(location: $location) {
id
name
url
rating
price
image_url
goings {
username
}
goingCount
}
}
`;
I believe I might need to handle to provide location using readQuery and writeQury but not too sure what I should do.
Here's my code:
const GoButton = ({ user, bar }) => {
const { token } = user;
const { id, goings, goingCount } = bar;
const [userGoes] = useMutation(GO_MUTATION, {
variables: { yelp_id: id },
update(proxy, result) {
const data = proxy.readQuery({
query: GET_BARS_QUERY
});
data.getBars = [result.userGoes, ...data.getBars];
proxy.writeQuery({ query: GET_BARS_QUERY, data });
}
});
return (
<Button onClick={userGoes}>
Go {goingCount}
</Button>
);
};
const GO_MUTATION = gql`
mutation go($yelp_id: String!) {
go(yelp_id: $yelp_id) {
id
goings {
id
username
}
goingCount
}
}
`;
export default GoButton;
Full code here https://github.com/footlessbird/Nightlife-Coordination-App
when you read/write the getBars query, you need to pass the location as a variable
const [userGoes] = useMutation(GO_MUTATION, {
variables: { yelp_id: id },
update(proxy, result) {
const data = proxy.readQuery({
query: GET_BARS_QUERY,
variables: {
location: 'New York'
}
});
data.getBars = [result.userGoes, ...data.getBars];
proxy.writeQuery({ query: GET_BARS_QUERY, data,
variables: {
location: 'New York'
}
});
}
});

Resources