Введение

Twilio позволяет таким разработчикам, как вы, добавлять коммуникационные технологии в свои приложения. Вы можете использовать API и SDK Twilio для отправки SMS и MMS, сообщений WhatsApp и электронных писем, а также для совершения телефонных звонков и видеозвонков.

В этом руководстве вы узнаете, как создать приложение, которое позволит вам делиться текстами любимых песен с друзьями с помощью React, Express и Twilio.

Давайте начнем!

Предпосылки

  • Учетная запись Twilio и номер телефона, на который можно отправлять SMS-сообщения (вы можете зарегистрировать учетную запись Twilio бесплатно здесь)
  • Node.js для создания нашего приложения React и запуска нашего сервера (вы можете создать серверный компонент этого на любом языке, но в этом посте мы собираемся сделать это в Node, чтобы мы могли сохранить все это JavaScript)

Проект, описанный в этой статье, состоит из двух частей:

  1. Фронтенд React.js
  2. Бэкэнд Node.js

Получить стартовый код

Для начала загрузите или клонируйте репозиторий, который будет содержать начальные файлы и файл .css:

git clone https://github.com/bhagatpratham/twilio-lyrics-sharing.git

Перейдите в каталог

cd twilio-lyrics-sharing

npm install

Установите зависимости и установите переменные среды.

Последний пакет для установки — twilio:

npm install twilio

Если вам так же, как и мне, интересно узнать, какие зависимости были установлены, вы всегда можете проверить их в файле package.json, который находится в корне проекта.

Затем создайте свое приложение для реагирования, которое будет содержать несколько начальных файлов, выполнив эту команду:

npx create-react-app client

Теперь, когда у вас есть начальный код, вы должны уделить немного времени изучению папки /src внутри twilio-lyrics-sharing-starter-kit. Вы найдете все файлы, доступные при создании реагирующего приложения.

Создание серверной части

В каталоге проекта создайте два новых файла: .env и index.js:

touch .env

touch index.js

В файл .env вы будете добавлять переменные среды, а в файл index.js вы будете писать код для своего бэкенда.

Получите учетные данные Twilio

Откройте новый файл .env в своем любимом текстовом редакторе.

Получите свой Sid учетной записи Twilio и токен аутентификации из консоли Twilio, а также номер телефона Twilio, по которому можно отправлять SMS-сообщения. Введите все три в файл .env, который вы создали ранее, вот так:

TWILIO_ACCOUNT_SID=YOUR_ACCOUNT_SID
TWILIO_AUTH_TOKEN=YOUR_AUTH_TOKEN
TWILIO_PHONE_NUMBER=YOUR_TWILIO_PHONE_NUMBER

Это установит ваши учетные данные в среде. Теперь откройте index.js, чтобы мы могли начать работу с кодом, необходимым для отправки текста.

Потребуйте и инициализируйте библиотеку Twilio с учетными данными из файла .env и создайте базовый экспресс-сервер, как показано в приведенном ниже коде:

const express = require('express');
const bodyParser = require('body-parser');
require("dotenv").config();
const app = express();
const port = 3000;
// Using the Twilio credentials
const client = require('twilio')(
  process.env.TWILIO_ACCOUNT_SID,
  process.env.TWILIO_AUTH_TOKEN
);
app.use(bodyParser.urlencoded({ extended: false }));
app.listen(port, () => {
  console.log(`Server listening at <http://localhost>:${port}`);
});

Создайте маршрут для запроса POST чуть ниже переменной client, где вы инициализировали учетные данные twilio.

app.post('/send-lyrics', (req, res) => {

});

Конечная точка принимает два параметра в теле запроса: lyrics, который является телом текстового сообщения, и toNumber, который является номером телефона для отправки SMS-сообщения.

app.post("/send-lyrics", (req, res) => {
  const lyrics = req.body.lyrics;
  const toNumber = req.body.toNumber;
});

Далее используйте клиент Twilio, который мы инициализировали ранее, чтобы создать сообщение. используйте свой номер Twilio в качестве from номера и получите to номер и body сообщения из тела входящего запроса.

