Tag: react native

Cara Menggunakan SQLite di React Native

Cara Menggunakan SQLite di React Native

Halo semuanya, seneng banget saya bisa posting kembali secara berturut-turut. Hari ini kembali pada konten codecast kita, kita akan sharing cara menggunakan sqlite react native.

Masih di kondisi yang sama, pandemi COVID-19 belum juga selesai. Protokol 3M masih terus dicanangkan untuk memutus rantai. Namun, kelihatannya temen-temen sudah malas hmmm….

Dah, kita kembali pada topik utama. Sebelum kita memasuki gerbang utama dari artikel ini: menggunakan sqlite react native, kita bisa tau dahulu apa itu SQLite dan bagaimana cara kerjanya, serta penggabungannya dengan React Native.

Menggunakan SQLite React Native: Apa itu SQLite?

Logo SQLite CodeSeem
SQLite CodeSeem

Kalo dari Wikipedia, sumber informasi yang agaknya cukup kredibel menurut masyarakat internet, SQLite adalah

SQLite is a relational database management system contained in a C library. In contrast to many other database management systems, SQLite is not a client–server database engine. Rather, it is embedded into the end program.

Wikipedia

Nah, singkatnya dia itu RDBMS yang sistem dan database-nya ditempelkan langsung pada program utama (tidak terpisah seperti MySQL dkk). Dengan begini, kita tidak perlu memiliki DB server yang terpisah lagi.

Dikarenakan kita tidak memiliki DB server lagi, pengimplementasian SQLite pada aplikasi (apalagi pada aplikasi mobile), sering disebut sebagai local storage / offline storage. Kenapa kacang? (why nut?), karena tidak adanya konektivitas dan pertukaran data keluar menuju DB server.

Bagaimana Cara Kerja SQLite?

Cara Kerja SQLite dibanding RDBMS lainnya CodeSeem
Flow SQLite pada aplikasi kita dibanding RDBMS lainnya

Nah, seperti yang telah dijelaskan pada definisinya di atas, kita sudah tau bahwa SQLite ditempelkan pada program utama. Jadi, alih-alih terpisah servernya, sistem SQLite dan file DB-nya justru menempel pada program utama.

Sistem SQLite terpasang pada aplikasi kita, file DB-nya juga dibuat di internal aplikasi kita, proses query data juga kita lakukan pada aplikasi kita. Query language yang digunakan tentu bisa kita tebak. Yapp, kita menggunakan SQL untuk query data.

Tapi, jangan samakan SQLite dengan RDBMS berbasis SQL lainnya. Mengapa ada kata lite pada SQLite? Karena tidak seluruh fitur RDBMS berbasis SQL berada pada SQLite. Contohnya saja, tipe data pada SQLite lebih simpel dari RDBMS berbasis SQL lainnya. Stored procedure juga tidak tersedia.

Terbatasnya fitur SQLite menurut saya bukan tanpa alasan. Sebagai embedded system, pastinya akan menjadi berat kalau seluruh fitur dari RDBMS pada umumnya diterapkan juga disana. Jadi, menurut saya sah-sah saja ya.

Implementasi SQLite pada React Native

Nah, kita masuk pada topik utama kita. Setelah kita tau apa itu SQLite dan bagaimana cara kerjanya, sekarang kita bisa mengetahui cara implementasinya pada React Native Application.

Untuk dapat mengimplementasi SQLite pada projek React Native, kita perlu bantuan library react-native-sqlite-storage. Salah satu library SQLite untuk React Native paling stabil dan populer.

Menggunakan sqlite react native: Menginisiasi Project

Pertama, kita harus menginisiasi projek React Native dahulu. Dalam hal ini saya menggunakan react-native init

npx react-native init codeseemrnsqlite

Setelah itu, kita bisa langsung menginstall dependency yang diperlukan yakni react-native-sqlite-storage.

cd codeseemrnsqlite && npm install --save react-native-sqlite-storage

Untuk React Native versi 0.60+, kita bisa langsung build supaya fitur Auto linking dari React Native berjalan.

npx react-native run-android

Setelah itu, akan nampil tampilan default dari React Native di emulator/device kita seperti dibawah ini.

React Native Welcoming Screen Android CodeSeem

Kemudian, kita modifikasi file App.js supaya memiliki fungsi menampilkan dan menambahkan list data dari state.

