Posts

Create WEB APP "MEME of the day" use IPFS network and REACT step by step

avatar of @goardbit
25
@goardbit
·
·
0 views
·
5 min read

WEB APP "MEME of the day!"

meme-web-app - ссылка на веб приложение в сети IPFS. git repository - ссылка на репозиторий в github.

Создадим веб приложение "Мем дня". Для создания веб приложения буду использовать библиотеку React, для выхода в сеть IPFS использую шлюз infura.io, для пользовательского интерфейса фреймворк material-ui. Разработка ведется в среде Linux Ubuntu 18.04.5 LTS. Также я предполагаю что пользователь знаком с работой React и языковом стандартом ECMAScript6 и выше.

Для начала нужно установить (в случаи отсутствия) среду выполнения JavaScript NodeJS и менеджер пакетов npm после этого установить пакет create-react-app. Этот инструмент позволяет настроить среду для разработки React, выполнив команду:
$ npm i -g create-react-app 

флаг -g устанавливает пакет глобально для этого нужно обладать правами администратора.
Создадим шаблонное веб приложение выполнив команду:

$ create-react-app meme-web-app  

В результате будет создана директория meme-web-app с необходимыми зависимостями, директориями и файлами. Переходим в созданную директорию и запустим редактор, я пользуюсь visual studio code.

$ cd meme-web-app 
meme-web-app$ code . 

На рисунке ниже отображен интерфейс редактора visual studio code в директории meme-web-app и исходный код в файле App.js, править будем только этот файл. В редакторе кода откройте дополнительное окно с терминалом и выполните команду npm start. React предлагает использование менеджера пакетов yarn, но я буду пользоваться npm.

meme-web-app$ npm start 

Подождите несколько секунд и по окончанию выполнения команды в терминале выведется следующее сообщение:

Compiled successfully! 
 
You can now view my-web-app in the browser. 
 
  Local:            http://localhost:3000 
  On Your Network:  http://192.168.0.47:3000 
 
Note that the development build is not optimized. 
To create a production build, use yarn build. 

Здесь IP адрес может отличаться от Вашего, после этого откроется браузер по умолчанию с адресом http://localhost:3000.
Скриншот запущенного веб приложения. Подготовительная часть окончена, приступим к правке кода но для начала установим библиотеку material-ui и ipfs клиент.

meme-web-app$ npm install @material-ui/core 
meme-web-app$ npm install ipfs-http-client 

Основной файл которой необходимо править это App.js. Ниже представлен код который лишь отображает интерфейс без какого-либо функционала.

import { makeStyles } from "@material-ui/core/styles"; 
import AppBar from "@material-ui/core/AppBar"; 
import Toolbar from "@material-ui/core/Toolbar"; 
import Typography from "@material-ui/core/Typography"; 
import Paper from "@material-ui/core/Paper"; 
import Container from "@material-ui/core/Container"; 
import Grid from "@material-ui/core/Grid"; 
import Input from "@material-ui/core/Input"; 
import InputLabel from "@material-ui/core/InputLabel"; 
import Button from "@material-ui/core/Button"; 
 
const useStyles = makeStyles((theme) => ({ 
  title: { 
    flexGrow: 1, 
  }, 
  input: { 
    display: "none", 
  }, 
  paper: { 
    display: "flex", 
    justifyContent: "center", 
    alignItems: "center", 
    marginTop: theme.spacing(4), 
    width: theme.spacing(90), 
    height: theme.spacing(80), 
  }, 
})); 
 
function App() { 
  const classes = useStyles(); 
 
  return ( 
    <div> 
 
 
      <Container> 
        <AppBar position="static"> 
          <Toolbar> 
            <Typography className={classes.title}> 
              Meme of the day!!! 
            </Typography> 
            <Input 
              inputProps={{ accept: "image/*" }} 
              type="file" 
              id="add_meme" 
              className={classes.input} 
            /> 
            <InputLabel htmlFor="add_meme"> 
              <Button variant="contained" color="secondary" component="span"> 
                ADD MEME 
              </Button> 
            </InputLabel> 
          </Toolbar> 
        </AppBar> 
        <Typography variant="h3" align="center"> 
          Meme on the topic of the day 
        </Typography> 
        <Grid justify="center" container> 
          <Paper className={classes.paper} elevation={3}> 
            <img src="https://memepedia.ru/wp-content/uploads/2019/08/enslaved-moisture-meme-2.jpg" /> 
          </Paper> 
        </Grid> 
      </Container> 
     
 
</div> 
 
  ); 
} 
 