app.post("/send-lyrics", (req, res) => {
  const lyrics = req.body.lyrics;
  const toNumber = req.body.toNumber;
client.messages
    .create({
      body: lyrics,
      to: toNumber,
      from: process.env.TWILIO_PHONE_NUMBER,
    })
    .then((message) => {
      console.log("SMS sent successfully:", message.sid);
      res.send("Lyrics shared successfully!");
    })
    .catch((err) => {
      console.error("Error sending SMS:", err);
      res.status(500).send("Error sharing lyrics!");
    });
});

Это все, что вам нужно на сервере, давайте начнем с части React. Вот как должен выглядеть окончательный файл index.js.

const express = require("express");
const bodyParser = require("body-parser");
require("dotenv").config();
const app = express();
const port = 3000;
const client = require("twilio")(
	process.env.TWILIO_ACCOUNT_SID,
	process.env.TWILIO_AUTH_TOKEN
);

app.use(bodyParser.urlencoded({ extended: false }));

app.post("/send-lyrics", (req, res) => {
  const lyrics = req.body.lyrics;
  const toNumber = req.body.toNumber;
  client.messages
    .create({
      body: lyrics,
      to: toNumber,
      from: process.env.TWILIO_PHONE_NUMBER,
    })
    .then((message) => {
      console.log("SMS sent successfully:", message.sid);
      res.send("Lyrics shared successfully!");
    })
    .catch((err) => {
      console.error("Error sending SMS:", err);
      res.status(500).send("Error sharing lyrics!");
    });
});

app.listen(port, () => {
  console.log(`Server listening at <http://localhost>:${port}`);
});

Запустите сервер с помощью следующей команды в вашем терминале:

nodemon index.js Если вы все сделали правильно, вы получите следующее сообщение: Server listening at [<http://localhost:3000>](<http://localhost:3000/>)

Создание клиента

Вы начнете с компонента App.

Откройте папку клиента, содержащую код React. Внутри папки клиента вы найдете папку src, содержащую файл App.js.

В этом компоненте вы получите текст из REST API и создадите функциональность для отправки текста на любой номер телефона, подтвержденный Twilio, с помощью SMS API Twilio.

Удалите все из файла App.js и замените его следующим кодом:

// App.js
import "./App.css";
function App() {
  return (
    <div className="App">
      <section className="logo">
        <h2>Lyrics Sharing App</h2>
        <p className="app-desc">
          a simple and easy to use app to share song lyrics with your friends
        </p>
      </section>
    </div>
  );
}

export default App;

Теперь мы создадим некоторые переменные состояния, используя хук useState.

Импортируйте useState()hook из React сверху и добавьте следующую переменную состояния, показанную в коде ниже:

// App.js
import { useState } from "react";
import "./App.css";

function App() {
  const [lyrics, setLyrics] = useState(null);
  const [trackName, setTrackName] = useState("");
  const [artistName, setArtistName] = useState("");
  const [copied, setCopied] = useState(false);
  const [number, setNumber] = useState("");

return (
    <div className="App">
      <section className="logo">
        <h2>Lyrics Sharing App</h2>
        <p className="app-desc">
          a simple and easy to use app to share song lyrics with your friends
        </p>
      </section>
    </div>
  );
}

export default App;
  • lyrics, который будет использоваться для хранения текстов песен, которые вы получите из API.
  • trackName будет использоваться для хранения названия песни, которую пользователь хочет найти.
  • artistName сохранит имя исполнителя песни, которое пользователь вводит в поле поиска.
  • copied, будет использоваться для копирования текста
  • number, в котором будет храниться номер телефона, введенный пользователем для обмена текстами песен.

Затем вы должны добавить следующий код внутрь return() компонента App.

Код определяет основную часть пользовательского интерфейса приложения, который включает в себя три основных раздела: окно поиска для ввода названия трека и исполнителя, раздел отображения для извлеченных текстов и раздел, где пользователь может ввести номер телефона. и поделитесь текстом.

<section className="main-body">
        <div className="search-lyrics">
          <input placeholder="Enter track name"></input>
          <div className="search-container">
            <input placeholder="Enter artist name (optional)"></input>
            <button className="search-btn">Search Lyrics</button>
          </div>
        </div>
        <div className="lyrics">
          <button className="copy-lyrics">Copy Lyrics</button>
          <p className="display-lyrics">
            {lyrics?.lyrics || "Nothing here yet ..."}
          </p>
        </div>
        <div className="send-lyrics">
          <label htmlFor="to">To:</label>
          <input
            className="phone-number-input"
            type="tel"
            name="to"
            id="to"
            placeholder="Enter phone number"
            value={number}
            onChange={(e) => setNumber(e.target.value)}
          />{" "}
          <button className="share-lyrics">Share Lyrics</button>
        </div>
        <p>Note: Lyrics should not exceed 1600 characters</p>
  </section>

Давайте добавим немного CSS, чтобы приложение выглядело красиво. Вставьте следующий код в файл App.css.

/* Define color palette */
:root {
  --color-primary: #ef233c;
  --color-secondary: #d90429;
  --color-accent: #8d99ae;
  --color-background: #edf2f4;
  --color-text: #2b2d42;
}

/* Apply global styles */
body {
  margin: 0;
  font-family: Arial, sans-serif;
  background-color: var(--color-background);
  color: var(--color-text);
}
/* Apply styles to App section */
.App {
  max-width: 800px;
  margin: 0 auto;
  padding: 40px 20px;
}
/* Apply styles to logo section */
.logo {
  text-align: center;
  margin-bottom: 40px;
}
.logo h2 {
  font-size: 36px;
  font-weight: bold;
  margin-bottom: 10px;
  color: var(--color-primary);
}
.logo .app-desc {
  font-size: 18px;
  margin-bottom: 0;
}
/* Apply styles to main-body section */
.main-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
/* Apply styles to search-lyrics section */
.search-lyrics {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.search-lyrics input {
  padding: 10px;
  border: none;
  border-radius: 5px;
  font-size: 16px;
}
.search-container {
  display: flex;
  gap: 10px;
  align-items: center;
}
.search-btn {
  background-color: var(--color-primary);
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;
}
.lyrics {
  margin-top: 2rem;
}
/* Button styles */
.copy-lyrics,
.share-lyrics {
  display: inline-block;
  background-color: var(--color-primary);
  color: var(--color-background);
  border: none;
  padding: 0.75rem 1.5rem;
  margin-right: 1rem;
  border-radius: 5px;
  font-size: 1rem;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.3s ease;
}
.copy-lyrics:hover,
.share-lyrics:hover {
  background-color: var(--color-secondary);
}
/* Display lyrics styles */
.display-lyrics {
  background-color: var(--color-background);
  color: var(--color-text);
  padding: 1.5rem;
  margin-top: 1.5rem;
  border-radius: 5px;
  font-size: 0.8rem;
  line-height: 1.5;
}
/* Send lyrics styles */
.send-lyrics {
  margin-top: 1.5rem;
}
.share-lyrics {
  background-color: var(--color-primary);
  color: var(--color-background);
}
.share-lyrics:hover {
  background-color: var(--color-secondary);
}
label {
  display: inline-block;
  margin-right: 1rem;
  font-size: 1.2rem;
  color: var(--color-text);
}
input {
  padding: 0.5rem 1rem;
  border-radius: 5px;
  border: 1px solid var(--color-accent);
  font-size: 1rem;
  color: var(--color-text);
}
input:focus {
  outline: none;
  border-color: var(--color-primary);
}
/* Note styles */
p {
  margin-top: 1.5rem;
  font-size: 0.9rem;
  color: var(--color-accent);
}

Запустите приложение, переключившись в следующий каталог в вашем терминале cd twilio-lyrics-sharing/client && npm start, и вы увидите это на странице. Убедитесь, что вы переключились на каталог клиента в своем терминале, чтобы запустить приложение React.

Если ваш серверный код уже запущен локально на PORT=3000, то при выполнении приведенной выше команды вам будет предложено запустить проект React на другом порту. Нажмите y, а затем return, чтобы сделать это.

Затем вы создадите две функции**getTrack()** и getArtist() внутри компонента App, прямо под объявлениями состояния, которые будут обновлять переменные состояния trackName и artistName, которые вы создали ранее соответственно.

Таким образом, всякий раз, когда пользователь вводит что-то в поля ввода, значение переменных будет автоматически обновляться до того, что ввел пользователь.

const getTrack = (e) => {
  setTrackName(e.target.value);
};

const getArtist = (e) => {
  setArtistName(e.target.value);
};

Добавьте функцию getTrack к следующему элементу <input>, используя атрибут onChange.

<input 
	placeholder="Enter track name" 
	onChange={getTrack}>
</input>

Точно так же вы должны добавить функцию getArtist к следующему <input>element.

<input 
	placeholder="Enter artist name (optional)" 
	onChange={getArtist}>
</input>

Теперь вы создадите функцию с именем handleClick, которая будет делать запрос API на **https://lyrist.vercel.app/api/ ****** и извлекать тексты для данной песни и исполнителя.

Он использует метод fetch() для выполнения запроса GET к конечной точке URL, который включает переменные trackName и artistName в качестве параметров.

Если ответ от API успешен, функция извлекает данные ответа в формате JSON с помощью метода res.json() и устанавливает текст в переменную состояния lyrics с помощью функции setLyrics().

const handleClick = async () => {
    if (trackName.trim() === "") {
      console.error("Track name can't be empty!");
    } else {
      const res = await fetch(
        `https://lyrist.vercel.app/api/${trackName}/${artistName}`
      );
      if (res.ok) {
        const data = await res.json();
        setLyrics(data);
      } else {
        res.error("Lyrics not found!");
      }
    }
  };

Добавьте функцию handleClick к кнопке Search Lyrics, используя атрибут onClick. Таким образом, всякий раз, когда пользователь нажимает кнопку, функция отправляет запрос API для поиска текста песни.

<button className="search-btn" onClick={handleClick}>Search Lyrics</button>

Создайте еще одну функцию с именем handleCopy, которая будет копировать текст песни в буфер обмена пользователя.

Сначала функция вызывает метод navigator.clipboard.writeText(), передавая выражение lyrics?.lyrics в качестве текста для копирования. Выражение lyrics?.lyrics использует необязательную цепочку, чтобы проверить, существует ли объект лирики перед доступом к его свойству лирики.

Если объект текстов песен имеет значение null или не определен, в качестве копируемого текста будет использоваться пустая строка.

Если текст успешно скопирован, функция устанавливает для скопированной переменной состояния copied значение true с помощью функции setCopied().

const handleCopy = () => {
    navigator.clipboard
      .writeText(lyrics?.lyrics || "")
      .then(() => {
        setCopied(true);
      })
      .catch(() => {
        console.error("Couldn't Copy!");
      });
  };

Аналогичным образом добавьте функцию handleCopy к кнопке Copy Lyrics.

<button className="copy-lyrics" onClick={handleCopy}>Copy Lyrics</button>

Теперь нам нужно обработать функцию обмена текстами песен, которая позволит пользователю делиться текстами песен на номер телефона.

Создайте функцию shareLyrics в компоненте App, которая принимает два параметра: lyrics и number

Сначала функция отправляет запрос POST на сервер Express.js, работающий на http://localhost:3000/send-lyrics, с параметрами lyrics и **toNumber** как часть тела запроса.

Если ответ от сервера успешен, функция регистрирует сообщение об успешном выполнении на консоли и показывает предупреждение пользователю. В противном случае он регистрирует сообщение об ошибке и показывает предупреждение об ошибке.

const shareLyrics = async (lyrics, number) => {
    if (lyrics && number.trim() !== "") {
      try {
        const smsRes = await fetch("<http://localhost:3000/send-lyrics>", {
          method: "POST",
          headers: {
            "Content-Type": "application/x-www-form-urlencoded",
          },
          body: new URLSearchParams({
            lyrics: lyrics,
            toNumber: number,
          }),
        });
        if (smsRes.ok) {
          console.log("Lyrics shared successfully!");
          alert("Lyrics shared successfully!");
          return true;
        } else {
          console.error("Error sharing lyrics!");
          alert("Error sharing lyrics!");
        }
      } catch (err) {
        console.error("Error sharing lyrics:", err);
      }
    }
    return false;
  };

Наконец, добавьте функцию shareLyrics к следующей кнопке, передав lyrics и number с помощью атрибута onClick.

<button className="share-lyrics" onClick={() => shareLyrics(lyrics?.lyrics, number)}>
	Share Lyrics
</button>

Это все, что вам нужно. Окончательный файл App.js должен выглядеть так:

import { useState } from "react";
import "./App.css";

function App() {
  const [lyrics, setLyrics] = useState(null);
  const [trackName, setTrackName] = useState("");
  const [artistName, setArtistName] = useState("");
  const [copied, setCopied] = useState(false);
  const [number, setNumber] = useState("");
  const getTrack = (e) => {
    setTrackName(e.target.value);
  };
  const getArtist = (e) => {
    setArtistName(e.target.value);
  };
  const handleClick = async () => {
    if (trackName.trim() === "") {
      console.error("Track name can't be empty!");
    } else {
      const res = await fetch(
        `https://lyrist.vercel.app/api/${trackName}/${artistName}`
      );
      if (res.ok) {
        const data = await res.json();
        setLyrics(data);
      } else {
        res.error("Lyrics not found!");
      }
    }
  };
  const handleCopy = () => {
    navigator.clipboard
      .writeText(lyrics?.lyrics || "")
      .then(() => {
        setCopied(true);
      })
      .catch(() => {
        console.error("Couldn't Copy!");
      });
  };
  const shareLyrics = async (lyrics, number) => {
    if (lyrics && number.trim() !== "") {
      if (lyrics.length > 1600) {
        console.error("Lyrics exceed 1600 characters!");
        alert("Lyrics exceed 1600 characters!");
        return false;
      }
      try {
        const smsRes = await fetch("<http://localhost:3000/send-lyrics>", {
          method: "POST",
          headers: {
            "Content-Type": "application/x-www-form-urlencoded",
          },
          body: new URLSearchParams({
            lyrics: lyrics,
            toNumber: number,
          }),
        });
        if (smsRes.ok) {
          console.log("Lyrics shared successfully!");
          alert("Lyrics shared successfully!");
          return true;
        } else {
          console.error("Error sharing lyrics!");
          alert("Error sharing lyrics!");
        }
      } catch (err) {
        console.error("Error sharing lyrics:", err);
      }
    }
    return false;
  };
  return (
    <div className="App">
      <section className="logo">
        <h2>Lyrics Sharing App</h2>
        <p className="app-desc">
          a simple and easy to use app to share song lyrics with your friends
        </p>
      </section>
      <section className="main-body">
        <div className="search-lyrics">
          <input placeholder="Enter track name" onChange={getTrack}></input>
          <div className="search-container">
            <input
              placeholder="Enter artist name (optional)"
              onChange={getArtist}
            ></input>
            <button className="search-btn" onClick={handleClick}>
              Search Lyrics
            </button>
          </div>
        </div>
        <div className="lyrics">
          <button className="copy-lyrics" onClick={handleCopy}>
            Copy Lyrics
          </button>
          <p className="display-lyrics">
            {lyrics?.lyrics || "Nothing here yet ..."}
          </p>
        </div>
        <div className="send-lyrics">
          <label htmlFor="to">To:</label>
          <input
            className="phone-number-input"
            type="tel"
            name="to"
            id="to"
            placeholder="Enter phone number"
            value={number}
            onChange={(e) => setNumber(e.target.value)}
          />{" "}
          <button
            className="share-lyrics"
            onClick={() => shareLyrics(lyrics?.lyrics, number)}
          >
            Share Lyrics
          </button>
        </div>
        <p>Note: Lyrics should not exceed 1600 characters</p>
      </section>
    </div>
  );
}
export default App;

Наконец, теперь вы можете проверить, работает ли проект, запустив npm start. Приложение загрузится в вашем браузере по адресу localhost:3000.

Если ваш серверный код уже запущен локально на PORT=3000, то при выполнении приведенной выше команды вам будет предложено запустить проект React на другом порту. Нажмите y, а затем return, чтобы сделать это.

Вы можете проверить весь код из этого примера приложения в репозитории GitHub.

Заключение

В этой статье вы узнали, как создать приложение для обмена текстами песен с помощью Twilio, React и Express. Надеюсь, вы узнали больше о некоторых нюансах React и о том, как интегрировать API-интерфейсы Twilio в свои приложения React и Express.

Чтобы сделать это приложение круче, возможно, вы можете создать свою собственную виртуальную вечеринку для прослушивания с помощью Twilio Programmable Video. Дайте мне знать в Твиттере, если у вас есть идея получше.

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .