Category: Programming

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 Menggunakan Git di VSCode 2020

Cara Menggunakan Git di VSCode 2020

Haloo, i’m happy to see you again today. Oke kita masih dalam minggu-minggu karantina karena covid19, tepatnya sudah masuk PSBB. Hari ini saya kan kasih tau Cara menggunakan Git vscode 2020.

VSCode adalah sebuah code editor yang dikeluarkan Microsoft. VSCode gratis dan tidak berbayar. VSCode memiliki banyak ekstensi yang bisa diinstall dan juga fitur yang telah tersedia (built-in).

Salah satu fitur built-in VSCode adalah version control Git yang telah built-in dalam VSCode. Hal ini memudahkan programmer yang memakai VSCode untuk tidak perlu lagi memasang ekstensi seperti yang kita rasakan di Sublime atau Notepad++.

Hal yang menarik disini adalah VSCode gratis. Namun, memiliki banyak fitur yang ‘gakerasa’ gratis. VSCode pun multi-language code editor. Yang mana Kalian bisa membuat program dari banyak bahasa disini.

Cara untuk memulai menggunakan Git di Visual Studio sangat mudah. Caranya berikut ini.

Let’s Start

Requirement

Bahan-bahan dan syarat untuk menggunakan Git di Visual Studio adalah sebagai berikut.

  • Visual Studio yang telah terinstall (bisa didownload disini)
  • Git yang telah terinstall (untuk yang belum menginstall, bisa klik disini “Cara Install Git di macOS)

Saya anggap Kalian telah menginstall VSCode di perangkat yang Kalian gunakan. Jadi tutorial langsung masuk kepada teknis penggunaan Git di VSCode.

Langkah-langkah

Buka VSCode yang telah terinstall

Kalian pertama harus membuka VSCode dengan Projek/aplikasi Kalian yang sedang dikerjakan (mulai dikerjakan). Contohnya seperti dibawah ini, workspace yang telah terisi projek aplikasi Kalian.

VSCode opened workspace - cara menggunakan git vscode

Nah, setelah itu, klik ikon Git/Versioning yang ada di sebelah kiri tab menu. Ikon berada di bawah ikon Search.

Setelah itu, akan terlihat tombol Initialize Repository, maka Kalian klik saja tombol tersebut untuk mulai menggunakan Git di VSCode.

VSCode start git - cara menggunakan git vscode

Setelah itu, Kalian akan melihat ditempat button tadi aan berisi file-file yang sedang Kalian kerjakan. Jika sudah seperti itu, berarti Git telah mendeteksi adanya penambahan/perubahan file di direktori projek Kalian.

Kita sebut perubahan tersebut sebagai Changes. Kita bisa lihat ada 95 perubahan, yang mana itu adalah file yang baru dideteksi oleh Git.

VSCode git stage changes - cara menggunakan git vscode

Setelah itu, tentu Kalian ingin menambahkan file projek Kalian di Git, maka kita harus melakukan tahap Stage Changes. Yang mana kita mendaftarkan mana saja file yang akan dimasukkan pada Git.

Kalian bisa tekan ikon “+” atau tambah disebelah nama file, atau bisa langsung menekan ikon tambah disebelah tulisan Changes. Yang mana akan Stage All Changes langsung menambahkan seluruh file tersebut.

VSCode git add stage changes - cara menggunakan git vscode

Jangan lupa buat baca ini juga ya:
Membuat Face Detection di Javascript by CodeSeem

Setelah dilakukan Staging, file tersebut berubah dari yang tadinya statusnya Changes, sekarang menjadi Staged Changes.

Hal, selanjutnya yang harus kita lakukan adalah Commit. Commit adalah proses untuk menambahkan point perubahan pada Git. Nantinya kita bisa melihat perubahan apa saja, bahkan kita bisa berpindah-pindah dari satu commit ke commit lain (maksudnya seperti revert/rollback changes).

Masukkan pesan commit yang akan dimasukkan. Contohnya disini saya memasukkan pesan “Initial Commit”. Sebagai tanda bahwa ini adalah commit pertama saya di aplikasi ini.