export default App; 

Получился вот такой интерфейс где изображение я вставил с любого доступного ресурса Займемся функционалом загрузки файла в сеть ipfs. В тег <Input/> добавим атрибуты type="file", onChange={changeHandler}. changeHandler это функция, которая выполняется при добавления файла.

<Input 
  onChange={changeHandler} 
  inputProps={{ accept: "image/*" }} 
  type="file" 
  id="add_meme" 
  className={classes.input} 
/> 

Теперь создадим функцию changeHandler(), при этом надо отметить функция асинхронная для того чтобы получить ответ после загрузки файла в сеть IPFS. На событие применяем функцию preventDefault(), чтобы не обновлялась страница. В этой функции будем использовать клиент ipfs-http-client давайте его импортируем и введем параметры.

import * as ipfsClient from "ipfs-http-client"; 
 
const ipfs = ipfsClient({ 
  host: "ipfs.infura.io", 
  port: "5001", 
  protocol: "https", 
}); 

Также добавляем useState для хранения ссылки на файл в сети IPFS между рендерами страницы. Более подробно как использовать useState можно ознакомиться в документации React.

import { useState } from "react"; 
const [memeHash, setMemeHash] = useState(""); 

Теперь напишем нашу функцию changeHandler() с предыдущими уточнениями.

const changeHandler = async (event) => { 
    event.preventDefault(); 
    const memeFile = event.target.files[0]; 
    const res = await ipfs.add(memeFile, { 
      progress: (prog) => console.log(prog), 
    }); 
    const resV1 = res.cid.toV1(); 
    setMemeHash(toBase32(resV1)); 
  }; 

Если вы заметили в коде применяется функция toBase32(), так зачем она нужна? Функция ipfs.add() возвращает нам CID. CID это идентификатор контента, который основан на криптографическом хэше контента, различают две версии CIDv0 and CIDv1. Так вот функция ipfs.add() возвращает версия CIDv0 для того чтобы преобразовать CIDv0 в CIDv1 вызываю функцию toBase32(). Преобразование делается в целях безопасности.

import { CID } from "ipfs-http-client"; 
const toBase32 = (value) => { 
  const cid = new CID(value); 
  return cid.toV1().toBaseEncodedString("base32"); 
}; 

Теперь нам нужно отобразить наш мем на странице в теге <img src=``/> с помощью строкового литерала.

<img src={`https://${memeHash}.ipfs.infura-ipfs.io/`} /> 

Основную работу мы завершили, добавляем несколько строк для показа прогресса загрузки файла сеть IPFS.

import LinearProgress from "@material-ui/core/LinearProgress"; 
import Box from "@material-ui/core/Box"; 
const [progress, setProgress] = useState(0); 
const [sizeFile, setSizeFile] = useState(0); 
 
const normalise = (value) => (value * 100) / sizeFile; 

Преобразуем функцию changeHandler(), всё также для показа прогресса загрузки.

const changeHandler = async (event) => { 
  event.preventDefault(); 
  const memeFile = event.target.files[0]; 
  setSizeFile(memeFile.size); 
  const res = await ipfs.add(memeFile, { 
    progress: (prog) => setProgress(prog), 
  }); 
  setProgress(0); 
  const resV1 = res.cid.toV1(); 
  setMemeHash(toBase32(resV1)); 
}; 

Добавляем наш прогресс загрузки на страницу.

{!!progress ? ( 
  <LinearProgress 
    color="secondary" 
    variant="determinate" 
    value={normalise(progress)} 
  /> 
) : ( 
  <Box className={classes.box}></Box> 
)} 

