messenger-android

Android graphical user interfaces for GNUnet Messenger
Log | Files | Refs | README | LICENSE

MemberListFragment.kt (4764B)


      1 /*
      2    This file is part of GNUnet.
      3    Copyright (C) 2021--2025 GNUnet e.V.
      4 
      5    GNUnet is free software: you can redistribute it and/or modify it
      6    under the terms of the GNU Affero General Public License as published
      7    by the Free Software Foundation, either version 3 of the License,
      8    or (at your option) any later version.
      9 
     10    GNUnet is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    Affero General Public License for more details.
     14 
     15    You should have received a copy of the GNU Affero General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18    SPDX-License-Identifier: AGPL3.0-or-later
     19  */
     20 /*
     21  * @author t3sserakt
     22  * @file GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/chat/MemberListFragment.kt
     23  */
     24 
     25 package org.gnunet.gnunetmessenger.ui.chat
     26 
     27 import android.os.Bundle
     28 import android.util.Log
     29 import android.view.View
     30 import android.widget.Button
     31 import android.widget.TextView
     32 import androidx.fragment.app.Fragment
     33 import androidx.fragment.app.activityViewModels
     34 import androidx.lifecycle.Observer
     35 import androidx.lifecycle.lifecycleScope
     36 import androidx.navigation.fragment.findNavController
     37 import androidx.navigation.fragment.navArgs
     38 import androidx.recyclerview.widget.LinearLayoutManager
     39 import androidx.recyclerview.widget.RecyclerView
     40 import kotlinx.coroutines.launch
     41 import org.gnunet.gnunetmessenger.MainActivity
     42 import org.gnunet.gnunetmessenger.R
     43 import org.gnunet.gnunetmessenger.model.ChatContact
     44 import org.gnunet.gnunetmessenger.ui.adapters.ContactListAdapter
     45 import org.gnunet.gnunetmessenger.viewmodel.ContactListViewModel
     46 
     47 class MemberListFragment : Fragment(R.layout.fragment_member_list) {
     48 
     49     private val args: MemberListFragmentArgs by navArgs()
     50     private lateinit var adapter: ContactListAdapter
     51     private val viewModel: ContactListViewModel by activityViewModels()
     52 
     53     private var existingMemberKeys: Set<String> = emptySet()
     54     private var latestContacts: List<ChatContact> = emptyList()
     55 
     56     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     57         val chatGroup = args.chatGroup
     58         Log.d("MemberListFragment", "Opened for group '${chatGroup.name}' userPointer=${chatGroup.userPointer}")
     59         val mainActivity = requireActivity() as MainActivity
     60         val gnunetChat = mainActivity.getGnunetChatInstance()
     61 
     62         val recyclerView = view.findViewById<RecyclerView>(R.id.memberRecyclerView)
     63         val inviteButton = view.findViewById<Button>(R.id.confirmButton)
     64         val hint = view.findViewById<TextView>(R.id.memberListHint)
     65 
     66         recyclerView.layoutManager = LinearLayoutManager(requireContext())
     67         adapter = ContactListAdapter()
     68         recyclerView.adapter = adapter
     69 
     70         fun renderList() {
     71             val invitable = latestContacts.filter {
     72                 it.key.isBlank() || it.key !in existingMemberKeys
     73             }
     74             adapter.submitList(invitable)
     75             hint.text = if (invitable.isEmpty()) {
     76                 "No contacts available to invite. Share your identity via a lobby to add contacts first."
     77             } else {
     78                 "Select contacts to invite"
     79             }
     80         }
     81 
     82         viewModel.contacts.observe(viewLifecycleOwner, Observer { contactList ->
     83             latestContacts = contactList ?: emptyList()
     84             renderList()
     85         })
     86 
     87         // Refresh the contact list each time this screen opens so stale/empty
     88         // view-model state doesn't leave the list blank.
     89         mainActivity.loadChats()
     90 
     91         // Load existing members asynchronously so we can filter them out.
     92         viewLifecycleOwner.lifecycleScope.launch {
     93             existingMemberKeys = try {
     94                 gnunetChat.listGroupContacts(chatGroup)
     95                     .mapNotNull { it.key.takeIf { k -> k.isNotBlank() } }
     96                     .toSet()
     97             } catch (t: Throwable) {
     98                 Log.w("MemberListFragment", "listGroupContacts failed", t)
     99                 emptySet()
    100             }
    101             renderList()
    102         }
    103 
    104         inviteButton.setOnClickListener {
    105             val selectedContacts = adapter.getSelectedContacts()
    106             Log.d("MemberListFragment", "Inviting ${selectedContacts.size} contacts to group '${chatGroup.name}' (groupPtr=${chatGroup.userPointer})")
    107             for (contact in selectedContacts) {
    108                 Log.d("MemberListFragment", "  Inviting contact '${contact.name}' (contactPtr=${contact.userPointer}, key=${contact.key.take(8)})")
    109                 gnunetChat.inviteContactToGroup(chatGroup, contact)
    110             }
    111             findNavController().popBackStack()
    112         }
    113     }
    114 }