Setelah itu, klik ikon Centang/Ceklis diatas. Proses Commit akan dimulai.

VSCode git commit changes - cara menggunakan git vscode

Jika proses commit berhasil, daftar perubahan file yang awalnya banyak disana, akan menghilang seperti gambar dibawah ini.

VSCode git done commit - cara menggunakan git vscode

Hal itu menunjukkan bahwa semua penambahan/perubahan file yang kita lakukan tadi berhasil ditambahkan ke Git. Git menunggu penambahan/perubahan file selanjutnya.

Contohnya, disini saya melakukan perubahan teks didala file App.js. Maka otomatis Git akan mendeteksinya sebagai perubahan. Kemudian, daftar tersebut bertambah menjadi satu.

Kalian juga bisa melihat perubahan apa yang dilakukan disana. Line mana yang berubah, juga kata/huruf apa yang berubah.

VSCode git file changes - cara menggunakan git vscode

Oke, tutorial sudah selesai. Kalian bisa memulai menggunakan git di projek aplikasi apapun. Kalian bisa terus mencari dan belajar lebih dalam lagi tentang Git yang telah banyak tersebar di Internet 🙂

Saya kira cukup untuk tutorial hari ini cara menggunakan git vscode. As always, here is the quote

“Some of the best programming is done on paper, really. Putting it into the computer is just a minor detail.”

― Max Kanat-Alexander, Code Simplicity: The Fundamentals of Software

Max Kanat-Alexander

Enjoy, keep learning…

Cara Mudah Install Git di mac OS 2020

Cara Mudah Install Git di mac OS 2020

Halo semuanya, good to see you again. Oke kita masih dalam minggu-minggu karantina karena covid19. Hari ini saya kan kasih tau Cara Install Git mac OS 2020.

Kalau dari internet Wikipedia, Git adalah

Git is a distributed version-control system for tracking changes in source code during software development. It is designed for coordinating work among programmers, but it can be used to track changes in any set of files. Its goals include speed, data integrity, and support for distributed, non-linear workflows.

Wikipedia: What is Git?

Git sederhananya adalah versioning control system. Git membantu kita untuk mengontrol perubahan file dan versi pada projek aplikasi kita. Banyak fungsi Git yang akan memudahkan kita dalam develop aplikasi.

Cara menginstall Git di mac OS ada dua cara, yang pertama dengan CLI yakni homebrew dan yang kedua dengan GUI Installer.

Tentunya di tutorial ini, saya kan mengambil cara mudahnya sesuai judulnya.

Let’s Start

Requirement

Bahan-bahan dan syarat untuk menginstall Git di macOS adalah sebagai berikut.

  • Perangkat mac
  • Git untuk macOS
  • Koneksi Internet

Langkah-langkah

Download Git melalui link resminya

Kalian pertama harus mendownload Git Installer melalui link resminya berikut ini. Penting diketahui untuk selalu mendownload sesuatu melalui link resminya. Supaya terhindar dari virus yang ditempelkan di paket instalasi.

Download Git GUI Installer untuk macOS

Setelah Kalian sampai di websitenya, pastikan Kalian memilih link installer untuk mempermudah proses instalasi Git.

Download Git for mac OS - Cara Install Git di mac OS

Link tersebut akan membawa Kalian kepada website SoureForge.net, tinggal klik tombol Download dan file akan terunduh. Hasil file download nantinya akan memiliki format .dmg.

Download Git for mac OS - Cara Install Git di mac OS

Buka file .dmg hasil download

Buka file .dmg hasil download yang telah selesai tadi. Setelah dibuka, double click pada file .pkg yang akan membuka setup wizard dan memulai proses instalasi. *dalam case ini, saya mendownload versi 2.15

Git pkg for mac OS - Cara Install Git di mac OS

Jangan lupa buat baca ini juga ya:
Membuat Face Detection di Javascript by CodeSeem