Итоговый код в файле App.js

import { useState } from "react"; 
import * as ipfsClient from "ipfs-http-client"; 
import { CID } from "ipfs-http-client"; 
import { makeStyles } from "@material-ui/core/styles"; 
import AppBar from "@material-ui/core/AppBar"; 
import Toolbar from "@material-ui/core/Toolbar"; 
import Typography from "@material-ui/core/Typography"; 
import Paper from "@material-ui/core/Paper"; 
import Container from "@material-ui/core/Container"; 
import Grid from "@material-ui/core/Grid"; 
import Input from "@material-ui/core/Input"; 
import InputLabel from "@material-ui/core/InputLabel"; 
import Button from "@material-ui/core/Button"; 
import LinearProgress from "@material-ui/core/LinearProgress"; 
import Box from "@material-ui/core/Box"; 
 
const useStyles = makeStyles((theme) => ({ 
  title: { 
    flexGrow: 1, 
  }, 
  input: { 
    display: "none", 
  }, 
  box: { 
    height: "4px", 
  }, 
  paper: { 
    display: "flex", 
    justifyContent: "center", 
    alignItems: "center", 
    marginTop: theme.spacing(1), 
    width: theme.spacing(90), 
    height: theme.spacing(80), 
  }, 
})); 
 
function App() { 
  const classes = useStyles(); 
  const [memeHash, setMemeHash] = useState( 
    "bafybeigc5k5hriuejo5ccdre7cnmcppml2vmx4ffjfw7juiai6nusdye7u" 
  ); 
  const [progress, setProgress] = useState(0); 
  const [sizeFile, setSizeFile] = useState(0); 
 
  const ipfs = ipfsClient({ 
    host: "ipfs.infura.io", 
    port: "5001", 
    protocol: "https", 
  }); 
 
  const normalise = (value) => (value * 100) / sizeFile; 
 
  const toBase32 = (value) => { 
    const cid = new CID(value); 
    return cid.toV1().toBaseEncodedString("base32"); 
  }; 
 
  const changeHandler = async (event) => { 
    event.preventDefault(); 
    const memeFile = event.target.files[0]; 
    setSizeFile(memeFile.size); 
    const res = await ipfs.add(memeFile, { 
      progress: (prog) => setProgress(prog), 
    }); 
    setProgress(0); 
    const resV1 = res.cid.toV1(); 
    setMemeHash(toBase32(resV1)); 
  }; 
 
  return ( 
    <div> 
 
 
      <Container> 
        <AppBar position="static"> 
          <Toolbar> 
            <Typography className={classes.title}> 
              Meme of the day!!! 
            </Typography> 
            <Input 
              onChange={changeHandler} 
              inputProps={{ accept: "image/*" }} 
              type="file" 
              id="add_meme" 
              className={classes.input} 
            /> 
            <InputLabel htmlFor="add_meme"> 
              <Button variant="contained" color="secondary" component="span"> 
                ADD MEME 
              </Button> 
            </InputLabel> 
          </Toolbar> 
        </AppBar> 
        {!!progress ? ( 
          <LinearProgress 
            color="secondary" 
            variant="determinate" 
            value={normalise(progress)} 
          /> 
        ) : ( 
          <Box className={classes.box}></Box> 
        )} 
        <Typography variant="h3" align="center"> 
          Meme on the topic of the day 
        </Typography> 
        <Grid justify="center" container> 
          <Paper className={classes.paper} elevation={3}> 
            <img src={`https://${memeHash}.ipfs.infura-ipfs.io`} /> 
          </Paper> 
        </Grid> 
      </Container> 
     
 
</div> 
 
  ); 
} 
 
export default App; 

Данное веб приложение хостит в сети IPFS доступ по ссылке ниже. meme-web-app Предоставлена краткая инструкция по созданию веб приложения "Мем дня" с отправкой и хранения файлов в сети IPFS. Если есть вопросы пишите в комментарии.

Posted Using LeoFinance Beta