Compare commits
4 Commits
9bab420f91
...
1aa51adab0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1aa51adab0 | |||
| ebe8380829 | |||
| c421d267b9 | |||
| bbacd40ae9 |
334
Panduan-Penggunaan-Aplikasi.md
Normal file
334
Panduan-Penggunaan-Aplikasi.md
Normal file
@@ -0,0 +1,334 @@
|
||||
# Panduan Penggunaan Aplikasi Desa+
|
||||
|
||||
## Daftar Isi
|
||||
1. [Gambaran Umum Aplikasi](#gambaran-umum-aplikasi)
|
||||
2. [Fitur-fitur Utama](#fitur-fitur-utama)
|
||||
3. [User Roles dan Hak Akses](#user-roles-dan-hak-akses)
|
||||
4. [Petunjuk Penggunaan](#petunjuk-penggunaan)
|
||||
5. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Gambaran Umum Aplikasi
|
||||
|
||||
Aplikasi Desa+ adalah platform digital berbasis mobile yang dirancang untuk membantu pengelolaan dan komunikasi di lingkungan desa/kelurahan. Aplikasi ini menyediakan berbagai fitur untuk memudahkan administrasi desa, komunikasi antar warga, dan pengelolaan informasi penting.
|
||||
|
||||
### Teknologi yang Digunakan
|
||||
- React Native dengan Expo
|
||||
- Firebase (Authentication, Realtime Database, Cloud Messaging)
|
||||
- Redux Toolkit untuk manajemen state
|
||||
- TypeScript untuk type safety
|
||||
|
||||
## Fitur-fitur Utama
|
||||
|
||||
### 1. Otentikasi (Login & Verifikasi)
|
||||
**Deskripsi:** Sistem login menggunakan nomor telepon dan verifikasi OTP (One Time Password)
|
||||
- **Fungsi:** Memverifikasi identitas pengguna sebelum mengakses aplikasi
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna yang terdaftar
|
||||
- **Cara menggunakan:**
|
||||
1. Masukkan nomor telepon yang terdaftar
|
||||
2. Klik "Kirim OTP"
|
||||
3. Masukkan kode OTP yang diterima melalui WhatsApp
|
||||
4. Klik "Verifikasi" untuk masuk ke aplikasi
|
||||
|
||||
### 2. Dashboard/Home Screen
|
||||
**Deskripsi:** Tampilan utama aplikasi yang menampilkan informasi dan akses cepat ke berbagai fitur
|
||||
- **Fungsi:** Menyediakan ringkasan informasi desa dan akses cepat ke fitur-fitur utama
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna yang telah login
|
||||
- **Komponen:**
|
||||
- Carousel banner untuk promosi atau informasi penting
|
||||
- Grafik progres kegiatan
|
||||
- Grafik dokumen
|
||||
- Daftar kegiatan terbaru
|
||||
- Daftar divisi aktif
|
||||
- Daftar acara mendatang
|
||||
- Diskusi terbaru
|
||||
|
||||
### 3. Pengumuman
|
||||
**Deskripsi:** Fitur untuk membuat, melihat, dan mengelola pengumuman desa
|
||||
- **Fungsi:** Menyebarkan informasi penting kepada seluruh warga
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Admin, Deputy Super Admin
|
||||
- Melihat: Semua pengguna
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Pengumuman" dari fitur utama
|
||||
2. Untuk membuat pengumuman baru, klik tombol "+" di kanan atas
|
||||
3. Isi judul dan deskripsi pengumuman
|
||||
4. Pilih grup yang akan menerima pengumuman (opsional)
|
||||
5. Klik "Simpan" untuk menerbitkan
|
||||
|
||||
### 4. Diskusi Umum
|
||||
**Deskripsi:** Forum diskusi untuk komunikasi antar warga dan pihak pengelola
|
||||
- **Fungsi:** Tempat berdiskusi mengenai berbagai topik yang berkaitan dengan desa
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Admin, Deputy Super Admin
|
||||
- Melihat: Semua pengguna
|
||||
- Berkomentar: Semua pengguna
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Diskusi" dari fitur utama
|
||||
2. Untuk membuat diskusi baru, klik tombol "+" di kanan atas
|
||||
3. Isi judul dan deskripsi diskusi
|
||||
4. Pilih anggota yang akan menjadi partisipan
|
||||
5. Klik "Simpan" untuk membuat diskusi
|
||||
6. Klik pada judul diskusi untuk membuka dan memberikan komentar
|
||||
|
||||
### 5. Kegiatan/Proyek
|
||||
**Deskripsi:** Fitur untuk mengelola dan melacak proyek atau kegiatan desa
|
||||
- **Fungsi:** Mengelola dan memonitor kemajuan proyek-proyek desa
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Admin, Deputy Super Admin
|
||||
- Melihat: Semua pengguna
|
||||
- Status (Buka/Tutup): Berdasarkan hak akses
|
||||
- **Status Kegiatan:**
|
||||
- Segera: Proyek yang akan segera dimulai
|
||||
- Dikerjakan: Proyek yang sedang dalam proses pengerjaan
|
||||
- Selesai: Proyek yang telah selesai
|
||||
- Batal: Proyek yang dibatalkan
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Kegiatan" dari fitur utama
|
||||
2. Untuk membuat kegiatan baru, klik tombol "+" di kanan atas
|
||||
3. Isi nama kegiatan dan deskripsi
|
||||
4. Pilih tahun pelaksanaan
|
||||
5. Tambahkan anggota yang terlibat
|
||||
6. Klik "Simpan" untuk membuat kegiatan
|
||||
|
||||
### 6. Divisi
|
||||
**Deskripsi:** Fitur untuk mengelola struktur organisasi desa berdasarkan divisi
|
||||
- **Fungsi:** Mengorganisir warga dan tugas-tugas berdasarkan divisi-divisi tertentu
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Admin, Deputy Super Admin
|
||||
- Melihat: Semua pengguna
|
||||
- Anggota: Berdasarkan divisi yang diikuti
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Divisi" dari fitur utama
|
||||
2. Untuk membuat divisi baru, klik tombol "+" di kanan atas
|
||||
3. Isi nama dan deskripsi divisi
|
||||
4. Pilih grup induk (jika ada)
|
||||
5. Tambahkan anggota dan admin divisi
|
||||
6. Klik "Simpan" untuk membuat divisi
|
||||
7. Klik pada nama divisi untuk melihat detail dan fitur dalam divisi tersebut
|
||||
|
||||
### 7. Anggota
|
||||
**Deskripsi:** Fitur untuk mengelola data warga atau anggota desa
|
||||
- **Fungsi:** Menyimpan dan mengelola informasi tentang warga desa
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Admin, Deputy Super Admin
|
||||
- Melihat: Semua pengguna
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Anggota" dari fitur utama
|
||||
2. Gunakan fitur pencarian untuk menemukan anggota tertentu
|
||||
3. Gunakan filter untuk menampilkan anggota aktif/tidak aktif
|
||||
4. Klik pada nama anggota untuk melihat detail profil
|
||||
5. Untuk menambah anggota baru, klik tombol "+" di kanan atas
|
||||
|
||||
### 8. Jabatan
|
||||
**Deskripsi:** Fitur untuk mengelola posisi atau jabatan dalam organisasi desa
|
||||
- **Fungsi:** Mendefinisikan struktur jabatan dalam lembaga desa
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Admin, Deputy Super Admin
|
||||
- Melihat: Semua pengguna
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Jabatan" dari fitur utama
|
||||
2. Gunakan fitur pencarian untuk menemukan jabatan tertentu
|
||||
3. Gunakan filter untuk menampilkan jabatan aktif/tidak aktif
|
||||
4. Untuk menambah jabatan baru, klik tombol "+" di kanan atas
|
||||
|
||||
### 9. Lembaga Desa
|
||||
**Deskripsi:** Fitur untuk mengelola berbagai lembaga dalam desa
|
||||
- **Fungsi:** Mengorganisir struktur organisasi desa berdasarkan lembaga
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Developer
|
||||
- Melihat: Semua pengguna
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Lembaga Desa" dari fitur utama
|
||||
2. Gunakan fitur pencarian untuk menemukan lembaga tertentu
|
||||
3. Gunakan filter untuk menampilkan lembaga aktif/tidak aktif
|
||||
4. Untuk menambah lembaga baru, klik tombol "+" di kanan atas
|
||||
|
||||
### 10. Diskusi Divisi
|
||||
**Deskripsi:** Forum diskusi khusus untuk masing-masing divisi
|
||||
- **Fungsi:** Tempat berdiskusi secara internal dalam divisi
|
||||
- **Siapa yang bisa mengakses:** Hanya anggota divisi yang bersangkutan
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih sebuah divisi dari menu "Divisi"
|
||||
2. Pilih submenu "Diskusi Divisi"
|
||||
3. Klik tombol "+" untuk membuat diskusi baru
|
||||
4. Isi judul dan deskripsi diskusi
|
||||
5. Klik "Simpan" untuk membuat diskusi
|
||||
|
||||
### 11. Tugas Divisi
|
||||
**Deskripsi:** Fitur untuk mengelola tugas-tugas dalam masing-masing divisi
|
||||
- **Fungsi:** Menetapkan dan melacak tugas-tugas yang harus diselesaikan oleh anggota divisi
|
||||
- **Siapa yang bisa mengakses:** Hanya anggota divisi yang bersangkutan
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih sebuah divisi dari menu "Divisi"
|
||||
2. Pilih submenu "Tugas Divisi"
|
||||
3. Klik tombol "+" untuk membuat tugas baru
|
||||
4. Isi nama tugas dan detail lainnya
|
||||
5. Tambahkan anggota yang bertugas
|
||||
6. Klik "Simpan" untuk membuat tugas
|
||||
|
||||
### 12. Dokumen
|
||||
**Deskripsi:** Sistem manajemen dokumen untuk menyimpan dan mengelola file-file desa
|
||||
- **Fungsi:** Menyimpan dokumen penting dalam struktur folder
|
||||
- **Siapa yang bisa mengakses:** Akses berdasarkan divisi dan hak akses
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih sebuah divisi dari menu "Divisi"
|
||||
2. Pilih submenu "Dokumen"
|
||||
3. Buat folder baru atau upload file
|
||||
4. Gunakan fitur share untuk membagikan dokumen ke divisi lain
|
||||
|
||||
### 13. Kalender/Acara
|
||||
**Deskripsi:** Fitur untuk menjadwalkan dan mengelola acara-acara desa
|
||||
- **Fungsi:** Menjadwalkan kegiatan dan acara penting desa
|
||||
- **Siapa yang bisa mengakses:** Hanya anggota divisi yang bersangkutan
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih sebuah divisi dari menu "Divisi"
|
||||
2. Pilih submenu "Kalender"
|
||||
3. Klik tombol "+" untuk membuat acara baru
|
||||
4. Isi detail acara (judul, deskripsi, tanggal, waktu)
|
||||
5. Tambahkan anggota yang ikut serta
|
||||
6. Klik "Simpan" untuk membuat acara
|
||||
|
||||
### 14. Notifikasi
|
||||
**Deskripsi:** Sistem notifikasi untuk memberitahu pengguna tentang aktivitas penting
|
||||
- **Fungsi:** Memberitahu pengguna tentang pengumuman, komentar, atau aktivitas lainnya
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna
|
||||
- **Cara menggunakan:**
|
||||
1. Klik ikon notifikasi di bagian atas layar
|
||||
2. Lihat daftar notifikasi yang belum dibaca
|
||||
3. Klik pada notifikasi untuk membuka konten terkait
|
||||
|
||||
### 15. Profil
|
||||
**Deskripsi:** Fitur untuk melihat dan mengedit informasi pribadi pengguna
|
||||
- **Fungsi:** Menampilkan dan mengelola informasi akun pengguna
|
||||
- **Siapa yang bisa mengakses:** Pengguna yang bersangkutan
|
||||
- **Cara menggunakan:**
|
||||
1. Klik menu "Profil" dari navigasi bawah
|
||||
2. Lihat informasi pribadi
|
||||
3. Klik "Edit" untuk mengubah informasi (tidak tersedia untuk developer)
|
||||
|
||||
### 16. Banner
|
||||
**Deskripsi:** Fitur untuk mengelola banner promosi atau informasi penting di halaman utama
|
||||
- **Fungsi:** Menampilkan informasi atau promosi penting di tampilan awal
|
||||
- **Siapa yang bisa mengakses:** Super Admin, Deputy Super Admin, Developer
|
||||
- **Cara menggunakan:**
|
||||
1. Pilih menu "Banner" dari fitur utama (jika tersedia)
|
||||
2. Klik tombol "+" untuk membuat banner baru
|
||||
3. Upload gambar banner dan isi deskripsi
|
||||
4. Klik "Simpan" untuk menerbitkan banner
|
||||
|
||||
## User Roles dan Hak Akses
|
||||
|
||||
Aplikasi Desa+ memiliki sistem hierarki peran pengguna sebagai berikut:
|
||||
|
||||
### 1. Developer
|
||||
- **Deskripsi:** Peran tertinggi dengan semua hak akses
|
||||
- **Hak akses:**
|
||||
- Semua fitur dan fungsi dalam aplikasi
|
||||
- Manajemen semua data dan pengaturan sistem
|
||||
|
||||
### 2. Super Admin (Supadmin)
|
||||
- **Deskripsi:** Administrator utama desa
|
||||
- **Hak akses:**
|
||||
- Semua fitur kecuali beberapa fungsi sistem tingkat tinggi
|
||||
- Manajemen Deputy Super Admin, Admin, Co-Admin, dan User
|
||||
- Akses ke semua data dan fungsi administratif
|
||||
|
||||
### 3. Deputy Super Admin (Cosupadmin)
|
||||
- **Deskripsi:** Wakil administrator utama
|
||||
- **Hak akses:**
|
||||
- Manajemen Admin, Co-Admin, dan User
|
||||
- Akses ke sebagian besar fitur administratif
|
||||
- Dapat mengelola banner
|
||||
|
||||
### 4. Admin
|
||||
- **Deskripsi:** Administrator tingkat menengah
|
||||
- **Hak akses:**
|
||||
- Manajemen Co-Admin dan User
|
||||
- Akses ke fitur-fitur administratif dasar
|
||||
- Tidak dapat mengelola Deputy Super Admin dan Super Admin
|
||||
|
||||
### 5. Deputy Admin (Coadmin)
|
||||
- **Deskripsi:** Wakil administrator
|
||||
- **Hak akses:**
|
||||
- Manajemen User
|
||||
- Akses terbatas ke fitur-fitur administratif
|
||||
- Tidak dapat mengelola Admin ke atas
|
||||
|
||||
### 6. User
|
||||
- **Deskripsi:** Pengguna biasa
|
||||
- **Hak akses:**
|
||||
- Akses ke fitur-fitur umum
|
||||
- Tidak dapat mengelola pengguna lain
|
||||
- Tidak dapat mengakses fungsi administratif
|
||||
|
||||
## Petunjuk Penggunaan
|
||||
|
||||
### Login ke Aplikasi
|
||||
1. Buka aplikasi Desa+
|
||||
2. Masukkan nomor telepon yang terdaftar
|
||||
3. Klik "Kirim OTP"
|
||||
4. Cek WhatsApp untuk menerima kode OTP
|
||||
5. Masukkan kode OTP yang diterima
|
||||
6. Klik "Verifikasi" untuk masuk ke aplikasi
|
||||
|
||||
### Navigasi Utama
|
||||
- **Home:** Tampilan utama dengan informasi dan akses cepat
|
||||
- **Fitur:** Menu utama untuk mengakses semua fitur aplikasi
|
||||
- **Pencarian:** Mencari konten di seluruh aplikasi
|
||||
- **Notifikasi:** Melihat pemberitahuan penting
|
||||
- **Profil:** Informasi akun dan pengaturan pribadi
|
||||
|
||||
### Membuat Pengumuman Baru
|
||||
1. Pilih menu "Fitur" dari navigasi bawah
|
||||
2. Klik "Pengumuman"
|
||||
3. Klik tombol "+" di kanan atas
|
||||
4. Isi judul dan deskripsi pengumuman
|
||||
5. Pilih grup sasaran (opsional)
|
||||
6. Klik "Simpan"
|
||||
|
||||
### Bergabung dalam Diskusi
|
||||
1. Pilih menu "Fitur" dari navigasi bawah
|
||||
2. Klik "Diskusi"
|
||||
3. Pilih diskusi yang ingin diikuti
|
||||
4. Ketik komentar di kotak bawah
|
||||
5. Klik "Kirim" untuk mengirim komentar
|
||||
|
||||
### Mengelola Divisi
|
||||
1. Pilih menu "Fitur" dari navigasi bawah
|
||||
2. Klik "Divisi"
|
||||
3. Klik pada nama divisi untuk melihat detail
|
||||
4. Di dalam detail divisi, Anda dapat mengakses:
|
||||
- Tugas Divisi
|
||||
- Dokumen Divisi
|
||||
- Diskusi Divisi
|
||||
- Kalender Divisi
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Masalah Login
|
||||
- Pastikan nomor telepon yang dimasukkan sudah benar dan terdaftar
|
||||
- Pastikan koneksi internet stabil saat menerima OTP
|
||||
- Jika tidak menerima OTP, coba kirim ulang setelah beberapa menit
|
||||
|
||||
### Tidak Bisa Mengakses Fitur Tertentu
|
||||
- Pastikan peran Anda memiliki hak akses ke fitur tersebut
|
||||
- Beberapa fitur hanya tersedia untuk peran tertentu (misalnya Admin ke atas)
|
||||
|
||||
### Notifikasi Tidak Muncul
|
||||
- Pastikan izin notifikasi diaktifkan di pengaturan aplikasi
|
||||
- Pastikan aplikasi tetap berjalan di latar belakang
|
||||
|
||||
### Gambar Tidak Muncul
|
||||
- Periksa koneksi internet
|
||||
- Coba refresh halaman atau restart aplikasi
|
||||
|
||||
### Lupa Password
|
||||
- Aplikasi ini menggunakan sistem login OTP, jadi tidak ada password yang disimpan
|
||||
- Cukup gunakan nomor telepon dan minta OTP kembali
|
||||
|
||||
## Dukungan dan Bantuan
|
||||
|
||||
Jika Anda mengalami masalah atau memiliki pertanyaan tentang penggunaan aplikasi, silakan hubungi administrator desa atau tim pengembang aplikasi.
|
||||
|
||||
---
|
||||
*Dokumen ini terakhir diperbarui pada 4 Februari 2026*
|
||||
@@ -14,7 +14,7 @@ import { startActivityAsync } from 'expo-intent-launcher';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Alert, Dimensions, Platform, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { Dimensions, Platform, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import ImageViewing from 'react-native-image-viewing';
|
||||
import * as mime from 'react-native-mime-types';
|
||||
import RenderHTML from 'react-native-render-html';
|
||||
@@ -68,6 +68,7 @@ export default function DetailAnnouncement() {
|
||||
* Opens the image preview modal for the selected image file
|
||||
* @param item The file data object containing image information
|
||||
*/
|
||||
|
||||
function handleChooseFile(item: FileData) {
|
||||
setChooseFile(item)
|
||||
setPreview(true)
|
||||
@@ -156,11 +157,13 @@ export default function DetailAnnouncement() {
|
||||
}
|
||||
} catch (openError) {
|
||||
console.error('Error opening file:', openError);
|
||||
Alert.alert('INFO', 'Tidak ada aplikasi yang dapat membuka file ini');
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Tidak ada aplikasi yang dapat membuka file ini'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error downloading or opening file:', error);
|
||||
Alert.alert('INFO', 'Gagal mengunduh atau membuka file');
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Gagal membuka file',
|
||||
|
||||
@@ -80,8 +80,6 @@ export default function DetailDiscussionGeneral() {
|
||||
})
|
||||
const [viewEdit, setViewEdit] = useState(false)
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const onValueChange = reference.on('value', snapshot => {
|
||||
if (snapshot.val() == null) {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import { isImageFile } from "@/constants/FileExtensions";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import { startActivityAsync } from 'expo-intent-launcher';
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import React, { useState } from "react";
|
||||
import { Alert, Dimensions, Platform, Pressable, View } from "react-native";
|
||||
import { Dimensions, Platform, Pressable, View } from "react-native";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import ImageViewing from "react-native-image-viewing";
|
||||
import * as mime from 'react-native-mime-types';
|
||||
import Toast from "react-native-toast-message";
|
||||
import Text from "./Text";
|
||||
|
||||
|
||||
@@ -33,26 +36,47 @@ type Props = {
|
||||
dataFile: { id: string; idStorage: string; name: string; extension: string }[]
|
||||
}
|
||||
|
||||
type PropsFile = {
|
||||
id: string;
|
||||
idStorage: string;
|
||||
name: string;
|
||||
extension: string
|
||||
}
|
||||
|
||||
|
||||
export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress, titleShowAll, dataFile }: Props) {
|
||||
const lebarDim = Dimensions.get("window").width;
|
||||
const lebar = width ? lebarDim * width / 100 : 'auto';
|
||||
const textColorFix = textColor ? textColor : 'black';
|
||||
const [isTap, setIsTap] = useState(false);
|
||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||
const [chooseFile, setChooseFile] = useState<PropsFile>()
|
||||
const [preview, setPreview] = useState(false)
|
||||
|
||||
function handleChooseFile(item: PropsFile) {
|
||||
setChooseFile(item)
|
||||
setPreview(true)
|
||||
}
|
||||
|
||||
const openFile = (item: { idStorage: string; name: string; extension: string }) => {
|
||||
if (Platform.OS == 'android') setLoadingOpen(true)
|
||||
let remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||
const fileName = item.name + '.' + item.extension;
|
||||
let localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||
const mimeType = mime.lookup(fileName)
|
||||
const openFile = async (item: PropsFile) => {
|
||||
try {
|
||||
setLoadingOpen(true);
|
||||
const remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||
const fileName = item.name + '.' + item.extension;
|
||||
const localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||
const mimeType = mime.lookup(fileName);
|
||||
|
||||
// Download the file
|
||||
const downloadResult = await FileSystem.downloadAsync(remoteUrl, localPath);
|
||||
|
||||
if (downloadResult.status !== 200) {
|
||||
throw new Error(`Download failed with status ${downloadResult.status}`);
|
||||
}
|
||||
|
||||
const contentURL = await FileSystem.getContentUriAsync(downloadResult.uri);
|
||||
|
||||
FileSystem.downloadAsync(remoteUrl, localPath).then(async ({ uri }) => {
|
||||
const contentURL = await FileSystem.getContentUriAsync(uri);
|
||||
setLoadingOpen(false)
|
||||
try {
|
||||
if (Platform.OS == 'android') {
|
||||
if (Platform.OS === 'android') {
|
||||
await startActivityAsync(
|
||||
'android.intent.action.VIEW',
|
||||
{
|
||||
@@ -61,89 +85,148 @@ export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress
|
||||
type: mimeType as string,
|
||||
}
|
||||
);
|
||||
} else if (Platform.OS == 'ios') {
|
||||
Sharing.shareAsync(localPath);
|
||||
} else if (Platform.OS === 'ios') {
|
||||
await Sharing.shareAsync(localPath);
|
||||
}
|
||||
} catch (error) {
|
||||
Alert.alert('INFO', 'Gagal membuka file, tidak ada aplikasi yang dapat membuka file ini');
|
||||
} finally {
|
||||
if (Platform.OS == 'android') setLoadingOpen(false)
|
||||
} catch (openError) {
|
||||
console.error('Error opening file:', openError);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Tidak ada aplikasi yang dapat membuka file ini'
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error downloading or opening file:', error);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Gagal membuka file',
|
||||
text2: 'Silakan coba lagi nanti'
|
||||
});
|
||||
} finally {
|
||||
setLoadingOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Pressable onLongPress={onLongPress} onPress={onPress}
|
||||
onPressIn={() => setIsTap(true)}
|
||||
onPressOut={() => setIsTap(false)}
|
||||
style={({ pressed }) => [
|
||||
borderType == 'bottom'
|
||||
? Styles.wrapItemBorderBottom
|
||||
: borderType == 'all'
|
||||
? Styles.wrapItemBorderAll
|
||||
: Styles.wrapItemBorderNone,
|
||||
bgColor && bgColor == 'white' && ColorsStatus.white,
|
||||
// efek warna saat ditekan (sementara)
|
||||
isTap && colorPress && ColorsStatus.pressedGray,
|
||||
]}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
{icon}
|
||||
<View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' }]}>
|
||||
<View style={[Styles.ml10, rightTopInfo ? { width: '70%' } : { width: '90%' }]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
<>
|
||||
<Pressable onLongPress={onLongPress} onPress={onPress}
|
||||
onPressIn={() => setIsTap(true)}
|
||||
onPressOut={() => setIsTap(false)}
|
||||
style={({ pressed }) => [
|
||||
borderType == 'bottom'
|
||||
? Styles.wrapItemBorderBottom
|
||||
: borderType == 'all'
|
||||
? Styles.wrapItemBorderAll
|
||||
: Styles.wrapItemBorderNone,
|
||||
bgColor && bgColor == 'white' && ColorsStatus.white,
|
||||
// efek warna saat ditekan (sementara)
|
||||
isTap && colorPress && ColorsStatus.pressedGray,
|
||||
]}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
{icon}
|
||||
<View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' }]}>
|
||||
<View style={[Styles.ml10, rightTopInfo ? { width: '70%' } : { width: '90%' }]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
{
|
||||
subtitle &&
|
||||
typeof subtitle == "string"
|
||||
? <Text style={[Styles.textMediumNormal, { lineHeight: 15, color: textColorFix }]}>{subtitle}</Text>
|
||||
: <View style={{ alignItems: 'flex-start' }}>
|
||||
{subtitle}
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
{
|
||||
subtitle &&
|
||||
typeof subtitle == "string"
|
||||
? <Text style={[Styles.textMediumNormal, { lineHeight: 15, color: textColorFix }]}>{subtitle}</Text>
|
||||
: <View style={{ alignItems: 'flex-start' }}>
|
||||
{subtitle}
|
||||
</View>
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: textColorFix }]}>{rightTopInfo}</Text>
|
||||
}
|
||||
</View>
|
||||
{
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: textColorFix }]}>{rightTopInfo}</Text>
|
||||
}
|
||||
</View>
|
||||
|
||||
</View>
|
||||
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'left', color: textColorFix }]} numberOfLines={descEllipsize == false ? 0 : 2} ellipsizeMode='tail'>{desc}</Text>}
|
||||
{
|
||||
dataFile.length > 0 && (
|
||||
<ScrollView horizontal style={[Styles.mv05]} showsHorizontalScrollIndicator={false}>
|
||||
{dataFile.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.rowItemsCenter, Styles.borderAll, Styles.round10, Styles.ph05, Styles.pv03, Styles.mr05]}
|
||||
onPress={() => { openFile({ idStorage: item.idStorage, name: item.name, extension: item.extension }) }}
|
||||
>
|
||||
<Ionicons name="document-text" size={18} color="grey" style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{item.name}.{item.extension}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
{
|
||||
(leftBottomInfo || rightBottomInfo) &&
|
||||
(
|
||||
<View style={[rightBottomInfo && !leftBottomInfo ? Styles.rowSpaceBetweenReverse : Styles.rowSpaceBetween, Styles.mt05]}>
|
||||
{
|
||||
typeof leftBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{leftBottomInfo}</Text>
|
||||
:
|
||||
leftBottomInfo
|
||||
}
|
||||
{
|
||||
typeof rightBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{rightBottomInfo}</Text>
|
||||
:
|
||||
rightBottomInfo
|
||||
}
|
||||
</View>
|
||||
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'left', color: textColorFix }]} numberOfLines={descEllipsize == false ? 0 : 2} ellipsizeMode='tail'>{desc}</Text>}
|
||||
{
|
||||
dataFile.length > 0 && (
|
||||
<ScrollView horizontal style={[Styles.mv05]} showsHorizontalScrollIndicator={false}>
|
||||
{dataFile.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.rowItemsCenter, Styles.borderAll, Styles.round10, Styles.ph05, Styles.pv03, Styles.mr05]}
|
||||
onPress={() => {
|
||||
isImageFile(item.extension) ?
|
||||
handleChooseFile(item)
|
||||
: openFile(item)
|
||||
}}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={isImageFile(item.extension) ? "file-image-outline" : "file-document-outline"}
|
||||
size={18}
|
||||
color="grey" />
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{item.name}.{item.extension}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
{
|
||||
(leftBottomInfo || rightBottomInfo) &&
|
||||
(
|
||||
<View style={[rightBottomInfo && !leftBottomInfo ? Styles.rowSpaceBetweenReverse : Styles.rowSpaceBetween, Styles.mt05]}>
|
||||
{
|
||||
typeof leftBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{leftBottomInfo}</Text>
|
||||
:
|
||||
leftBottomInfo
|
||||
}
|
||||
{
|
||||
typeof rightBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{rightBottomInfo}</Text>
|
||||
:
|
||||
rightBottomInfo
|
||||
}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</Pressable>
|
||||
|
||||
<ImageViewing
|
||||
images={[{ uri: `${ConstEnv.url_storage}/files/${chooseFile?.idStorage}` }]}
|
||||
imageIndex={0}
|
||||
visible={preview}
|
||||
onRequestClose={() => setPreview(false)}
|
||||
doubleTapToZoomEnabled
|
||||
HeaderComponent={({ imageIndex }) => (
|
||||
<View style={[Styles.headerModalViewImg]}>
|
||||
{/* CLOSE */}
|
||||
<Pressable
|
||||
onPress={() => setPreview(false)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Close image viewer"
|
||||
>
|
||||
<Text style={{ color: 'white', fontSize: 26 }}>✕</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* MENU */}
|
||||
<Pressable
|
||||
onPress={() => chooseFile && openFile(chooseFile)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Download or share image"
|
||||
disabled={loadingOpen}
|
||||
>
|
||||
<Text style={{ color: loadingOpen ? 'gray' : 'white', fontSize: 22 }}>⋯</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</Pressable>
|
||||
)}
|
||||
FooterComponent={({ imageIndex }) => (
|
||||
<View style={{
|
||||
paddingBottom: 20,
|
||||
paddingHorizontal: 16,
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{ color: 'white', fontSize: 16 }}>{chooseFile?.name}.{chooseFile?.extension}</Text>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -662,21 +662,21 @@ const Styles = StyleSheet.create({
|
||||
marginBottom: 10,
|
||||
},
|
||||
chipSelected: {
|
||||
backgroundColor: "#FFF5F2",
|
||||
borderColor: "#FF5A3C",
|
||||
backgroundColor: "#f2f6ffff",
|
||||
borderColor: "#384288",
|
||||
},
|
||||
chipText: {
|
||||
fontSize: 16,
|
||||
color: "#222",
|
||||
},
|
||||
chipTextSelected: {
|
||||
color: "#FF5A3C",
|
||||
color: "#384288",
|
||||
},
|
||||
checkIcon: {
|
||||
position: "absolute",
|
||||
top: -6,
|
||||
left: -6,
|
||||
backgroundColor: "#FF5A3C",
|
||||
backgroundColor: "#384288",
|
||||
borderRadius: 10,
|
||||
padding: 2,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user