بناء تطبيق ويب متكامل باستخدام AWS Amplify و React: دليل شامل

دقائق القراءة: 19

مقدمة إلى AWS Amplify: تسريع تطوير تطبيقات الويب المتكاملة

يُعد AWS Amplify أداة قوية ومطورة من قِبل خدمات أمازون الويب (Amazon Web Services) تهدف إلى تبسيط عملية تطوير التطبيقات بشكل كبير. يوفر Amplify مجموعة واسعة من الميزات التي تتيح للمطورين التفاعل بسرعة وسهولة مع خدمات AWS الأخرى. هذا يعني أن بإمكانك تخصيص وقت أطول لبناء الميزات الفريدة التي تميز تطبيقك، بدلاً من الانشغال بتفاصيل البنية التحتية المعقدة.

يتناول هذا الدليل الشامل بناء تطبيق ويب متكامل خطوة بخطوة، مقسماً إلى أربعة أجزاء رئيسية لضمان فهم عميق وتطبيق عملي:

  • كيفية إنشاء التطبيق وإعداد نظام تسجيل الدخول والخروج.
  • كيفية دمج قاعدة بيانات والتعامل مع البيانات.
  • كيفية إضافة تخزين للملفات والاستفادة منها.
  • كيفية تمكين المستخدمين من رفع ملفاتهم وبياناتهم الخاصة.

الجزء الأول: إعداد التطبيق مع ميزات التسجيل وتسجيل الدخول والخروج

في هذا القسم الأول، سنقوم بإعداد تطبيق React جديد ودمج AWS Amplify لإضافة وظائف التسجيل وتسجيل الدخول والخروج بأسهل طريقة ممكنة.

إنشاء تطبيق React جديد

نحتاج أولاً إلى إنشاء تطبيق React جديد باستخدام أداة create-react-app. افتح نافذة طرفية (Terminal) وقم بتشغيل الأوامر التالية. إذا لم تكن لديك create-react-app مثبتة مسبقًا، يمكنك تشغيل الأمر npm i -g create-react-app أولاً لتثبيتها عالميًا.

npx create-react-app amplify-react-app
cd amplify-react-app

تثبيت وتكوين AWS Amplify CLI

بعد إعداد تطبيق React، يمكننا الآن تثبيت Amplify CLI ثم تكوينه.

npm install -g @aws-amplify/cli
amplify configure

سيؤدي هذا الأمر إلى فتح علامة تبويب في متصفحك خاصة بـ AWS Console. تأكد من تسجيل الدخول إلى الحساب الصحيح باستخدام مستخدم يمتلك صلاحيات إدارية (admin permissions). ارجع إلى نافذة الطرفية واتبع الخطوات، مضيفًا المنطقة (region) واسمًا للمستخدم. سيتم إعادتك بعد ذلك إلى المتصفح حيث يمكنك اتباع الخطوات لإنشاء المستخدم الجديد. تأكد من البقاء في الصفحة التي تعرض المفتاح السري (key and secret)!

عد إلى نافذة الطرفية مرة أخرى واتبع الخطوات، وانسخ مفتاح الوصول (access key) والمفتاح السري (secret) إلى الطرفية عندما يُطلب منك ذلك. عندما يُسأل عما إذا كنت تريد إضافة هذا إلى ملف تعريف (profile)، اختر Yes. أنشئ ملف تعريف باسم مثل serverless-amplify.

تهيئة مشروع Amplify وإضافة المصادقة

يمكننا الآن تهيئة إعدادات Amplify عن طريق تشغيل الأمر amplify init. يمكنك إعطاء المشروع اسمًا والإجابة على جميع الأسئلة؛ معظم الإجابات يجب أن تكون صحيحة افتراضيًا. تستغرق هذه العملية بعض الوقت لإجراء التغييرات على حسابك في AWS. بمجرد الانتهاء، نحتاج إلى إضافة المصادقة (authentication) إلى التطبيق. نقوم بذلك باستخدام الأمر amplify add auth. اختر الطريقة الافتراضية (default) لتسجيل الدخول عبر البريد الإلكتروني (email)، ثم اختر no, I am done.

يمكننا الآن نشر هذه التغييرات عن طريق تشغيل amplify push. تستغرق هذه العملية بعض الوقت، ولكن في النهاية، سيتم إنشاء ملف src/aws-exports.js الخاص بنا.

بناء تطبيق React مع مكونات Amplify UI

الآن يمكننا البدء في بناء تطبيق React. ابدأ بتثبيت حزم npm الخاصة بـ Amplify التي نحتاجها:

npm install --save aws-amplify @aws-amplify/ui-react

