// DiscoverActivity.kt
package com.search2learn.s2l
import android.util.Log
import org.web3j.protocol.http.HttpService
import android.net.wifi.WifiManager
import java.net.InetAddress
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import androidx.activity.result.ActivityResultLauncher
import android.app.Activity
import android.content.Intent
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.firebase.firestore.FieldValue
import java.math.BigDecimal
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import androidx.lifecycle.lifecycleScope
import com.search2learn.s2l.S2LRewardUtils.sendS2LToUser
import kotlinx.coroutines.tasks.await
import android.net.Uri
import android.os.Bundle
import android.widget.ImageButton
import android.widget.EditText
import android.widget.Button
import android.widget.ImageView
import android.view.View
import android.content.Context
import com.bumptech.glide.Glide
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.tabs.TabLayout
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.launch
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.messaging.ktx.messaging
import com.google.firebase.storage.ktx.storage
import kotlinx.coroutines.delay
import kotlinx.coroutines.tasks.await
import org.web3j.abi.FunctionEncoder
import org.web3j.abi.datatypes.Address
import org.web3j.abi.datatypes.Function
import org.web3j.abi.datatypes.generated.Uint256
import org.web3j.crypto.Credentials
import org.web3j.protocol.Web3j
import org.web3j.protocol.core.methods.response.TransactionReceipt
import org.web3j.tx.RawTransactionManager
import java.math.BigInteger
class DiscoverActivity : AppCompatActivity(), PostAdapter.OnPostClickListener {
lateinit var web3jBSC: Web3j
private lateinit var web3jETH: Web3j
private lateinit var priceApi: PriceApiService
private val bscRpcUrl = "https://bsc-dataseed.binance.org/"
private val ethRpcUrl = "https://mainnet.infura.io/v3/c88ab0a11eb040438cff2f7bef79feec"
private lateinit var recyclerViewPosts: RecyclerView
private lateinit var postAdapter: PostAdapter
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
private lateinit var postRepository: PostRepository
private lateinit var auth: FirebaseAuth
private var currentUserId: String = ""
private var selectedImageUri: Uri? = null
private var currentProfileImageView: ImageView? = null
// في MainActivity أو Application class:
private val pickImageLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { uri ->
selectedImageUri = uri
}
}
}
// في DiscoverActivity.kt، تأكد أن onCommentClick يعمل هكذا:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.posts_feed)
web3jBSC = Web3j.build(HttpService(bscRpcUrl))
val btnProfileSetup = findViewById<ShapeableImageView>(R.id.btnProfileSetup)
btnProfileSetup.setOnClickListener {
showProfileSetupDialog()
}
val btnNotifications: ShapeableImageView = findViewById(R.id.btnNotifications)
btnNotifications.setOnClickListener {
startActivity(Intent(this, NotificationsActivity::class.java))
}
// استعادة أي صلاحيات URI محفوظة
val sharedPref = getSharedPreferences("user_profile", Context.MODE_PRIVATE)
sharedPref.getString("user_image_uri", null)?.let { uriString ->
try {
val uri = Uri.parse(uriString)
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
} catch (e: Exception) {
Log.e("DiscoverActivity", "Error restoring URI permissions", e)
}
}
// 1. تهيئة Firebase
auth = Firebase.auth
currentUserId = auth.currentUser?.uid ?: ""
postRepository = PostRepository() // التهيئة هنا مهمة
// 2. تهيئة الواجهة
initViews()
setupAdapter()
setupSwipeRefresh()
setupTabs()
setupFabButton()
// 3. تحميل البيانات الأولية
loadPosts()
// حفظ FCM Token
saveFCMToken()
// 4. تفعيل التحديثات الفورية
setupRealTimeUpdates() // <-- هنا
}
private fun initViews() {
recyclerViewPosts = findViewById(R.id.recyclerViewPosts)
swipeRefreshLayout = findViewById(R.id.swipeRefresh)
recyclerViewPosts.layoutManager = LinearLayoutManager(this)
}
private val profileImagePickerLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { uri ->
selectedImageUri = uri
currentProfileImageView?.let { imageView ->
Glide.with(this@DiscoverActivity).load(uri).into(imageView)
}
}
}
}
private fun saveFCMToken() {
Firebase.messaging.token.addOnCompleteListener { task ->
if (task.isSuccessful) {
val token = task.result
val currentUser = FirebaseAuth.getInstance().currentUser ?: return@addOnCompleteListener
Firebase.firestore.collection("users")
.document(currentUser.uid)
.update("fcmToken", token)
.addOnFailureListener { e ->
Log.e("FCM", "Error saving token", e)
}
}
}
}
private fun setupAdapter() {
postAdapter = PostAdapter(this, currentUserId, this)
recyclerViewPosts.adapter = postAdapter
}
private fun loadPosts() {
swipeRefreshLayout.isRefreshing = true
lifecycleScope.launch {
try {
val posts = postRepository.getPosts()
postAdapter.setPosts(posts)
} catch (e: Exception) {
Toast.makeText(this@DiscoverActivity,
"Error loading posts: ${e.message}", Toast.LENGTH_SHORT).show()
} finally {
swipeRefreshLayout.isRefreshing = false
}
}
}
private fun setupRealTimeUpdates() {
// تأكد من أن postRepository مهيأ
if (!::postRepository.isInitialized) {
postRepository = PostRepository()
}
postRepository.getPostsRealTime { posts ->
runOnUiThread {
// تحديث واجهة المستخدم على الخيط الرئيسي
postAdapter.setPosts(posts)
// إيقاف أي تحميل إذا كان نشطاً
if (swipeRefreshLayout.isRefreshing) {
swipeRefreshLayout.isRefreshing = false
}
// تسجيل للفحص (اختياري)
Log.d("DiscoverActivity", "Received ${posts.size} posts in realtime")
}
}
}
private fun setupSwipeRefresh() {
swipeRefreshLayout.setOnRefreshListener {
loadPosts()
}
}
private fun setupTabs() {
val tabLayout: TabLayout = findViewById(R.id.tabLayout)
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab?.position) {
0 -> filterPosts("all")
1 -> filterPosts("following")
2 -> filterPosts("trending")
}
}
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {}
})
}
private fun filterPosts(filterType: String) {
lifecycleScope.launch {
try {
val posts = when (filterType) {
"following" -> postRepository.getFollowingPosts(currentUserId)
"trending" -> postRepository.getTrendingPosts()
else -> postRepository.getPosts() // "all" or "for you"
}
postAdapter.setPosts(posts)
} catch (e: Exception) {
Toast.makeText(this@DiscoverActivity,
"Error filtering posts", Toast.LENGTH_SHORT).show()
}
}
}
private fun setupFabButton() {
val fab: ExtendedFloatingActionButton = findViewById(R.id.fabCreatePost)
fab.setOnClickListener {
showCreatePostDialog()
}
}
private fun isValidSolanaAddress(address: String): Boolean {
return address.matches("^[1-9A-HJ-NP-Za-km-z]{32,44}$".toRegex())
}
private fun isValidEvmAddress(address: String): Boolean {
return address.startsWith("0x") && address.length == 42
}
private suspend fun getTokenBalance(token: TokenInfo, walletAddress: String): BigDecimal {
return withContext(Dispatchers.IO) {
try {
when (token.tokenType) {
TokenType.SOLANA -> {
if (!isValidSolanaAddress(walletAddress)) {
return@withContext BigDecimal.ZERO
}
val contract = token.contracts["SOLANA"] ?: return@withContext BigDecimal.ZERO
if (contract.address == "So11111111111111111111111111111111111111112") {
SolanaUtils.getSolanaBalance(walletAddress)
} else {
SolanaUtils.getTokenBalance(walletAddress, contract.address)
}
}
TokenType.EVM -> {
if (!isValidEvmAddress(walletAddress)) {
return@withContext BigDecimal.ZERO
}
val contract = token.contracts.values.firstOrNull() ?: return@withContext BigDecimal.ZERO
Web3TokenUtils.getTokenBalance(
contract.address,
walletAddress,
contract.decimals
)
}
else -> BigDecimal.ZERO
}
} catch (e: Exception) {
Log.e("Balance", "Error getting ${token.symbol} balance", e)
BigDecimal.ZERO
}
}
}
private fun showCreatePostDialog() {
val dialog = BottomSheetDialog(this)
val view = layoutInflater.inflate(R.layout.dialog_create_post, null)
dialog.setContentView(view)
val etContent = view.findViewById<EditText>(R.id.etPostContent)
val btnCoin = view.findViewById<Button>(R.id.btnAddCoin)
val btnPost = view.findViewById<Button>(R.id.btnPost)
val imagePreview = view.findViewById<ImageView>(R.id.imagePreview)
val etImageUrl = view.findViewById<EditText>(R.id.etImageUrl)
var coinSymbol: String? = null
var coinPrice: Double? = null
btnCoin.setOnClickListener {
// يمكنك استبدال هذا باختيار عملة حقيقي
coinSymbol = "BTC"
coinPrice = 50000.0
btnCoin.text = "BTC"
}
btnPost.setOnClickListener {
val content = etContent.text.toString().trim()
val imageUrl = etImageUrl.text.toString().trim() // احصل على رابط الصورة من الحقل
if (imageUrl.isNotEmpty()) {
// لا نتحقق من الامتداد، نقبل الرابط كما هو
// يمكن لاحقاً عرض الصورة وتجربة تحميلها وإذا فشل العرض نبلغ المستخدم
}
// التحقق من أن المحتوى ليس فارغًا
if (content.isEmpty()) {
Toast.makeText(this, "Please enter the post content", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
// التحقق من طول المحتوى
if (content.length < 10) {
Toast.makeText(this, "The post is too short", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val wordCount = content.trim().split("\\s+".toRegex()).size
if (wordCount > 77) {
Toast.makeText(this, "The post must not exceed 77 words", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
// التحقق من الحروف العربية أو الإنجليزية فقط
// استرجاع معرف المستخدم
// التحقق من الرموز الغريبة أو الإيموجي
// التحقق من الروابط داخل النص
// كلمات خطيرة داخل النص أو الرابط
val dangerousKeywords = listOf("script", "base64", "data:", "<", ">", "onerror", "alert")
// قائمة الدومينات الممنوعة (يمكنك توسيعها لاحقًا)
val bannedDomains = listOf("porn", "xvideos", "xnxx", "phishing", "sex", "adult")
val lowerContent = content.lowercase()
val hasDangerousCode = dangerousKeywords.any { lowerContent.contains(it) }
val hasBannedDomain = bannedDomains.any { lowerContent.contains(it) }
if (hasDangerousCode || hasBannedDomain) {
Toast.makeText(this, "Your post contains unsafe or inappropriate content", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
//التحقق من التكرار
if (content.all { it in "!?.-_" }) {
Toast.makeText(this, "The post must contain meaningful text", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
// التحقق من الكلمات الممنوعة
val badWordsDetected = BadWordsFilter.detectBadWords(content)
if (badWordsDetected.isNotEmpty()) {
val message = "The post contains prohibited words: ${badWordsDetected.joinToString()}"
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
return@setOnClickListener
}
getUserProfile { name, imageUri ->
lifecycleScope.launch {
try {
var finalImageUrl: String? = null
// 1. معالجة رابط الصورة المدخل يدويًا
if (imageUrl.isNotEmpty()) {
if (isValidImageUrl(imageUrl)) {
finalImageUrl = imageUrl
Log.d("PostImage", "Image URL set: $finalImageUrl") // للتتبع
} else {
Toast.makeText(this@DiscoverActivity, "Invalid image URL format", Toast.LENGTH_SHORT).show()
return@launch
}
}
// 2. إنشاء كائن المنشور مع التأكد من تعيين postImage
val post = Post(
userId = auth.currentUser?.uid ?: "",
userName = name ?: "Anonymous",
userProfileImage = imageUri?.toString() ?: "",
postContent = content,
coinSymbol = coinSymbol,
coinPrice = coinPrice,
postImage = finalImageUrl // هذا هو الحقل المهم
)
// 3. طباعة للتحقق (يمكن حذفها لاحقًا)
Log.d("PostDebug", "Post to be saved: $post")
// 4. حفظ المنشور
val postId = postRepository.addPost(post)
Log.d("PostImage", "Post saved with ID: $postId, Image URL: ${post.postImage}")
// ✅ منح المكافأة مباشرة بعد النشر
val userId = auth.currentUser?.uid ?: return@launch
grantRewardWithTransfer(userId, 100)
// ✅ إعلام المستخدم
Toast.makeText(this@DiscoverActivity, "Post created and reward granted!", Toast.LENGTH_SHORT).show()
// 5. إغلاق النافذة وإعادة التحميل
loadPosts()
dialog.dismiss()
selectedImageUri = null
Toast.makeText(this@DiscoverActivity, "Post created successfully", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
Log.e("PostError", "Failed to create post", e)
Toast.makeText(
this@DiscoverActivity,
"Error: ${e.localizedMessage}",
Toast.LENGTH_SHORT
).show()
}
}
}
}
// عرض معاينة الصورة إذا تم اختيارها
selectedImageUri?.let {
imagePreview.visibility = View.VISIBLE
Glide.with(this).load(it).into(imagePreview)
} ?: run {
imagePreview.visibility = View.GONE
}
dialog.show()
}
override fun onLikeClick(postId: String) {
lifecycleScope.launch {
try {
postRepository.likePost(postId, currentUserId)
} catch (e: Exception) {
Toast.makeText(this@DiscoverActivity,
"Error liking post: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCommentClick(postId: String) {
lifecycleScope.launch {
try {
postRepository.incrementCommentsCount(postId)
// افتح شاشة التعليقات
val intent = Intent(this@DiscoverActivity, CommentsActivity::class.java)
intent.putExtra("postId", postId)
startActivity(intent)
} catch (e: Exception) {
Toast.makeText(this@DiscoverActivity,
"Error adding comment: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
override fun onShareClick(postId: String) {
lifecycleScope.launch {
try {
postRepository.incrementSharesCount(postId)
// مشاركة المنشور
val shareIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "https://t.me/S2LCoin/16773!")
type = "text/plain"
}
startActivity(Intent.createChooser(shareIntent, "Share post via"))
} catch (e: Exception) {
Toast.makeText(this@DiscoverActivity,
"Error sharing post: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
override fun onOptionsClick(post: Post, position: Int) {
showPostOptionsDialog(post, position)
}
private fun showPostOptionsDialog(post: Post, position: Int) {
val options = arrayOf("Delete Post", "Report", "Cancel")
val builder = androidx.appcompat.app.AlertDialog.Builder(this)
builder.setTitle("Post Options")
builder.setItems(options) { _, which ->
when (which) {
0 -> deletePost(post, position)
1 -> reportPost(post)
// 2 is Cancel
}
}
builder.show()
}
private fun getTodayPostCount(userId: String, callback: (Int) -> Unit) {
val cal = java.util.Calendar.getInstance()
cal.set(java.util.Calendar.HOUR_OF_DAY, 0)
cal.set(java.util.Calendar.MINUTE, 0)
cal.set(java.util.Calendar.SECOND, 0)
cal.set(java.util.Calendar.MILLISECOND, 0)
val todayTimestamp = cal.timeInMillis
Firebase.firestore.collection("user_post_counts")
.document(userId)
.get()
.addOnSuccessListener { document ->
val lastResetTime = document.getLong("lastReset") ?: 0L
val count = document.getLong("count")?.toInt() ?: 0
if (lastResetTime < todayTimestamp) {
// لم يتم تحديث عداد اليوم بعد، نعيده إلى صفر
callback(0)
} else {
callback(count)
}
}
.addOnFailureListener {
callback(0) // في حالة فشل الاسترجاع، نفرض أنه لا يوجد منشورات
}
}
private fun incrementTodayPostCount(userId: String) {
val cal = java.util.Calendar.getInstance()
cal.set(java.util.Calendar.HOUR_OF_DAY, 0)
cal.set(java.util.Calendar.MINUTE, 0)
cal.set(java.util.Calendar.SECOND, 0)
cal.set(java.util.Calendar.MILLISECOND, 0)
val todayTimestamp = cal.timeInMillis
val docRef = Firebase.firestore.collection("user_post_counts").document(userId)
Firebase.firestore.runTransaction { transaction ->
val snapshot = transaction.get(docRef)
val lastResetTime = snapshot.getLong("lastReset") ?: 0L
val currentCount = snapshot.getLong("count") ?: 0
if (lastResetTime < todayTimestamp) {
// بداية يوم جديد، أعد تعيين العداد ووقت التحديث
transaction.set(docRef, hashMapOf(
"count" to 1,
"lastReset" to todayTimestamp
))
} else {
// تحديث العداد بزيادة واحد
transaction.update(docRef, "count", currentCount + 1)
}
}
}
fun grantRewardWithTransfer(userId: String, rewardAmount: Int) {
Log.d("PostReward", "🎁 Granting reward of $rewardAmount S2L to user: $userId")
val firestore = Firebase.firestore
firestore.collection("walletsnew").document(userId).get()
.addOnSuccessListener { walletSnapshot ->
Log.d("RewardDebug", "📄 Wallet snapshot: ${walletSnapshot.data}")
val userAddress = walletSnapshot.getString("bscAddress")
if (userAddress.isNullOrEmpty()) {
Log.e("RewardError", "❌ User wallet address is missing")
return@addOnSuccessListener
}
val amountInDecimal = BigDecimal(rewardAmount)
// تشغيل التحويل داخل كوروتين تابع لـ Activity
lifecycleScope.launch(Dispatchers.IO) {
Log.d("RewardDebug", "🚀 Starting blockchain transfer to $userAddress")
val success = sendS2LToUser(userAddress, amountInDecimal)
withContext(Dispatchers.Main) {
if (success) {
Log.d("RewardDebug", "✅ S2L transfer succeeded to $userAddress")
firestore.collection("users").document(userId)
.update("s2lBalance", FieldValue.increment(rewardAmount.toLong()))
.addOnSuccessListener {
Log.d("RewardDebug", "✅ Firestore balance updated for $userId")
}
.addOnFailureListener {
Log.e("RewardError", "❌ Failed to update Firestore balance: ${it.localizedMessage}")
}
} else {
Log.e("RewardError", "❌ Blockchain transfer failed for $userAddress")
}
}
}
}
.addOnFailureListener {
Log.e("RewardError", "❌ Failed to get wallet info: ${it.localizedMessage}")
}
}
private suspend fun transferToken(
token: TokenInfo,
userAddress: String,
privateKey: String,
amount: BigDecimal,
recipientAddress: String
): Boolean = withContext(Dispatchers.IO) {
try {
val contract = token.contracts["BSC"]
if (contract == null) {
Log.e("TransferToken", "❌ No contract info for BSC")
return@withContext false
}
if (!recipientAddress.startsWith("0x") || recipientAddress.length != 42) {
Log.e("TransferToken", "❌ Invalid recipient address: $recipientAddress")
return@withContext false
}
val credentials = Credentials.create(privateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56)
val amountInWei = amount.multiply(BigDecimal.TEN.pow(contract.decimals)).toBigInteger()
val function = Function("transfer", listOf(Address(recipientAddress), Uint256(amountInWei)), emptyList())
val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = web3jBSC.ethGasPrice().send().gasPrice
val gasLimit = BigInteger.valueOf(100_000L)
Log.d("TransferToken", """
📤 Transaction Details:
- From: ${credentials.address}
- To (contract): ${contract.address}
- Recipient: $recipientAddress
- GasPrice: $gasPrice
- GasLimit: $gasLimit
- AmountInWei: $amountInWei
""".trimIndent())
val txHash = txManager.sendTransaction(gasPrice, gasLimit, contract.address, encodedFunction, BigInteger.ZERO).transactionHash
Log.d("TransferToken", "⛓️ txHash: $txHash")
var receipt: TransactionReceipt? = null
repeat(30) {
delay(1000)
val receiptOptional = web3jBSC.ethGetTransactionReceipt(txHash).send().transactionReceipt
receipt = receiptOptional.orElse(null)
if (receipt != null) return@repeat
}
receipt?.let {
val status = try {
it::class.java.getMethod("getStatus").invoke(it).toString()
} catch (e: Exception) {
"unknown"
}
Log.d("TransferToken", "✅ Receipt received. Status: $status")
return@withContext (status == "0x1" || status == "1")
}
Log.e("TransferToken", "❌ Receipt not received after 30 seconds")
false
} catch (e: Exception) {
Log.e("TransferToken", "❌ Exception during transfer", e)
false
}
}
private fun getDeviceIp(): String {
val wm = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val ip = wm.connectionInfo.ipAddress
return InetAddress.getByAddress(
ByteBuffer.allocate(4).putInt(ip).array().reversedArray()
).hostAddress ?: "unknown"
}
fun isIpBanned(ip: String, callback: (Boolean) -> Unit) {
val ipRef = Firebase.firestore.collection("bannedIPs").document(ip)
ipRef.get().addOnSuccessListener { doc ->
callback(doc.exists())
}.addOnFailureListener {
callback(false)
}
}
private fun canUserReceiveReward(userId: String, onResult: (Boolean) -> Unit) {
val today = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
val userRef = Firebase.firestore.collection("users").document(userId)
userRef.get().addOnSuccessListener { doc ->
val lastDate = doc.getString("lastPostDate")
val count = doc.getLong("todayPostCount") ?: 0
if (lastDate != today) {
userRef.update(mapOf("lastPostDate" to today, "todayPostCount" to 0))
onResult(true)
} else {
onResult(count < 3)
}
}.addOnFailureListener {
onResult(false)
}
}
private suspend fun sendS2LToUser(
userAddress: String,
amount: BigDecimal
): Boolean = withContext(Dispatchers.IO) {
try {
Log.d("S2LDebug", "🚀 Starting sendS2LToUser to $userAddress with amount $amount")
val adminUserId = "biLjoTseNQQg47tDa4o1JLdZ3ky2"
val firestore = FirebaseFirestore.getInstance()
val adminWallet = firestore.collection("walletsnew").document(adminUserId).get().await()
val adminPrivateKey = adminWallet.getString("bscPrivateKey")
val adminAddress = adminWallet.getString("bscAddress")
if (adminPrivateKey.isNullOrEmpty() || adminAddress.isNullOrEmpty()) {
Log.e("S2LDebug", "❌ Missing admin credentials.")
return@withContext false
}
Log.d("S2LDebug", "✅ Admin address: $adminAddress")
val s2lToken = allTokens.find { it.symbol == "S2L" }
if (s2lToken == null) {
Log.e("S2LDebug", "❌ S2L token not found")
return@withContext false
}
val adminBalance = getTokenBalance(s2lToken, adminAddress)
Log.d("S2LDebug", "🔢 Admin S2L balance: $adminBalance")
if (adminBalance < amount) {
Log.e("S2LDebug", "❌ Insufficient balance: $adminBalance < $amount")
return@withContext false
}
val success = transferToken(s2lToken, adminAddress, adminPrivateKey, amount, userAddress)
if (!success) {
Log.e("S2LDebug", "❌ transferToken failed")
return@withContext false
}
Log.d("S2LDebug", "🎉 S2L transferred successfully to $userAddress")
true
} catch (e: Exception) {
Log.e("S2LDebug", "❌ Exception in sendS2LToUser", e)
false
}
}
private fun grantReward(userId: String, amount: Int) {
val firestore = Firebase.firestore
val userRef = firestore.collection("users").document(userId)
userRef.get().addOnSuccessListener { snapshot ->
val bscAddress = snapshot.getString("bscAddress")
if (bscAddress.isNullOrEmpty()) {
Log.e("RewardError", "No BSC address found for user $userId")
return@addOnSuccessListener
}
// أضف هنا التحويل الفعلي
lifecycleScope.launch {
val sent = sendS2LToUser(
userAddress = bscAddress,
amount = BigDecimal(amount)
)
if (sent) {
Log.d("RewardDebug", "S2L transferred successfully to $bscAddress")
// (اختياري) تحديث الرصيد في Firestore
userRef.update("s2lBalance", FieldValue.increment(amount.toLong()))
} else {
Log.e("RewardError", "Blockchain transfer failed to $bscAddress")
}
}
}.addOnFailureListener { e ->
Log.e("RewardError", "Failed to fetch user data: ${e.localizedMessage}")
}
}
// داخل DiscoverActivity
private fun saveUserProfile(name: String, imageUri: Uri?) {
val user = auth.currentUser ?: return
if (imageUri != null) {
// تحقق إذا كان الرابط بعيدًا (يبدأ بـ http أو https)
if (imageUri.scheme?.startsWith("http") == true) {
// إذا كان رابطًا بعيدًا، احفظه مباشرة دون محاولة قراءته كمحلي
saveToSharedPrefsAndFirestore(name, imageUri.toString())
} else {
// إذا كان URI محليًا، استخدم الكود الحالي لرفعه
try {
contentResolver.takePersistableUriPermission(
imageUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val storageRef = Firebase.storage.reference
.child("profile_images/${user.uid}")
val inputStream = contentResolver.openInputStream(imageUri)
inputStream?.let { stream ->
val uploadTask = storageRef.putStream(stream)
uploadTask.continueWithTask { task ->
if (!task.isSuccessful) {
task.exception?.let { throw it }
}
storageRef.downloadUrl
}.addOnCompleteListener { task ->
if (task.isSuccessful) {
val downloadUri = task.result
saveToSharedPrefsAndFirestore(name, downloadUri.toString())
} else {
Log.e("Profile", "Error uploading image", task.exception)
saveToSharedPrefsAndFirestore(name, null)
Toast.makeText(this, "Failed to upload image", Toast.LENGTH_SHORT).show()
}
}
} ?: run {
saveToSharedPrefsAndFirestore(name, null)
Toast.makeText(this, "Could not read image file", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Log.e("Profile", "Error handling image", e)
saveToSharedPrefsAndFirestore(name, null)
Toast.makeText(this, "Error processing image: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
} else {
saveToSharedPrefsAndFirestore(name, null)
}
}
private fun saveToSharedPrefsAndFirestore(name: String, imageUrl: String?) {
val user = auth.currentUser ?: return
val sharedPref = getSharedPreferences("user_profile", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
putString("user_name", name)
imageUrl?.let { putString("user_image", it) }
apply()
}
// إنشاء بيانات المستخدم مع التأكد من أن imageUrl ليس null
val userData = hashMapOf<String, Any>(
"name" to name
).apply {
if (!imageUrl.isNullOrEmpty()) {
put("imageUrl", imageUrl!!)
} else {
put("imageUrl", "")
}
}
Firebase.firestore.collection("user_profiles")
.document(user.uid)
.set(userData)
.addOnSuccessListener {
Log.d("Profile", "Profile saved")
Toast.makeText(this, "Profile saved successfully", Toast.LENGTH_SHORT).show()
}
.addOnFailureListener { e ->
Log.e("Profile", "Error saving profile", e)
Toast.makeText(this, "Error saving profile", Toast.LENGTH_SHORT).show()
}
}
private fun getUserProfile(callback: (String?, Uri?) -> Unit) {
val user = auth.currentUser ?: run {
callback(null, null)
return
}
// حاول جلب من الذاكرة المحلية أولاً
val sharedPref = getSharedPreferences("user_profile", Context.MODE_PRIVATE)
val savedName = sharedPref.getString("user_name", null)
val savedImage = sharedPref.getString("user_image", null)?.let { Uri.parse(it) }
if (savedName != null && savedImage != null) {
callback(savedName, savedImage)
return
}
// إذا لم توجد في الذاكرة المحلية، جلب من Firestore
FirebaseFirestore.getInstance().collection("user_profiles")
.document(user.uid)
.get()
.addOnSuccessListener { document ->
val name = document.getString("name")
val imageUrl = document.getString("imageUrl")?.let { Uri.parse(it) }
callback(name, imageUrl)
}
.addOnFailureListener {
callback(null, null)
}
}
// داخل DiscoverActivity
private fun showProfileSetupDialog() {
val dialog = BottomSheetDialog(this)
val view = layoutInflater.inflate(R.layout.dialog_setup_profile, null)
dialog.setContentView(view)
val etName = view.findViewById<EditText>(R.id.etName)
val etImageUrl = view.findViewById<EditText>(R.id.etImageUrl)
val imagePreview = view.findViewById<ImageView>(R.id.imagePreview)
val btnSave = view.findViewById<Button>(R.id.btnSave)
var selectedImageUri: Uri? = null
// تحميل البيانات الحالية
getUserProfile { name, uri ->
name?.let { etName.setText(it) }
uri?.let {
selectedImageUri = it
etImageUrl.setText(it.toString())
Glide.with(this).load(it).into(imagePreview)
}
}
etImageUrl.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val url = etImageUrl.text.toString().trim()
if (url.isNotEmpty()) {
try {
// إنشاء URI من الرابط
val uri = Uri.parse(url)
selectedImageUri = uri
Glide.with(this).load(url).into(imagePreview)
} catch (e: Exception) {
Toast.makeText(this, "Invalid URL", Toast.LENGTH_SHORT).show()
}
}
}
}
btnSave.setOnClickListener {
val name = etName.text.toString().trim()
if (name.isEmpty()) {
Toast.makeText(this, "Please enter your name", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val imageUrl = etImageUrl.text.toString().trim()
if (imageUrl.isNotEmpty()) {
try {
selectedImageUri = Uri.parse(imageUrl)
saveUserProfile(name, selectedImageUri)
} catch (e: Exception) {
Toast.makeText(this, "Invalid image URL", Toast.LENGTH_SHORT).show()
}
} else {
saveUserProfile(name, null)
}
dialog.dismiss()
}
dialog.show()
}
// يمكنك إضافة هذا في onCreate() في Application class أو في MainActivity
// DiscoverActivity.kt
private fun deletePost(post: Post, position: Int) {
Log.d("DELETE_POST", "Attempting to delete post: ${post.id}")
lifecycleScope.launch {
try {
val isAdmin = postRepository.isUserAdmin(currentUserId)
Log.d("DELETE_POST", "User is admin: $isAdmin, Current user: $currentUserId, Post owner: ${post.userId}")
if (post.userId == currentUserId || isAdmin) {
Log.d("DELETE_POST", "User has permission to delete")
postRepository.deletePost(post.id)
Log.d("DELETE_POST", "Post deleted from repository")
val adapterPosition = postAdapter.findPostPosition(post.id)
Log.d("DELETE_POST", "Adapter position: $adapterPosition")
if (adapterPosition != -1) {
postAdapter.removePost(adapterPosition)
Log.d("DELETE_POST", "Post removed from adapter")
Toast.makeText(this@DiscoverActivity, "Post deleted", Toast.LENGTH_SHORT).show()
}
} else {
Log.d("DELETE_POST", "User doesn't have permission")
Toast.makeText(this@DiscoverActivity,
"You don't have permission to delete this post",
Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Log.e("DELETE_POST", "Error deleting post", e)
Toast.makeText(this@DiscoverActivity,
"Error deleting post: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
private fun isValidImageUrl(url: String): Boolean {
return url.isNotBlank()
}
private fun reportPost(post: Post) {
Toast.makeText(this, "Post reported", Toast.LENGTH_SHORT).show()
}
}
0 تعليقات