/**
 * CodeSeem SQLite React Native Application
 */
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet, TextInput, ScrollView, Alert } from 'react-native';

const App = () => {
  const [value, setValue] = useState('');
  const [listData, setListData] = useState([]);

  const handleAddData = () => {
    if (value) {
      let newListData = listData;
      newListData.push(value);

      setListData(newListData);
      setValue("");
    } else {
      Alert.alert('Info', 'Mohon masukkan data yang benar!')
    }
  }

  return (
    <View style={styles.container}>
      <Text>React Native SQLite</Text>
      <View style={styles.textInputWrapper}>
        <TextInput onChangeText={text => setValue(text)} value={value} placeholder="Masukkan data" style={styles.textInput} />
        <View style={{ margin: 10 }}>
          <Button onPress={handleAddData} title="Add Data" />
        </View>
      </View>
      <Text>List Data</Text>
      <ScrollView>
        {
          listData.map(item => {
            return <Text key={item}>{item}</Text>
          })
        }
      </ScrollView>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20
  },
  textInputWrapper: {
    flexDirection: 'row'
  },
  textInput: {
    flex: 1,
    paddingVertical: 0,
    marginVertical: 10,
    borderWidth: 1,
    borderColor: 'black'
  }
});

export default App;

Code di atas akan menampilkan tampilan seperti di bawah ini. Sudah memiliki fungsi untuk menambah dan menampilkan data, hanya saja belum kita integrasikan dengan SQLite.

Tampilan awal App.js React Native dengan Add Data menggunakan State CodeSeem
Tampilan awal App.js dengan Add Data menggunakan State

Tahan dulu bro, coba liat artikel ini juga da
Cara membuat Read More di WhatsApp menggunakan JavaScript

CodeSeem

Menambahkan Fungsi SQLite

Untuk sharing kali ini, saya menggunakan opsi ‘open pre-populated database’. Jadi, di awal saya membuat file SQLite dahulu. Saya menggunakan Navicat untuk membuat file db SQLite baru.

File database dengan nama codeseem.db yang memiliki satu table ms_user dua kolom (user_id dan user_fullname).

Struktur database SQLite codeseem.db dengan table ms_user
Struktur Database codeseem.db dan Table ms_user

Setelah kita membuat database tersebut, kita bisa langsung membuat direktori baru di project kita. Untuk Android, tambahkan folder baru bernama www di android/app/src/main/assets/www. Kemudian masukkan db yang tadi dibuat ke folder tersebut.

Lokasi database SQLite pre-populated android CodeSeem
Lokasi Database Pre-populated

Setelah itu, kita bisa memodifikasi file App.js supaya bisa loading database kita.

//Tambahkan line ini di awal
import { openDatabase } from 'react-native-sqlite-storage';

var db = openDatabase({name:'codeseem.db', createFromLocation:1}, () => {
  Alert.alert('Info', 'Sukses loading database SQLite');
}, (err) => {
  console.log(err)
})

Disclaimer! Temen-temen harus ingat bahwa file codeseem.db tadi berada di folder android. Apapun yang berubah di folder tersebut, tidak akan terlihat perubahannya pada app, kecuali kita build ulang. Maka kita harus build ulang dengan command dibawah.

npx react-native run-android

Menambah Insert data ke SQLite pada handleAddData

Setelah aplikasi launch, temen-temen seharusnya sudah bisa melihat Alert yang dihasilkan karena callback function di atas. Kalau sudah muncul, berarti database sudah berhasil ter-load.

Kita masih punya pr lagi. Sekarang saatnya kita mengganti function handleAddData supaya bisa menambah dan mengurangi data di database.

const handleAddData = () => {
    if (value) {
      db.transaction((tx) => {
        tx.executeSql(
          'INSERT INTO ms_user (user_fullname) VALUES (?);',
          [value],
          (txn, res) => {
            if (res.rowsAffected > 0) {
              let newListData = listData;
              newListData.push(value);

              setListData(newListData);
              setValue("");
            }
          },
          (err) => {
            console.log(err);
          }
        )
      })
    } else {
      Alert.alert('Info', 'Mohon masukkan data yang benar!')
    }
  }

Sedikit penjelasan, variable db yang sebelumnya sudah kita inisiasi di atas, sekarang kita gunakan untuk memulai transaction. Kemudian fungsi transaction mengembalikan callback function dengan prameter tx yang kita panggil untuk executeSql.

Dalam executeSql, kita memasukkan Insert Statement SQL dengan query binding. Setelah itu, dalam callback function success, kita tambahkan logic untuk memeriksa apakah berhasil insert dengan rowsAffected > 0.

Barulah kita taruh kembali setListData di dalam logic tersebut. Walaupun sudah masuk database, setelah kita keluar dari aplikasi dan masuk kembali, data yang ditampilkan tetap kosong, padahal kita sudah menambahkan data sebelumnya.

Data kembali kosong padahal SQLite telah berhasil terisi CodeSeem
Data Kosong padahal sudah diinsert.

Kenapa itu seperti mati? (Why is it like that?) Kalau temen-temen jeli, dalam map function di JSX, kita mengambil data dari variable listData. Padahal listData berasal dari useState dengan value kosong di awal. Maka tinggal kita tambahkan useEffect dengan argumen pertama sebuah function untuk fetch data dari SQLite dan argumen kedua sebuah array kosong [], supaya function bisa berjalan sekali ketika aplikasi dimuat.

Memodifikasi App.js supaya load data SQLite di awal

//Ganti line ini
import React, { useState } from 'react';

//dengan ini
import React, { useState, useEffect } from 'react';
//Tambahkan useEffect diatas return function
useEffect(() => {
    db.transaction((tx) => {
      tx.executeSql(
        'SELECT * FROM ms_user;',
        [],
        (txn, res) => {
          if (res.rows.length > 0) {
            let count = res.rows.length;
            let newListData = [];

            for (let index = 0; index < count; index++) {
              const element = res.rows.item(index);
              newListData.push(element.user_fullname);
            }

            setListData(newListData);
          }
        },
        (err) => {
          console.log(err);
        }
      )
    })
  }, []);

return (
...

Setelah temen-temen tambahkan useEffect, maka temen-temen akan mendapatkan hasil yang terisi di awal. Contohnya seperti di bawah ini.

Data sudah terload menggunakan useEffect dari SQLite CodeSeem
Data yang diload dari database

Sampai sini sebetulnya sudah selesai. Temen-temen bisa menambahkan data baru berdasarkan state dan juga update via SQLite. Hanya saja ada yang mau saya tambahkan. Temen-temen bisa lihat ada warning yang menunjukkan bahwa React menemukan dua element children dengan key yang sama. Ini terjadi karena pada map function, saya menggunakan value name sebagai key. Seperti di bawah ini.

listData.map(item => {
  return <Text key={item}>{item}</Text>
})

Padahal, di database kita memiliki column unique dengan auto-increment-nya. Column tersebut adalah user_id. Kita bisa memanfaatkan column tersebut sebagai key. Kita akan melakukan perubahan pada 3 tempat yakni, handleAddData, useEffect, dan map function. Perubahan tersebut adalah dengan merubah state listData yang tadinya menerima array of text, menjadi array of object. Sehingga object akan terlihat seperti dibawah ini.

{
  id: 1,
  name: 'John'
}

handleAddData

const handleAddData = () => {
    if (value) {
      db.transaction((tx) => {
        tx.executeSql(
          'INSERT INTO ms_user (user_fullname) VALUES (?);',
          [value],
          (txn, res) => {
            if (res.rowsAffected > 0) {
              let newListData = listData;
              let insertID = res.insertId;
              newListData.push({
                id: insertID, //memanfaatkan ID unique dari column user_id
                name: value
              });

              setListData(newListData);
              setValue("");
            }
          },
          (err) => {
            console.log(err);
          }
        )
      })
    } else {
      Alert.alert('Info', 'Mohon masukkan data yang benar!')
    }
  }

useEffect

useEffect(() => {
    db.transaction((tx) => {
      tx.executeSql(
        'SELECT * FROM ms_user;',
        [],
        (txn, res) => {
          if (res.rows.length > 0) {
            let count = res.rows.length;
            let newListData = [];

            for (let index = 0; index < count; index++) {
              const element = res.rows.item(index);
              newListData.push({
                id: element.user_id, //memanfaatkan value dari database, column user_id
                name: element.user_fullname //memanfaatkan value dari database, column user_fullname
              });
            }

            setListData(newListData);
          }
        },
        (err) => {
          console.log(err);
        }
      )
    })
  }, []);

map function

listData.map(item => {
  return <Text key={item.id}>{item.name}</Text>
})

Setelah kita rubah, tidak ada lagi warning karena seluruh element children memiliki key yang unique.

Ending

Baik temen-temen, kita telah mempelajari cara menggunakan sqlite react native Setelah ini, temen-temen bisa banyak explore tentang hal-hal yang berhubungan dengan SQLite seperti mungkin Update Statement, Delete Statement atau juga membuat View dan Table.

Semoga seluruh isi artikel ini bisa bermanfaat buat temen-temen. Kurang lebihnya mohon dimaafkan. Semoga kita selalu sehat dan diberikan keberkahan hidup. Aamiin.

Link github dibawah ini bous:
Github CodeSeem React Native SQLite

As always, i’ll show you a good quote.

“Jika aku belajar hanya untuk penghidupan, maka aku akan hidup.. namun jika aku belajar untuk mengamalkannya maka aku akan selalu hidup.”

― Hilaludin Wahid, seorang pecinta puisi Indonesia

Enjoy, keep learning…

Cara Membuat WhatsApp Baca Selengkapnya Read More dengan JavaScript

Cara Membuat WhatsApp Baca Selengkapnya Read More dengan JavaScript

Halo semuanya, senang sekali bisa posting lagi hari ini. Kita akan membuat whatsapp baca selengkapnya read more dengan menggunakan JavaScript.

Minggu-minggu dan bulan-bulan terakhir ini cukup terasa lebih longgar dari sebelumnya. Jadi, sudah sedikit demi sedikit terasa agak ‘normal’. Walaupun kenaikan kasus masih saja ada, tetapi kita tetap mematuhi 3M juga, yakni Masker, Mencuci Tangan, dan Menjaga jarak.

Nah, di sela-sela aktivitas, saya sempetin untuk buat tutorial yang lagi hype banget nih. Bikin WhatsApp Read More atau Baca Selengkapnya. Kalo temen-temen udah tau, itu sebenernya ada cara gampangnya ya heuehu. Cuma, as a programmer ya ada hal baru yang bisa dipelajari kenapa kacang? (baca: kenapa tidak).

Maka dari itu, hari ini akan saya share cara bikin WhatsApp Read More atau Baca Selengkapnya menggunakan JavaScript. Tidak ada library yang digunakan kok alias VanillaJS.

Demo WhatsApp Read More Baca Selengkapnya dengan JavaScript

Yuk ah langsung aja dimulai gimana cara bikinnya

Requirement Membuat whatsapp baca selengkapnya

  • Laptop yang capable buat coding HTML + JS
  • Code Editor (disini saya pakai VSCode)
  • Baca ini dulu kalo bisa : Combining Grapheme Joiner

Let’s start

Flowchart cara kerja sistem membuat whatsapp baca selengkapnya

Pertama, kita harus tau bagaimana sih cara kerja dari sistem ini. Jadi, pada dasarnya WhatsApp akan memberikan teks “Baca Selengkapnya” atau “Read More” pada obrolan kita, jika karakter dalam teks yang kita kirimkan sudah terlalu banyak dan panjang.

Maka dari itu, flowchart akan seperti dibawah ini.

Flowchart dari WhatsApp Read More Generator System

Nah, pada dasarnya si unicode character Combining Grapheme Joiner (CGJ) atau pada HTML ditulis &#847; adalah sebuah karakter yang “diabaikan” dan tidak ditampilkan oleh aplikasi (dalam hal ini WhatsApp). Walaupun begitu, WhatsApp tetap menghitung character CGJ dalam chat.

Sampai sini sudah mulai mengerti yaa? Jadi kita menggunakan “banyak” CGJ ini sebagai tambahan diantara teks awal dan teks akhir.

Baca ini juga dong guys
Cara membuat Face Detection menggunakan JavaScript

CodeSeem

Implementation: Coding Section

Nah setelah tau konsep dasarnya, kita bisa langsung menuju kepada implementasinya. Sudah tergambar betul gimana codingan-nya sepertinya haha

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CodeSeem jQuery WhatsApp Read More Demo</title>
</head>
<body>
    <form>
        <input type="text" name="awal" id="shownText">
        <input type="text" name="akhir" id="hiddenText">
        <input type="button" onclick="generate()" value="Submit">
    </form>
    <br>
    <p id="freview" style="display:block;">Hasilnya akan muncul disini..</p>
    <div id="fenrslt">
        <pre id="theResult" contenteditable=""></pre>
    </div>
    <script src="./scripts/index.js"></script>
</body>
</html>

scripts/index.js

function generate() {
    var shownText = document['getElementById']('shownText')['value'];
    var hiddenText = document['getElementById']('hiddenText')['value'];
    var textContent = '&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;&#847;';
    document['getElementById']('theResult')['innerHTML'] = shownText + textContent + hiddenText;
    document['getElementById']('fenrslt')['style']['display'] = 'block';
    document['getElementById']('freview')['style']['display'] = 'none'
}

Penjelasan Codingan

Pada file index.html kita bisa melihat element input dan juga sebuah submit button. Tiap kolom input memiliki ID yang kemudian nantinya bisa diakses di JavaScript.

File index.js tidak kalah pentingnya karena memiliki line yang sangat berguna. Berikut ini line yang pentingnya.

document['getElementById']('theResult')['innerHTML'] = shownText + textContent + hiddenText;

Nah, line diatas memiliki fungsi untuk mengganti value dari result menjadi hasil append variable (dalam hal ini teks awal, teks kosong/CGJ, dan teks akhir).

Setelah kode diluncurkan, maka hasilnya akan terlihat dengan jelas. Copy hasil yang ada di kolom bawah ke WhatsApp. Maka akan muncul hasil seperti dibawah ini.

WhatsApp Read More Baca Selengkapnya CodeSeem

Ending

Oke setelah kita berhasil membuat whatsapp baca selengkapnya, kita telah mempelajari banyak hal. Dari sifat salah satu Unicode (dalam hal ini CGJ) dan sifat aplikasi WhatsApp yang meng-ignore karakter tersebut untuk bisa terlihat di chat.

Untuk yang menginginkan source code, tersedia di github berikut
https://github.com/alviankosim/codeseem-whatsapp-read-more

Saya harap temen-temen bisa mendapatkan manfaat dari postingan ini. Semoga kita selalu dalam keberkahan dan kelancaran hidup juga terbebas dari mara bahaya dari COVID dan apapun. Aamiin.

Tengok bentar lah video ini

As always, i’ll show you a good quote

“Mengajar adalah semacam pertunjukan yang harus menarik.”

― Helvy Tiana Rosa

Helvy Tiana Rosa, penulis dari Indonesia kelahiran Medan

Enjoy, keep learning…

Cara Membuat Simple Authentication Login React Native AsyncStorage 2020

Cara Membuat Simple Authentication Login React Native AsyncStorage 2020

Halo semuanya, senang sekali bisa posting lagi hari ini. Kita akan membuat login authentication react native. Minggu karantina yang diupgrade jadi PSBB ini alias Pembatasan Sosial Berskala Besar bikin saya dan mungkin kalian juga bosen.

Karena virus covid19 ini, kita jadi harus ngelakuin social distancing yang mana gaboleh keluar rumah kalo gak penting-penting banget. Semoga saja wabah ini cepat berlalu.

Nah, disitulah saya merasa bosan karena setelah WFH (work from home) selesai, maka kegiatan saya tinggal tidur dan istirahat. Disitulah saya berfikir.

Kenapa saya tidak buat tutorial Login Authentication di React Native dengan AsyncStorage? Bukankah itu suatu hal yang bagus juga untuk diposting.

Login Authentication kalo di internet artinya

the process or action of proving or showing something to be true, genuine, or valid.

Yah intinya sih proses validasi kalo dia punya akses gitu. Nah kali ini kita akan implementasiin secara local. Ada juga yang secara online contohnya pakai Firebase. Tapi, kali ini saya akan nunjukin lebih kepada pemakaian AsyncStorage-nya.

Disclaimer

Cara yang saya jelaskan berikut hanya sebagai dasar bagaimana proses proses autentikasi di React Native dengan React Navigation v5 bekerja menggunakan AsyncStorage. Untuk lebih baiknya kalian bisa mengimplementasikannya dengan Context, Reducer, atau bahkan Redux/Mob.

Kemudian untuk versi React Native akan terus naik dan berkembang seiring berjalannya waktu. Versi library/dependency yang digunakan juga akan berkembang juga. Maka sesuaikan jika berbeda dari yang dipaparkan disini. Adapun versi yang digunakan pada tutorial ini dibuat

  • react-native v0.62.2
  • @react-native-community/async-storage v1.9.0
  • React Navigation v5.1.5

Maka kita mulai saja ya caranya.

Let’s start

Buat project baru React Native

Pertama, kita buat project baru untuk react native login ini. Caranya dengan menggunakan perintah dibawah ini. Kemudian tunggu hingga proses selesai.

npx react-native init projectlogin

Oke, setelah proses pembuatan project baru telah selesai, kita install beberapa dependency untuk tutorial kali ini.

Struktur Direktori

Struktur direktori dari project ini bisa dilihat di bawah ini yaa

projectlogin/
- android/
- ios/
- src/
-- screens/
--- loading.js
--- login.js
--- home.js
- App.js

Tampilan Awal Aplikasi

Kita coba untuk menjalankan project baru ini dengan perintah.

npx react-native run-android //Untuk android
npx react-native run-ios //Untuk IOS / iPhone
React Native 0.62.2 Android CodeSeem cara buat login authentication asyncstorage

Jika sudah bisa muncul tampilan awal aplikasi React Native kita, maka kita bisa mulai ke tahap edit beberapa file. Kita mulai edit file App.js

App.js

import 'react-native-gesture-handler';
import React, {useEffect, useState} from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import AsyncStorage from '@react-native-community/async-storage';

//importing screens
import LoginScreen from './src/screens/login';
import HomeScreen from './src/screens/home';
import LoadScreen from './src/screens/loading';

const Stack = createStackNavigator();
const App = () => {
  
  const [foundToken, setFoundToken] = useState('');
  const [isLoad, setIsLoad] = useState(true);

  const checkToken = async () => {
    try {
      let findingToken = await AsyncStorage.getItem('token');
      setFoundToken(findingToken);
      setIsLoad(false);
    } catch (error) {
      console.log(error);
    }
  }

  const loginAction = async () => {
    //Proses post login form untuk mendapat token/ semacamnya
    let dummyToken = 'CodeSeemToken';

    try {
        await AsyncStorage.setItem('token', dummyToken);
        setFoundToken(dummyToken);
    } catch (error) {
        console.log(error);
    }
  }

  const logoutAction = async () => {
    try {
        await AsyncStorage.removeItem('token');
        setFoundToken('');
    } catch (error) {
        console.log(error);
    }
  }

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

  return (
    <NavigationContainer>
      <Stack.Navigator>
        {
          foundToken
          ?<Stack.Screen name="Home">
            {props => <HomeScreen {...props} logout={logoutAction} />}
          </Stack.Screen>
          :(isLoad
           ?<Stack.Screen 
              name="Load"
              options={{headerShown:false}}>
              {props => <LoadScreen {...props}/>}
            </Stack.Screen>
           :<Stack.Screen name="Login">
              {props => <LoginScreen {...props} login={loginAction} />}
            </Stack.Screen>
          )
        }
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

Yuk baca juga, gakalah menarik
Membuat Realtime Face Detection dengan Javascript

CodeSeem

Sedikit penjelasan

Di kode di bawah ini, saya menambahkan state variable agar bisa berpindah-pindah antar screen jika terjadi proses login/logout.

Variabel foundToken ditujukan untuk tempat token yang nantinya kita simpan di AsyncStorage.

Kemudian, variabel isLoad dibuat karena proses pengambilan token dari AsyncStorage asynchronous, maka fungsinya untuk membantu kita mendeteksi apakah proses pengambilan token sudah selesai.

const [foundToken, setFoundToken] = useState('');
const [isLoad, setIsLoad] = useState(true);

Selanjutnya, fungsi checkToken adalah fungsi asynchronous yang mengecek dahulu apakah sudah ada token yang disimpan atau belum.

Dalam fungsi ini juga dilakukan set value pada variabel isLoad dan foundToken. Karena variabel ini berubah, maka komponen akan merender ulang screen.

const checkToken = async () => {
......
}

Penjelasan Proses Login dan Logout

Kita masuk ke fungsi loginAction dan logoutAction. Fungsi ini dari namanya tentu sudah jelas untuk memproses login/logout. Dalam fungsi ini dilakukan proses set/unset token dari AsyncStorage.

const loginAction = async () => {
......
}

const logoutAction = async () => {
......
}

Tentunya proses login/logout dalam aplikasi live/production tidak akan sesimpel itu. Untuk latihan mungkin ini sudah cukup.

Bagian ini cukup menarik, karena kita memanggil fungsi useEffect dengan parameter keduanya empty array. Berguna untuk menjalankan fungsi dalam parameter pertama sekali ketika komponen dimuat.

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

Kita memanggil function checkToken untuk melakukan pengecekan pertama kali apakah sudah terlogin atau belum.

Bagian Return App.js

Kita masuk pada bagian return dari App.js, terdapat 3 screen yang ada didalam masing-masing kondisi. Kondisinya juga menggunakan state variable foundToken dan isLoad.

return (
    <NavigationContainer>
      <Stack.Navigator>
        {
          foundToken
          ?<Stack.Screen name="Home">
            .....
          </Stack.Screen>
          :(isLoad
           ?<Stack.Screen  name="Load" options={{headerShown:false}}>
              ....
            </Stack.Screen>
           :<Stack.Screen name="Login">
              ....
            </Stack.Screen>
          )
        }
      </Stack.Navigator>
    </NavigationContainer>
  );

Hal itulah yang menjadi kunci screen apa yang kita tampilkan pada user ketika belum login, sudah login, atau sedang loading token.

Penjelasan Bagian Setiap Screen

Kita masuk ke file loading.js. Screen ini ditampilkan ketika foundToken memiliky value falsy karena default useState adalah ‘ ‘ dan isLoad true alias masih dalam proses loading token .

Screen ini hanya menampilkan tulisan loading ditengah dengan background gelap tanpa header. Tujuannya hanya untuk menandakan bahwa proses pengambilan token sedang berlangsung.

src/screens/loading.js

import React from 'react';
import {View, Text} from 'react-native';

const Loading = () => {

    return (
        <View style={{flex:1, backgroundColor:'#333', justifyContent:'center', alignItems:'center'}}>
            <Text style={{color:'white'}}>Loading...</Text>
        </View>
    );
}

export default Loading;

Kemudian kita masuk ke file login.js. Screen ini ditampilkan ketika value foundToken masih juga falsy alias belum login setelah pengecekan karena isLoad sudah menjadi false.

Coba perhatikan, screen ini menerima props login yang isinya adalah function loginAction. Props ini akan dipanggil ketika didalam screen Login user memulai proses login. Dalam hal ini menekan tombol Login.

src/screens/login.js

import React from 'react';
import {View, Text, Button} from 'react-native';

const Login = ({login}) => {

    return (
        <View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
            <Text>You haven't logged in yet</Text>
            <Button title="Login" onPress={login}/>
        </View>
    );
}

export default Login;

Nah, file home.js ditampilkan ketika foundToken berisi value truthy. Hal ini menunjukan token sudah ada di AsyncStorage artinya sudah pernah login sebelumnya.

Hampir sama seperti file login.js, screen Home juga menerima props. Tapi sebaliknya props yang diterima ada logout yang mana adalah function logoutAction yang nantinya bisa dipanggil ketika user memulai proses logout. Dalam hal ini menekan tombol Logout.

src/screens/home.js

import React from 'react';
import {View, Text, Button} from 'react-native';

const Home = ({logout}) => {

    return (
        <View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
            <Text>You already logged in</Text>
            <Button title="Logout" onPress={logout} />
        </View>
    );
}

export default Home;

Nah setelah menulis dan memahami seluruh kode diatas, kita bisa langsung coba untuk melihatnya di emulator. Namun, dengan fitur Fast Refresh membuat kita bisa melihat perubahan langsung tanpa harus build ulang. Jika memang ingin build ulang dipersilahkan.

Hasilnya kurang lebih seperti ini.

Mengimplementasi Teknik Lainnya dan Mengintegrasikannya

Umm, bisa dibilang teknik diatas sangatlah simpel. Hanya sedikit pemahaman dasar gimana konsep authentication flow dan AsyncStorage bekerja.

Untuk kedepannya kalian bisa implementasi teknik seperti useReducer, createContext, useMemo, Redux, Mobx, dan lainnya.

Kalian juga bisa mengintegrasikannya dengan API yang betul-betul men-submit login form (username dan password) dan mendapatkan token atau data lain dari response API tersebut.

Ending

Oke setelah kita berhasil membuat local login authentication flow di react native, banyak hal yang bisa diimprovisasi.

Di dunia yang tidak sempurna ini, kita pasti tidak sempurna, tapi dibalik ketidaksempurnaan itu kita jadi selalu memiliki celah untuk terus improvisasi.

As always, i’ll show you a good quote

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live”

― John Woods

John Woods: a programmer, found in a Google group, 1991

Enjoy, keep learning…