الآن يمكننا البدء في تعديل كود تطبيقنا. في ملف src/App.js، يمكننا إزالة كل شيء في قسم الرأس (headers) واستبداله بهذا الكود:

<header className= "App-header" >
  <AmplifySignOut />
  <h2> My App Content </h2>
</header>

هذا إعداد أساسي جدًا، ولكن يمكنك وضع المحتوى الرئيسي لموقعك هنا ووضع زر AmplifySignOut في أي مكان تريده. نحتاج أيضًا إلى إضافة بعض الاستيرادات (imports) الإضافية إلى أعلى الملف:

import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
import { AmplifySignOut, withAuthenticator } from '@aws-amplify/ui-react';

Amplify.configure(awsconfig);

الشيء الأخير الذي نحتاج إلى القيام به هو تغيير طريقة تصدير التطبيق. قم بتغيير السطر الأخير ليصبح export default withAuthenticator(App); لإضافة Amplify إلى هذا التطبيق. الآن عندما نقوم بتشغيل npm start، يجب أن نحصل على شاشة تسجيل دخول. لم نقم بإنشاء هذه الشاشة بأنفسنا، بل جاءت من Amplify نفسها.

شاشة تسجيل الدخول الافتراضية لتطبيق React مع AWS Amplify

إذا حاولنا تسجيل الدخول، فسيفشل ذلك، حيث نحتاج إلى التسجيل أولاً. يمكننا النقر على create account ثم إدخال بريدنا الإلكتروني وكلمة مرور للتسجيل. بمجرد تأكيد بريدنا الإلكتروني عن طريق إدخال الرمز الذي تم إرساله إلينا، ننتقل إلى الصفحة الرئيسية لتطبيقنا. إذا قمنا بتسجيل الخروج، يمكننا الآن تسجيل الدخول مرة أخرى كما هو متوقع.

الجزء الثاني: إضافة قاعدة بيانات والتعامل مع البيانات

إذا كنت ترغب في إضافة بيانات إلى تطبيق React الخاص بك ولكن لا تريد بناء واجهة برمجة تطبيقات (API) يدوياً، فهذا القسم مخصص لك. سنلقي نظرة على كيفية استخدام AWS Amplify داخل تطبيق React الخاص بنا للوصول إلى قاعدة بياناتنا في الواجهة الخلفية (backend) باستخدام GraphQL.

إعداد واجهة برمجة تطبيقات GraphQL

للبدء، نحتاج إلى الانتقال إلى نافذة الطرفية وتشغيل الأمر التالي:

amplify add api

سيؤدي هذا إلى بدء مجموعة من خيارات CLI، وسيسألك بعض أسئلة التكوين:

  • ما نوع API الذي نريد استخدامه: GraphQL
  • اسم API: songAPI
  • كيف نريد مصادقة API: Amazon Cognito User Pool
  • الإعدادات المتقدمة (Advanced Settings): No, I am done
  • هل لديك مخطط (schema): No
  • ما نوع المخطط الذي تريده: Single object with fields

خيارات AWS Amplify CLI لإضافة واجهة برمجة تطبيقات GraphQL

بعد إعداد بسيط، يُسأل عما إذا كنا نريد تعديل مخططنا الجديد. نريد أن نقول Yes. سيؤدي هذا إلى فتح مخطط GraphQL الذي سنقوم بتحديثه ليكون المخطط الموضح هنا:

type Song @model {
  id: ID!
  title: String!
  description: String!
  filePath: String!
  likes: Int!
  owner: String!
}

مع إعداد مخططنا، سنقوم بتشغيل amplify push الذي سيقارن إعداد Amplify الحالي لدينا مع الإعداد الموجود على حساب AWS الخاص بنا. نظرًا لأننا أضفنا API جديدًا، ستكون هناك تغييرات، لذلك سيُسأل عما إذا كنا نريد المتابعة مع التغييرات. بمجرد اختيار Yes، سننتقل إلى مجموعة أخرى من الخيارات:

  • هل نريد إنشاء كود لواجهة GraphQL API الخاصة بنا: Yes
  • اللغة: JavaScript
  • نمط الملفات للملفات الجديدة: src/graphql/ / .js
  • إنشاء جميع العمليات (Generate all operations): Yes
  • الحد الأقصى لعمق البيان (Maximum statement depth): 2

سيقوم هذا الآن بنشر جميع التغييرات على AWS وإعداد ملفات الطلبات الجديدة في تطبيق React الخاص بنا. تستغرق هذه العملية بضع دقائق. بمجرد اكتمال ذلك، يمكننا الانتقال إلى ملف App.js الخاص بنا وإعادة تسميته إلى App.jsx. هذا يجعل كتابة كود JSX أسهل.