Ikuti setup wizard sampai selesai

Setelah setup wizard terbuka, Kalian bisa langsung klik tombol Continue.

Installation Wizard Git for mac OS - Cara Install Git di mac OS

Nah, pada bagian ini pastikan lokasi instalasi sudah sesuai keinginan Kalian. Jika ingin diinstall secara lokasi default, langsung saja klik Install.

Installation location Git for mac OS - Cara Install Git di mac OS

Tunggu proses instalasi selesai. Proses ini memakan waktu tidak sampai 2 menit kok.

Installation process Git for mac OS - Cara Install Git di mac OS

Proses instalasi telah selesai dan berhasil dipasang di mac Kalian.

Installation success Git for mac OS - Cara Install Git di mac OS

Konfigurasi awal Git pada mac

Untuk bisa mulai menggunakan Git pada mac. Ada beberapa langkah yang harus dilakukan dahulu, yakni menambahkan global user dan email. Hal ini dilakukan untuk mengidentifikasi siapa yang melakukan perubahan file di Git.

Berikut cara konfigurasi awal Git di mac

Cara Konfigurasi Awal Git di mac

Membuka terminal dan cek versi Git

Pertama, kalian harus membuka terminal di mac Kalian. Cara termudah menurut saya adalah membuka Spotlight dengan menekan Cmd+Space kemudian cari “Terminal“.

Setelah itu, coba ketik perintah git --version untuk mengecek apakah Git sudah betul terinstall. Hasilnya kira-kira akan seperti dibawah ini.

Jangan tutup terminal Kalian, karena langkah selanjutnya kita masih membutuhkannya

Terminal git version mac os- Cara Install Git di mac OS

Memasukkan Global User dan Email di Git

Cara memasukkan global user dan email di git mac adalah dengan membuka terminal dan mengetikkan perintah berikut ini

git config --global user.name "Nama Kalian"
git config --global user.email "Email Kalian"

Nama dan email tergantung nama dan email Kalian, silahkan disesuaikan. Kira-kira hasilnya akan seperti dibawah ini. Jika tidak ada error message, berarti seharusnya sudah ter-set dengan baik.

Terminal git set email user name mac os- Cara Install Git di mac OS

Memastikan Global User dan Email di Git mac Sudah Betul

Kita bisa mengeceknya lebih detil lagi dengan cara mengetikkan perintah git config --list di terminal.

Hasilnya jika discroll pada bagian user.name dan user.email akan berisi name dan email yang barusan Kalian set.

Terminal git check email user name mac os- Cara Install Git di mac OS

Oke, instalasi sudah selesai. Kalian bisa memulai menggunakan git di projek aplikasi apapun. Untuk Kalian yang menggunakan VSCode sebagai Code Editor Kalian, yuk baca juga:

Cara Menggunakan Git di VSCode by CodeSeem

Saya kira cukup untuk tutorial hari ini cara install git mac os. As always, here is the quote

“Most improved things can be improved.”

― Mokokoma Mokhonoana

Mokokoma Mokhonoana: someone, you can search on Google

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…

Cara Install Python di macOS Catalina dengan Anaconda

Cara Install Python di macOS Catalina dengan Anaconda

Hai, happy to see you again. Tetap masih di minggu karantina covid19, sepertinya semakin rajin posting karena masih mencari kegiatan yang positif dan gabikin pusing. Hari ini saya kan kasih tau cara install python anaconda macOS Catalina.

Python sebagai salah satu bahasa pemograman terpopuler di tahun 2019 dan 2020 ini, membuat banyak programmer atau calon programmer yang ingin mempelajarinya. Stabilitas, performa, kecepatan, serta dukungan library dan implementasinya yang luas, membuat python dipakai banyak programmer.

Namun, untuk instalasi environment Python membutuhkan beberapa step. Kali ini, saya akan memberitahu cara install Python menggunakan Anaconda.

Anaconda secara singkat adalah paket aplikasi untuk environment pemograman python yang telah terbundle dengan beberapa aplikasi dan library seperti Jupyter Notebook dan NumPy.