جلب البيانات من قاعدة البيانات

نحتاج الآن إلى كتابة دالة هنا لجلب قائمة الأغاني من قاعدة بياناتنا الجديدة. تستدعي هذه الدالة واجهة GraphQL API وتمرر عملية listSongs. نحتاج أيضًا إلى إضافة حالة (state) جديدة إلى مكون App.

const [songs, setSongs] = useState([]);

const fetchSongs = async () => {
  try {
    const songData = await API.graphql(graphqlOperation(listSongs));
    const songList = songData.data.listSongs.items;
    console.log('song list', songList);
    setSongs(songList);
  } catch (error) {
    console.log('error on fetching songs', error);
  }
};

نحتاج الآن إلى إضافة أو تحديث بعض الاستيرادات إلى ملفنا لجعل هذا يعمل:

import React, { useState, useEffect } from 'react';
import { listSongs } from './graphql/queries';
import Amplify, { API, graphqlOperation } from 'aws-amplify';

الدالة listSongs هي إحدى تلك الدوال التي أنشأها Amplify لمساعدتنا في الوصول إلى بياناتنا. يمكنك رؤية الدوال الأخرى المتاحة في مجلد ./graphql. الآن نريد أن يتم استدعاء هذه الدالة مرة واحدة عند عرض المكون (component renders)، ولكن ليس في كل مرة يتم فيها إعادة عرضه. للقيام بذلك، نستخدم useEffect ولكن تأكد من إضافة معامل ثانٍ وهو [] بحيث يتم تشغيله مرة واحدة فقط.

useEffect(() => {
  fetchSongs();
}, []);

إذا بدأنا تطبيقنا الآن باستخدام npm start ثم انتقلنا إلى التطبيق، يمكننا فتح وحدة التحكم (console) ورؤية سجل song list []. هذا يعني أن useEffect قد استدعى fetchSongs الذي يقوم بتسجيل النتيجة في وحدة التحكم، ولكن لا يوجد حاليًا أي شيء في قاعدة البيانات.

إضافة بيانات يدوياً إلى DynamoDB

لتصحيح ذلك، نحتاج إلى تسجيل الدخول إلى حساب AWS الخاص بنا وإضافة خدمة DynamoDB. يجب أن نجد جدولًا جديدًا باسم مثل Song-5gq8g8wh64w-dev. إذا لم تتمكن من العثور عليه، فتأكد من التحقق من المناطق (regions) الأخرى أيضًا. لا يحتوي هذا الجدول حاليًا على بيانات، لذا نحتاج إلى إضافة بعضها. في الوقت الحالي، سنقوم بإنشاء بيانات جديدة يدويًا هنا. ضمن Items، انقر على Create item ثم تأكد من أن القائمة المنسدلة في أعلى اليسار تعرض text. إذا كانت تعرض tree، فما عليك سوى النقر عليها وتغييرها إلى text. يمكننا بعد ذلك إدخال البيانات في هذا الصف. نبدأ بمخطط GraphQL، مع إعطاء الصف بعض البيانات لكل سمة. ولكن نحتاج أيضًا إلى إضافة قيم createdAt و updatedAt. يمكنك العثور على ذلك باستخدام وحدة تحكم المتصفح. اكتب new Date().toISOString() وانسخ النتيجة. يجب أن ينتهي بك الأمر بكائن مثل هذا:

{
  "id": "gr4334t4tog345ht35",
  "title": "My First Song",
  "description": "A test song for our amplify app",
  "owner": "Sam Williams",
  "filePath": "",
  "likes": 4,
  "createdAt": "2020-08-13T07:01:39.176Z",
  "updatedAt": "2020-08-13T07:01:39.176Z"
}

إذا حفظنا هذا الكائن الجديد، يمكننا العودة إلى تطبيقنا وتحديث الصفحة. يجب أن نكون الآن قادرين على رؤية بياناتنا في console.log.

سجل وحدة التحكم يظهر البيانات التي تم جلبها من DynamoDB

عرض البيانات في واجهة المستخدم

يمكننا الآن استخدام هذه البيانات في تطبيقنا لعرض قائمة الأغاني التي حصلنا عليها للتو. استبدل النص الحالي song list بهذه المجموعة من JSX.