Kali ini kita akan membahas cara install Python bersamaan dengan Anaconda di macOS. Jadi tidak perlu khawatir untuk teman-teman developer yang mungkin sebelumnya develop di Windows atau ingin memulai belajar Python di macOS.

Let’s Start

Requirement

Bahan-bahan dan syarat untuk menginstall Python Anaconda di macOS Catalina adalah sebagai berikut.

  • Perangkat mac dengan macOS Catalina (sebetulnya varian apa saja)
  • Anaconda untuk macOS
  • Koneksi Internet

Langkah-langkah

Download Anaconda melalui link resminya

Kalian pertama harus mendownload Anaconda melalui link resminya berikut ini. Penting diketahui untuk selalu mendownload sesuatu melalui link resminya. Supaya terhindar dari virus yang ditempelkan di paket instalasi.

Download Anaconda untuk macOS Catalina

Setelah kalian klik, akan muncul tampilan untuk mendownload. Pilih versi Python yang diinginkan dan tekan link “64 bit Graphical Installer”untuk memulai proses download.

Kita memilih Graphical Installer dibandingkan dengan Command Line Installer karena untuk memudahkan kita dalam proses instalasi Anaconda ini.

Pilihan versi Python cara install python anaconda macOS

File yang sudah terdownload akan berformat .pkg.

Baca juga:
Membuat Face Detection dengan Javascript di Browser by CodeSeem

Buka file .pkg hasil download

Maka buka file tersebut dengan double click. Akan muncul setup wizard untuk memulai instalasi Anaconda.

Ikuti setup wizard sampai selesai

Pertama ketika setup wizard terbuka, klik Continue untuk memulai proses instalasi Anaconda.

Setelah itu, pada bagian Read Me, setelah kalian selesai membacanya, klik Continue.

Lalu, pada bagian License Agreement, baca dengan teliti segala ketentuan lisensinya. Setelah itu, klik Continue

Setup wizard cara install python anaconda macOS

Selanjutnya, ada pilihan lokasi install.

  • Install for me only -> hanya user yang sedang login yang bisa pakai
  • Install on a specific disc -> bisa dibuka di lokasi tsb oleh user mana aja

Kemudian pilih Continue. Setelah itu ada konfirmasi install yang menampilkan ukuran storage yang akan terpakai, jika sudah yakin pilih Install.

Tunggu proses instalasi sampai selesai. Biasanya proses instalasi memakan waktu 2-5 menit tergantung spesifikasi PC dan banyak faktor lain.

Setup wizard cara install python anaconda macOS

Setelah proses instalasi selesai, ada sedikit pesan dari sponsor Anaconda yaitu PyCharm. Kita bisa langsung saja klik Continue.

Pesan instalasi selesai muncul, tanda seluruh rangkaian proses instalasi benar-benar sudah selesai, klik Close untuk menutup jendela instalasi.

Setup finish cara install python anaconda macOS

Instalasi selesai, buka Anaconda melalui Launchpad

Nah sekarang Anaconda sudah berhasil diinstall di macOS kalian. Kalian bisa membukanya lewat Launchpad dan cari Anaconda.

Anaconda Navigator berhasil dibuka dan siap memanggil aplikasi yang dibutuhkan.

Anaconda navigator cara install python anaconda macOS

Contohnya disini saya membuat Jupyter Notebook dan langsung bisa mulai mempelajari bahasa pemograman Python di macOS.

Jupyter Notebook cara install python anaconda macOS

Oke, instalasi sudah selesai. Kalian bisa memulai membangun aplikasi python apapun bentuknya dengan Anaconda ini. Saya kira cukup untuk tutorial hari ini cara install python anaconda macOS Catalina. As always, here is this day’s quote

“Talk is cheap. Show me the code.”

― Linus Torvalds

Linus Torvalds: Linux Kernel Creator

Enjoy, keep learning…

Cara Install Apache, PHP, dan MySQL di macOS Catalina XAMPP

Cara Install Apache, PHP, dan MySQL di macOS Catalina XAMPP

Halo semuanya, i’m happy to see you again in this Fri(d|y)ay. Masih di minggu karantina karena covid19, semakin mencari kegiatan yang bisa dilakukan dirumah. Hari ini saya kan kasih tau cara install Apache mySQL macOS Catalina yang terpaket dalam XAMPP.

Apache, PHP, dan MySQL secara berurutan adalah webserver, bahasa pemograman, dan database. Banyak varian lain dari webserver dan database, tapi di postingan kali ini saya akan membahas mengenai mereka.

Kali ini kita akan membahas cara installnya dengan menggunakan XAMPP. Bagi yang belum tau, XAMPP singkatnya adalah kumpulan software yang dipaket menjadi satu. Dengan XAMPP kita langsung mendapatkan Apache, PHP, dan MySQL sekaligus yang terinstall. Jadi tidak perlu repot-repot lagi.

Biasanya XAMPP banyak digunakan oleh web developer untuk membangun website tanpa ribet setup server dahulu. Mungkin juga kalian sudah biasa untuk menginstall XAMPP di Windows dan baru migrasi ke mac. Tidak apa-apa, saya akan beritahu caranya.

Let’s Start

Requirement

Bahan-bahan dan syarat untuk menginstall Apache, PHP, dan MySQL menggunakan XAMPP di macOS Catalina adalah sebagai berikut.

  • Perangkat mac dengan macOS Catalina (sebetulnya varian apa saja)
  • XAMPP untuk mac
  • Koneksi Internet

Langkah-langkah

Download XAMPP melalui link resminya

Kalian pertama harus mendownload XAMPP melalui link resminya berikut ini. Penting diketahui untuk selalu mendownload sesuatu melalui link resminya.

Download XAMPP untuk macOS Catalina

Setelah kalian klik, akan muncul tampilan untuk mendownload. Scroll agak kebawah sampai ke bagian “XAMPP for OSX“. Pilih versi PHP yang diinginkan dan download.

File yang sudah terdownload akan berformat .dmg.

Baca juga:
Membuat Face Detection dengan Javascript di Browser by CodeSeem

Buka file .dmg hasil download

Maka buka file tersebut dengan double click. Kemudian, window baru muncul, double click juga ikon XAMPP yang muncul. Akan muncul setup wizard

Klik Next pada setup wizard untuk memulai instalasi.

Ikuti setup wizard sampai selesai

Centang XAMPP Core Files dan XAMPP Developer Files karena tentunya kita ingin menginstall seluruh aplikasi dan main foldernya, klik Next.

Kemudian klik Next pada bagian Installation Directory, XAMPP akan diinstall di /Application/XAMPP.

Lalu, uncentang Learn more about Bitnami for XAMPP, supaya setelah instalasi selesai kita tidak dibawa ke website mereka, klik Next.

Klik Next dan tunggu hingga proses instalasi selesai.

Setelah selesai, akan ada opsi Launch XAMPP. Jika ingin membuka XAMPP tepat setelah instalasi selesai, maka centang saja, jika tidak maka uncentang. Klik Finish.

Instalasi selesai, buka Apache dan MySQL melalui XAMPP

Jika pada opsi Launch XAMPP tadi dicentang, maka XAMPP akan otomatis terbuka. Jika tidak, kalian bisa membukanya lewat folder Application atau menggunakan shortcut Cmd + Space dan ketik XAMPP.

Apache, PHP, dan MySQL sudah siap digunakan. Untuk mengatur control server, masuk ke tab Manage Servers.

Oke, instalasi sudah selesai. Kalian bisa memulai membangun aplikasi dengan XAMPP ini. Saya kira cukup untuk tutorial hari ini cara install Apache mySQL macOS Catalina. As always, it comes the quote

“Simple things should be simple, complex things should be possible.”

― Alan Kay

Alan Kay: American Computer Scientist

Enjoy, keep learning…

Implementasi Face Detection di Javascript Browser dengan face-api.js

Implementasi Face Detection di Javascript Browser dengan face-api.js

Halo semuanya, good to see you again reading this post. Kita akan membuat face detection dengan face-api.js javascript. Minggu karantina ini betul-betul bikin kita cepet bosen. Karena virus covid19 ini, kita jadi harus ngelakuin social distancing yang mana gaboleh keluar rumah kalo gak penting-penting amat. Semoga saja wabah ini cepat berlalu.

Nah, disaat itulah saya merasa bosan karena setelah WFH (work from home) selesai, maka kegiatan saya hanya malas-malasan. Disitulah saya berfikir, kenapa saya gabuat tutorial tentang face detection? Bukankah suatu yang bagus juga untuk diposting.

Face Detection adalah proses pendeteksian wajah dari video atau gambar yang dilakukan oleh komputer dan bukan manusia. Face Detection yang kita akan buat diimplementasikan secara realtime melalui video dari webcam laptop kita di browser.

Disini kita akan implementasi face detection menggunakan library yang namanya face-api.js.

Maka kita mulai saja ya caranya.

Let’s start

Requirements

Pertama, kita masuk ke bagian bahan-bahan dulu yang diperlukan yakni,

  • Laptop (yang ada webcamnya yaa)
  • Code Editor (VSCode, Sublime Text)
  • face-api.js (library based on TensorFlow Core yang membantu kita untuk pendeteksian)
  • File pre-trained model (file yang berisi data pendeteksian)
  • Browser (saya saranin pakai Chrome)
  • Internet dan StackOverflow (kalo-kalo stuck)
  • Secangkir kopi, semangat, dan kemauan 🙂

Oke, setelah bahan-bahan telah dimiliki, kita mulai ke tutorialnya. Download dulu library face-api.js yang akan kita gunakan disini.

Struktur Direktori

Struktur direktori dari project ini bisa dilihat di bawah ini yaa

project/
--index.html
--main.js
--face-api.js
--model/
---- *all the models

Menampilkan Output Webcam di Browser

Kita masuk ke tahap yang pertama, buat satu file html seperti di bawah ini. Untuk file index.html dan main.js. Untuk library face-api.js nanti akan kita libatkan selanjutnya :).

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 Face Detection</title>
    <style>
        body {
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        #videoEl {
            transform: rotateY(180deg);
            -webkit-transform: rotateY(180deg);
        }
    </style>
</head>
<body>
    <video id="videoEl" width="800" height="600" autoplay muted></video>
    <script defer src="main.js"></script>
</body>
</html>

main.js

//Get elemen video untuk menampilkan kamera
const videEl = document.getElementById('videEl');

//Membuat function untuk menampilkan input webcam di elemen video
async function startCamera(){
    navigator.mediaDevices.getUserMedia({video: true})
    .then(function(stream){
        videoEl.srcObject = stream;
    })
    .catch(function(err){
        console.log('Ada yang error');
        console.log(err);
    })
}

startCamera();

Dengan code diatas, kita sudah bisa menampilkan output dari webcam kita di browser. Hasilnya seperti di bawah ini.

CodeSeem Browser camera in html javascript. membuat face detection face-api.js javascript
Hasil yang muncul di browser.

Memulai Implementasi Face Detection

Selanjutnya, setelah kita berhasil menampilkan output dari webcam kita, kita akan mulai untuk mengintegrasikan face-api.js supaya bisa melakukan proses face detection.

Tambahkan satu baris tag script seperti dibawah ini. Kode dibawah diletakkan diatas tag script dari file main.js.

index.html

<script src="js/face-api.js"></script> <--- Tambahkan ini
<script defer src="main.js"></script>

Setelah ditambahkan, kita bisa mencoba untuk cek apakah library sudah terload dengan benar. Dengan cara membuka console log dan akses faceapi seperti dibawah

Accessing faceapi variable in console membuat face detection face-api.js javascript
Console mengakses class faceapi

Jika muncul tidak jauh seperti di atas, alias tidak ada error message maka kalian bisa ke langkah berikutnya. Jika ada error, coba dicek kembali apakah cara memanggilnya sudah benar?

Setelah itu, kita mulai untuk memakai face-api.js. Edit file main.js supaya bisa mendeteksi wajah kita menjadi seperti di bawah ini.

//Get elemen video untuk menampilkan kamera
const videoEl = document.getElementById('videoEl');

//Inisiasi function dari faceapi load model
Promise.all([
    faceapi.nets.tinyFaceDetector.loadFromUri('./models'),
    faceapi.nets.faceLandmark68Net.loadFromUri('./models'),
    faceapi.nets.faceRecognitionNet.loadFromUri('./models'),
    faceapi.nets.faceExpressionNet.loadFromUri('./models')
])
.then(startCamera);

//Membuat function untuk menampilkan input webcam di elemen video
async function startCamera(){
    navigator.mediaDevices.getUserMedia({video: true})
    .then(function(stream){
        videoEl.srcObject = stream;
    })
    .catch(function(err){
        console.log('Ada yang error');
        console.log(err);
    })
}

//Main function untuk deteksi wajah
function startFunction(){
    //Set interval setiap 0,1 detik
    setInterval(async function(){
        //Memanggil function untuk mendeteksi wajah di video webcam
        const detection = await faceapi.detectAllFaces(
            videoEl,
            new faceapi.TinyFaceDetectorOptions()
        )
        .withFaceLandmarks()
        console.log(detection);//Tes  berhasil atau tidak
    }, 100);
}

//Listen event saat output webcam sudah mulai muncul di browser
videoEl.addEventListener('playing', startFunction)

Yuk baca juga:
a hello world by CodeSeem

Penjelasan

Di bagian atas, kita menggunakan Promise untuk memanggil function loadmodel dari face-api.js. Kemudian menjalankan function startCamera tepat setelah model selesai load.

//Inisiasi function dari faceapi load model
Promise.all([
    faceapi.nets.tinyFaceDetector.loadFromUri('./models'),
    faceapi.nets.faceLandmark68Net.loadFromUri('./models'),
    faceapi.nets.faceRecognitionNet.loadFromUri('./models'),
    faceapi.nets.faceExpressionNet.loadFromUri('./models')
])
.then(startCamera);

Lalu di function startFunction, kita menjalankan function setInterval dalam waktu 0,1 detik. Kita disini menggunakan function detectAllFaces untuk mendeteksi wajah.

Pada tahap ini, kita belum benar-benar melihat bukti berhasil mendeteksi wajah secara visual, namun disini kita memanggil console.log(detection) yang akan menampilkan data hasil deteksinya secara teks.

//Main function untuk deteksi wajah
function startFunction(){
    //Set interval setiap 0,1 detik
    setInterval(async function(){
        //Memanggil function untuk mendeteksi wajah di video webcam
        const detection = await faceapi.detectAllFaces(
            videoEl,
            new faceapi.TinyFaceDetectorOptions()
        )
        .withFaceLandmarks()//Deteksi dengan metode landmark
        .withFaceExpressions()//Deteksi dengan metode ekspresi wajah
        console.log(detection);//Tes  berhasil atau tidak
    }, 100);
}

Jika berhasil, maka akan terlihat ouput data di console seperti di bawah ini.

Data Face Detection in ouput console membuat face detection face-api.js javascript
Data output dari detection

Kemudian, tahap selanjutnya adalah memvisualisasikan hasil deteksi wajah. Kita fokus pada function startFunction pada file main.js.

startFunction()

//Function ketika output webcam mulai muncul
function startFunction(){

    //Create canvas untuk detection shape nya
    const canvas = faceapi.createCanvasFromMedia(videoEl);
    document.body.append(canvas);

    //Get video element width dan height nya
    const sizeEl = {width:videoEl.width , height: videoEl.height}
    //Match ukuran canvas dengan video elemen
    faceapi.matchDimensions(canvas, sizeEl);

    //Set interval setiap 0,1 detik
    setInterval(async function(){
        //Memanggil function untuk mendeteksi wajah di video webcam
        const detection = await faceapi.detectAllFaces(
            videoEl,
            new faceapi.TinyFaceDetectorOptions()
        )
        .withFaceLandmarks()//Sistem deteksi menggunakan face landmark
        .withFaceExpressions()//Sistem deteksi menggunakan face expression
        
        //Membuat detection yang diresize menurut ukuran wajah
        const resizedSizeDetection = faceapi.resizeResults(detection, sizeEl);

        //Mengaplikasikan deteksi di canvas yang telah dibuat tadi, 
        //juga menghapus hasil deteksi yang sebelumnya
        canvas.getContext('2d').clearRect(0,0, canvas.width, canvas.height);
        faceapi.draw.drawDetections(canvas, resizedSizeDetection);

    }, 100);
}

Penjelasan *lagi

Pertama yang ditambahkan adalah code untuk membuat elemen canvas dan menyamakan ukurannya dengan elemen video.

//Create canvas untuk detection shape nya
    const canvas = faceapi.createCanvasFromMedia(videoEl);
    document.body.append(canvas);

    //Get video element width dan height nya
    const sizeEl = {width:videoEl.width , height: videoEl.height}
    //Match ukuran canvas dengan video elemen
    faceapi.matchDimensions(canvas, sizeEl);

Kemudian, fungsi untuk menampilkan visual deteksi juga ditambahkan. Jangan lupa function clearRect sangat berguna untuk menghapus hasil visual deteksi sebelumnya.

//Membuat detection yang diresize menurut ukuran wajah
        const resizedSizeDetection = faceapi.resizeResults(detection, sizeEl);

        //Mengaplikasikan deteksi di canvas yang telah dibuat tadi, 
        //juga menghapus hasil deteksi yang sebelumnya
        canvas.getContext('2d').clearRect(0,0, canvas.width, canvas.height);
        faceapi.draw.drawDetections(canvas, resizedSizeDetection);

Jangan lupa juga untuk menambah styling pada canvas supaya memiliki posisi di atas elemen lain dan tidak terganggu elemen lain.

canvas {
            position: absolute;
            transform: rotateY(180deg);
            -webkit-transform:rotateY(180deg);
        }

Jika styling sudah ditambahkan, maka seharusnya deteksi akan berjalan lancar dan tampil hasil deteksi berupa kotak dengan angka persentase di ujungnya.

Face detection in javascript with face-api membuat face detection face-api.js javascript
Hasil deteksi berhasil

Penjelasan Opsional *tapi penting

Pada kode styling di atas, elemen canvas diset posisinya menjadi absolute. Kemudian ditambahkan properti transform dengan value rotasi 180deg. Saya menambahkan itu karena sebelumnya untuk elemen video pun saya melakukan hal yang sama.

Hal itu dilakukan supaya video hasil ouput webcam dan juga hasil deteksinya tidak mirrorred. Namun, kekurangannya styling dengan CSS diatas, karena hasil deteksinya mirrored, angka persentase ketepatan deteksinya juga jadi mirrored.

Ending

Oke setelah kita berhasil membuat deteksi wajah, banyak sekali tentunya kekurangan yang bisa diimprovisasi. Kita telah membuat face detection dengan face-api.js javascript. Tapi, di dunia yang tidak sempurna ini, kita pasti tidak sempurna, tapi dibalik ketidaksempurnaan itu kita jadi selalu memiliki celah untuk terus improvisasi.

Banyak yang bisa diimplementasikan dari face-api.js ini setelah diimprovisasi. Seperti

  • Absen menggunakan wajah
  • Login atau authentication menggunakan wajah (mungkin less secured)
  • Game atau app kecil yang bisa menambah portofolio Kalian.
  • — dan banyak lagi

As always, i’ll show you a good quote

“Everyday life is like programming, I guess. If you love something you can put beauty into it.”
― Donald Knuth

Knuth, Donald.

Enjoy, keep learning…