<div className= "songList" >
  {songs.map((song, idx) => {
    return (
      <Paper variant="outlined" elevation={2} key={`song${idx}`}>
        <div className="songCard">
          <IconButton aria-label="play">
            <PlayArrowIcon />
          </IconButton>
          <div>
            <div className="songTitle"> {song.title} </div>
            <div className="songOwner"> {song.owner} </div>
          </div>
          <div>
            <IconButton aria-label="like">
              <FavoriteIcon />
            </IconButton>
            {song.likes}
          </div>
          <div className="songDescription"> {song.description} </div>
        </div>
      </Paper>
    );
  })}
</div>

يقوم هذا الكود بالمرور على كل أغنية في القائمة وعرض مكون Paper جديد لها مع جميع التفاصيل التي نحتاجها. نحن نستخدم مكتبة MaterialUI للمساعدة في جعل هذا يبدو جيدًا لنا، لذا نحتاج إلى التأكد من تشغيل npm install --save @material-ui/core @material-ui/icons لتثبيت هذه الحزم ثم إضافتها إلى الاستيرادات في أعلى الملف أيضًا:

import { Paper, IconButton } from '@material-ui/core';
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
import FavoriteIcon from '@material-ui/icons/Favorite';

مع هذا، إذا حفظنا وأعدنا تحميل تطبيقنا، نحصل الآن على هذا:

قائمة الأغاني المعروضة في تطبيق React

بينما هذا جيد، يمكننا تحديث CSS لجعله يبدو أفضل بكثير. افتح ملف App.css الخاص بك وقم بتغييره إلى هذا:

.App {
  text-align: center;
}

.App-logo {
  height: 10vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 5vh;
  display: flex;
  align-items: center;
  justify-content: space-around;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

.songList {
  display: flex;
  flex-direction: column;
}

.songCard {
  display: flex;
  justify-content: space-around;
  padding: 5px;
}

.songTitle {
  font-weight: bold;
}

الآن نحصل على هذا المظهر – أفضل بكثير.

قائمة الأغاني بتصميم محسن بعد تطبيق CSS

الآن لدينا عنصر واحد في قاعدة البيانات، لذا نحصل على سجل واحد فقط. إذا عدنا إلى DynamoDB وأنشأنا عنصرًا جديدًا أو قمنا بتكرار العنصر الموجود، يمكننا رؤية كيف تبدو الأغاني المتعددة. يمكنك تكرار عنصر بالنقر فوق مربع الاختيار على يساره.

تحديث البيانات: إضافة وظيفة الإعجاب

الآن بعد أن أصبح بإمكاننا جلب البيانات، ماذا عن تحديث تلك المعلومات؟ لهذا، سنضيف القدرة على الإعجاب بأغنية. للبدء، يمكننا إضافة دالة onClick إلى زر الأيقونة الذي لدينا للإعجابات.

<IconButton aria-label= "like" onClick={() => addLike(idx)}>
  <FavoriteIcon />
</IconButton>

ربما تكون قد أدركت أن هناك خاصية idx لم نرها من قبل. هذا اختصار لـ index. حيث نقوم بـ songs.map، يمكننا تحديثه قليلاً للحصول على موضع كل عنصر في القائمة. يمكننا أيضًا استخدام هذا idx لإضافة مفتاح (key) إلى Paper ذي المستوى الأعلى في تلك الخريطة لإزالة أي أخطاء نحصل عليها من React.

{songs.map((song, idx) => {
  return (
    <Paper variant="outlined" elevation={2} key={`song${idx}`}>
      ...
    </Paper>
  )
})}

مع الفهرس الجديد واستدعاء دالة onClick، نحتاج الآن إلى إنشاء دالة addLike. تحتاج هذه الدالة إلى أخذ فهرس الأغنية للعثور على الأغنية الصحيحة وتحديث عدد الإعجابات. ثم تزيل بعض الحقول التي لا يمكن تمريرها إلى عملية updateSong قبل استدعاء تلك العملية.

const addLike = async idx => {
  try {
    const song = songs[idx];
    song.likes = song.likes + 1;
    delete song.createdAt;
    delete song.updatedAt;
    const songData = await API.graphql(graphqlOperation(updateSong, { input: song }));
    const songList = [...songs];
    songList[idx] = songData.data.updateSong;
    setSongs(songList);
  } catch (error) {
    console.log('error on adding Like to song', error);
  }
};

بمجرد تحديث الأغنية في قاعدة البيانات، نحتاج إلى الحصول على هذا التحديث مرة أخرى في حالتنا (state). نحتاج إلى استنساخ الأغاني الموجودة باستخدام const songList = [...songs]. إذا قمنا فقط بتغيير القائمة الأصلية للأغاني، فلن يقوم React بإعادة عرض الصفحة. مع قائمة الأغاني الجديدة هذه، نستدعي setSongs لتحديث حالتنا، وقد انتهينا من الدالة. نحتاج فقط إلى إضافة استيراد واحد آخر إلى أعلى الملف الذي نحصل عليه من المحوّلات (mutators) التي أنشأها Amplify:

import { updateSong } from './graphql/mutations';

الآن عندما ننقر على زر الإعجاب على أغنية، يتم تحديثها في الحالة وفي قاعدة البيانات.

تفاعل الإعجاب على الأغنية وتحديث عدد الإعجابات

الجزء الثالث: إضافة تخزين للملفات وتشغيلها

الآن بعد أن أصبح لدينا بيانات الأغاني مخزنة في DynamoDB، نريد تخزين بيانات ملفات MP3 الفعلية في مكان ما. سنقوم بتخزين الأغاني في S3 والوصول إليها باستخدام Amplify.

إضافة وظيفة التشغيل/الإيقاف المؤقت

للبدء، سنضيف طريقة لتشغيل وإيقاف كل أغنية مؤقتًا. في الوقت الحالي، سيؤدي هذا فقط إلى تغيير زر التشغيل إلى زر الإيقاف المؤقت. لاحقًا، سنستخدم طرق تخزين Amplify للحصول على ملف MP3 الخاص بنا من S3 وتشغيله مباشرة في تطبيقنا. سنضيف دالة جديدة إلى مكون App تسمى toggleSong.

const toggleSong = async idx => {
  if (songPlaying === idx) {
    setSongPlaying('');
    return;
  }
  setSongPlaying(idx);
  return
}

لجعل هذا يعمل، نحتاج أيضًا إلى إضافة متغير حالة جديد إلى التطبيق. سيتم استخدام هذا لتخزين فهرس الأغنية التي يتم تشغيلها حاليًا.

const [songPlaying, setSongPlaying] = useState('');

مع هذا الإعداد، نحتاج إلى إجراء تغيير على كود JSX لاستخدام دالتنا ومتغيراتنا الجديدة. ابحث عن الزر الأول في <div className="songCard">. سنضيف onClick يستدعي دالتنا الجديدة. سنستخدم أيضًا معادلة ثلاثية (ternary equation) للقول إنه إذا كانت الأغنية التي يتم تشغيلها هي هذه الأغنية، فاعرض أيقونة إيقاف مؤقت بدلاً من أيقونة تشغيل.

<IconButton aria-label= "play" onClick={() => toggleSong(idx)}>
  {songPlaying === idx ? <PauseIcon /> : <PlayArrowIcon />}
</IconButton>

سنحتاج فقط إلى استيراد PauseIcon في أعلى الملف وسنكون جاهزين.

import PauseIcon from '@material-ui/icons/Pause';

تغيير زر التشغيل إلى إيقاف مؤقت عند النقر

إضافة خدمة التخزين إلى Amplify

بعد ذلك، نحتاج إلى استخدام Amplify CLI لإضافة التخزين إلى تطبيقنا. يمكننا البدء بالانتقال إلى نافذة الطرفية وتشغيل amplify add storage. سيؤدي هذا إلى فتح واجهة CLI حيث نحتاج إلى تحديد الخيارات التالية:

  • الرجاء تحديد خدمتك: Content (images, audio, video, etc.)
  • اسم ودي لموردك: songStorage
  • اسم الحاوية (Bucket name): song-storage
  • من يجب أن يكون لديه حق الوصول: Auth Users Only
  • ما هو الوصول الذي يمتلكه هؤلاء المستخدمون: Read and Create/Update
  • هل تريد مشغل Lambda: No

بعد تكوين كل ذلك، نحتاج إلى نشره. يمكننا البدء بذلك باستخدام amplify push الذي سيستغرق بعض الوقت لمعرفة ما قمنا بتغييره في تطبيق Amplify الخاص بنا. ستحصل بعد ذلك على عرض صغير لجميع الموارد التي لدينا في Amplify. التغيير الوحيد هو إنشاء مورد songStorage الجديد الخاص بنا. يمكننا اختيار Yes للمتابعة ثم سيقوم بإنشاء حاوية S3 الخاصة بنا.

الوصول إلى ملفات S3 عبر طرق تخزين Amplify

بمجرد الانتهاء من النشر، يمكننا البدء في استخدامه للوصول إلى أغانينا. في دالة toggleSong الخاصة بنا، سنضيف بعض المنطق الإضافي.

const toggleSong = async idx => {
  if (songPlaying === idx) {
    setSongPlaying('');
    return;
  }

  const songFilePath = songs[idx].filePath;
  try {
    const fileAccessURL = await Storage.get(songFilePath, { expires: 60 });
    console.log('access url', fileAccessURL);
    setSongPlaying(idx);
    setAudioURL(fileAccessURL);
    return;
  } catch (error) {
    console.error('error accessing the file from s3', error);
    setAudioURL('');
    setSongPlaying('');
  }
};

يقوم هذا الكود بجلب مسار الملف (file path) من بيانات الأغنية (التي تم تخزينها في DynamoDB) ثم يستخدم Storage.get(songFilePath, { expires: 60 }); للحصول على عنوان URL للوصول إلى الملف. يشير { expires: 60 } في النهاية إلى أن عنوان URL الذي تم إرجاعه سيعمل لمدة 60 ثانية فقط. بعد ذلك، لن تتمكن من الوصول إلى الملف باستخدامه. هذا إجراء أمني مفيد حتى لا يتمكن الأشخاص من مشاركة عنوان URL مع آخرين لا ينبغي أن يكون لديهم حق الوصول إلى الملفات. بمجرد حصولنا على الملف، نقوم أيضًا بتعيين ذلك في متغير حالة جديد باستخدام setAudioURL. في أعلى تطبيق App الخاص بنا، نحتاج إلى إضافة هذه الحالة الإضافية.

const [audioURL, setAudioURL] = useState('');

احفظ هذا وعد إلى تطبيقنا. إذا ضغطنا على زر التشغيل وفتحنا وحدة التحكم، سنرى عنوان URL يتم تسجيله. هذا ما سنستخدمه لتشغيل الأغنية.

رفع الأغاني إلى S3 وتحديث البيانات

الآن وصلنا إلى النقطة التي نحتاج فيها إلى بعض الأغاني لتشغيلها. إذا ذهبنا إلى حساب AWS الخاص بنا وبحثنا عن S3 ثم بحثنا عن song، يجب أن نجد حاوية S3 الجديدة الخاصة بنا. هناك نحتاج إلى إنشاء مجلد جديد يسمى public. هذا لأن الملفات ستكون عامة لكل من قام بتسجيل الدخول إلى التطبيق. هناك طرق أخرى لتخزين البيانات تكون خاصة ولا يمكن عرضها إلا من قبل أشخاص محددين. داخل هذا المجلد، نحتاج إلى رفع أغنيتين. لدي أغنيتان خاليتان من حقوق الطبع والنشر قمت برفعهما باسم epic.mp3 و tomorrow.mp3. بمجرد رفعها، نحتاج إلى تحديث بيانات DynamoDB الخاصة بنا للإشارة إلى تلك الأغاني. في DynamoDB، يمكننا العثور على جدول Songs-(الكثير من الأحرف العشوائية) الخاص بنا. ضمن Items، يجب أن يكون لدينا سجلان. افتح الأول وغير filePath إلى tomorrow.mp3 والاسم إلى Tomorrow. احفظ ذلك وافتح الأغنية الثانية، وقم بتغييرها إلى "filePath": "epic.mp3" و "name": "Epic"، مع حفظ هذا الملف أيضًا. إذا استخدمت أغنياتك الخاصة، فتأكد فقط من أن filePath يطابق اسم ملفات أغانياتك. يمكننا الآن العودة إلى تطبيقنا، وتحديث الصفحة، والضغط على تشغيل إحدى الأغاني. إذا نظرنا في وحدة التحكم ونسخنا عنوان URL الذي تم إعطاؤنا إياه، يمكننا لصقه في متصفح ويجب أن تبدأ أغنيتنا بالتشغيل.

إضافة مشغل وسائط إلى تطبيقنا

الآن نريد أن نتمكن من تشغيل أغنيتنا فعليًا من داخل تطبيقنا. للقيام بذلك، سنستخدم مكتبة تسمى react-player. نحتاج أولاً إلى تثبيتها باستخدام npm install --save react-player. في تطبيقنا، يمكننا بعد ذلك استيرادها في أعلى الملف.

import ReactPlayer from 'react-player';

إذا وجدنا <div className="songCard"> الخاص بنا، فنحن نريد إضافة مشغلنا بعد هذا المكون. بنفس الطريقة التي عرضنا بها أيقونات التشغيل/الإيقاف المؤقت، سنقوم بإظهار أو إخفاء هذا المشغل بناءً على الأغنية التي يتم تشغيلها.

<div className= "songCard" >
  ..
  ..
</div>
{songPlaying === idx ? (
  <div className="ourAudioPlayer">
    <ReactPlayer url={audioURL} controls playing height="50px" onPause={() => toggleSong(idx)} />
  </div>
) : null}

يأخذ ReactPlayer بعض المعاملات. url هو ببساطة عنوان URL للملف المراد تشغيله، وهو الذي نحصل عليه من Amplify Storage. تتيح controls للمستخدم تغيير مستوى الصوت أو إيقاف الأغنية مؤقتًا. تعني playing أن الأغنية تبدأ بالتشغيل بمجرد تحميلها، و onPause هو مستمع (listener) حتى نتمكن من جعل زر الإيقاف المؤقت المدمج يعمل بنفس طريقة زر الإيقاف المؤقت الخاص بنا. مع كل هذا، يمكننا حفظ كل شيء مرة أخرى وفتح تطبيقنا. هناك، إذا ضغطنا على تشغيل إحدى الأغاني، يجب أن يظهر مشغل وتبدأ الأغنية بالتشغيل.

مشغل الوسائط المدمج في تطبيق React لتشغيل الأغاني

الجزء الرابع: تمكين تحميل المستخدمين للملفات

الآن لدينا تطبيق يسمح للمستخدمين بعرض جميع الأغاني وتشغيلها من S3. للبناء على ذلك، نريد السماح لمستخدمينا برفع أغانيهم الخاصة إلى المنصة.

إنشاء مكون إضافة أغنية

نحتاج أولاً إلى إنشاء طريقة لإضافة أغنية وإدخال بعض المعلومات. نبدأ بإنشاء زر Add.

{showAddSong ? <AddSong /> : <IconButton> <AddIcon /> </IconButton>}

ثم نحتاج أيضًا إلى إضافة AddIcon إلى استيراداتنا.

import AddIcon from '@material-ui/icons/Add';

الآن يمكننا الانتقال إلى إنشاء مكون AddSong الجديد. يمكننا إنشاء هذا في أسفل ملف App.jsx الخاص بنا.

const AddSong = () => {
  return (
    <div className="newSong">
      <TextField label="Title" />
      <TextField label="Artist" />
      <TextField label="Description" />
    </div>
  )
}

نحتاج أيضًا إلى إضافة TextField إلى استيراداتنا من Material UI.

import { Paper, IconButton, TextField } from '@material-ui/core';

الشيء التالي الذي يجب القيام به هو إضافة القدرة على فتح مكوننا الجديد عن طريق التحكم في متغير showAddSong. نحتاج إلى إنشاء إعلان حالة جديد بجانب الآخرين.

const [showAddSong, setShowAddNewSong] = useState(false);

يمكننا الآن تحديث زر AddIcon الجديد لتعيين showAddSong إلى true.

<IconButton onClick={() => setShowAddNewSong(true)}>
  <AddIcon />
</IconButton>

لتغييره مرة أخرى، يمكننا إضافة معامل إلى مكون AddSong الخاص بنا يسمى onUpload. عندما يتم استدعاء هذا، سنقوم بإعادة تعيين showAddSong إلى false.

<AddSong onUpload={() => {
  setShowAddNewSong(false);
}} />

ثم نحتاج إلى تحديث مكوننا للعمل مع هذا المعامل الجديد وزر لـ "رفع" الأغنية الجديدة. يستدعي هذا الزر دالة في المكون حيث سنضيف القدرة على رفع البيانات، ولكن في الوقت الحالي سنقوم فقط باستدعاء دالة onUpload.

const AddSong = ({ onUpload }) => {
  const uploadSong = async () => {
    //Upload the song
    onUpload();
  };

  return (
    <div className="newSong">
      <TextField label="Title" />
      <TextField label="Artist" />
      <TextField label="Description" />
      <IconButton onClick={uploadSong}>
        <PublishIcon />
      </IconButton>
    </div>
  );
};

والآن نضيف PublishIcon إلى استيراداتنا ونكون جاهزين لاختبار هذا.

import PublishIcon from '@material-ui/icons/Publish';

عندما نبدأ التطبيق ونسجل الدخول، نحصل الآن على أيقونة زائد. بالنقر عليها، يمكننا إدخال بعض التفاصيل للأغنية والنقر على رفع.

شاشة إضافة أغنية جديدة في تطبيق React
نموذج إدخال تفاصيل الأغنية مع زر الرفع

تحديث مكون AddSong لجمع البيانات

الآن نريد أن نتمكن من تخزين والوصول إلى البيانات التي يدخلها المستخدم في الحقول عند إضافة أغنية.

const AddSong = ({ onUpload }) => {
  const [songData, setSongData] = useState({});

  const uploadSong = async () => {
    //Upload the song
    console.log('songData', songData);
    const { title, description, owner } = songData;
    onUpload();
  };

  return (
    <div className="newSong">
      <TextField label="Title" value={songData.title} onChange={e => setSongData({ ...songData, title: e.target.value })} />
      <TextField label="Artist" value={songData.owner} onChange={e => setSongData({ ...songData, owner: e.target.value })} />
      <TextField label="Description" value={songData.description} onChange={e => setSongData({ ...songData, description: e.target.value })} />
      <IconButton onClick={uploadSong}>
        <PublishIcon />
      </IconButton>
    </div>
  );
};

لقد قمنا أيضًا بتغيير جميع حقول TextField لتكون متحكمًا بها (controlled components)، حيث نمرر قيمة من حالتنا ونوفر دالة onChange أيضًا. إذا حفظنا هذا وحاولنا إدخال بعض التفاصيل قبل الرفع، يجب أن نرى console.log للتفاصيل في وحدة تحكم Chrome الخاصة بنا.

رفع ملف الأغنية إلى S3 وإنشاء سجل في قاعدة البيانات

بعد ذلك، نحتاج إلى إضافة القدرة على رفع الأغنية فعليًا. لهذا، سنستخدم إدخال HTML الافتراضي بنوع file. أضف هذا إلى JSX قبل زر أيقونة الرفع مباشرة.

<input type="file" accept="audio/mp3" onChange={e => setMp3Data(e.target.files[0])} />

كما لاحظت، نحن نستدعي setMp3Data عند التغيير. هذه حالة إضافية في مكون AddSong.

const [mp3Data, setMp3Data] = useState();

الآن بعد أن أصبح لدينا جميع البيانات التي نحتاجها، يمكننا البدء برفع الأغنية إلى S3 ثم البيانات إلى قاعدة بياناتنا. لرفع الأغنية، سنستخدم فئة Amplify Storage مرة أخرى. سيكون اسم الملف UUID، لذا نحتاج أيضًا إلى تشغيل npm install --save uuid في نافذة الطرفية ثم استيرادها في أعلى ملفنا import { v4 as uuid } from 'uuid';. ثم نمرر mp3Data و contentType ونحصل على كائن بمفتاح (key).

const { key } = await Storage.put(` ${uuid()} .mp3`, mp3Data, {
  contentType: 'audio/mp3'
});

الآن بعد أن أصبح لدينا المفتاح، يمكننا إنشاء سجل للأغنية في قاعدة البيانات. نظرًا لأنه قد تكون هناك أغاني متعددة بنفس الاسم، سنستخدم UUID كمعرف (ID) مرة أخرى.

const createSongInput = {
  id: uuid(),
  title,
  description,
  owner,
  filePath: key,
  likes: 0,
};

await API.graphql(graphqlOperation(createSong, { input: createSongInput }));

لجعل هذا يعمل، نحتاج إلى استيراد محول createSong الذي تم إنشاؤه عندما أنشأنا تخزين DynamoDB باستخدام Amplify.

import { updateSong, createSong } from './graphql/mutations';

الشيء الأخير الذي نحتاج إلى القيام به هو جعل التطبيق يعيد جلب البيانات من قاعدة البيانات بمجرد الانتهاء من رفعها. يمكننا القيام بذلك عن طريق إضافة استدعاء fetchSongs كجزء من دالة onUpload.

<AddSong onUpload={() => {
  setShowAddNewSong(false);
  fetchSongs();
}} />

الآن عندما نعيد تحميل الصفحة، يمكننا النقر لإضافة أغنية جديدة، وإدخال التفاصيل، وتحديد أغنيتنا الجديدة، ورفعها ثم تشغيلها من التطبيق. هذا كل شيء!

الخلاصة التقنية

لقد استعرضنا في هذا الدليل الشامل كيفية بناء تطبيق ويب متكامل وظيفي بالكامل باستخدام AWS Amplify و React. من خلال الاستفادة من قوة Amplify CLI، تمكنا من تبسيط عمليات إعداد المصادقة (Cognito)، وقواعد البيانات (DynamoDB المدعومة بـ GraphQL API)، وتخزين الملفات (S3). أظهرنا كيف يمكن للمطورين التركيز على تجربة المستخدم وواجهة التطبيق (React و Material-UI) بينما يتولى Amplify إدارة البنية التحتية الخلفية المعقدة. هذه المرونة والكفاءة تجعل Amplify خيارًا ممتازًا لتسريع دورات التطوير وتقديم ميزات قوية مثل رفع الملفات وتشغيل الوسائط بسهولة. إن القدرة على دمج هذه الخدمات بسلاسة تفتح آفاقًا واسعة لبناء تطبيقات قابلة للتوسع وموثوقة.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *