diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt index a3ba5af19..0b1f386dd 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt @@ -22,7 +22,6 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs import com.google.android.material.textfield.TextInputLayout @@ -30,13 +29,12 @@ import com.stripe.android.Stripe import com.stripe.android.TokenCallback import com.stripe.android.model.Card import com.stripe.android.model.Token -import kotlinx.android.synthetic.main.fragment_attendee.cardNumber -import kotlinx.android.synthetic.main.fragment_attendee.cvc -import kotlinx.android.synthetic.main.fragment_attendee.email -import kotlinx.android.synthetic.main.fragment_attendee.firstName -import kotlinx.android.synthetic.main.fragment_attendee.helloUser -import kotlinx.android.synthetic.main.fragment_attendee.lastName -import kotlinx.android.synthetic.main.fragment_attendee.postalCode +import kotlinx.android.synthetic.main.fragment_attendee.view.cvc +import kotlinx.android.synthetic.main.fragment_attendee.view.email +import kotlinx.android.synthetic.main.fragment_attendee.view.firstName +import kotlinx.android.synthetic.main.fragment_attendee.view.helloUser +import kotlinx.android.synthetic.main.fragment_attendee.view.lastName +import kotlinx.android.synthetic.main.fragment_attendee.view.postalCode import kotlinx.android.synthetic.main.fragment_attendee.view.attendeeScrollView import kotlinx.android.synthetic.main.fragment_attendee.view.accept import kotlinx.android.synthetic.main.fragment_attendee.view.amount @@ -48,6 +46,7 @@ import kotlinx.android.synthetic.main.fragment_attendee.view.month import kotlinx.android.synthetic.main.fragment_attendee.view.monthText import kotlinx.android.synthetic.main.fragment_attendee.view.moreAttendeeInformation import kotlinx.android.synthetic.main.fragment_attendee.view.paymentSelector +import kotlinx.android.synthetic.main.fragment_attendee.view.paymentSelectorContainer import kotlinx.android.synthetic.main.fragment_attendee.view.progressBarAttendee import kotlinx.android.synthetic.main.fragment_attendee.view.qty import kotlinx.android.synthetic.main.fragment_attendee.view.register @@ -57,7 +56,7 @@ import kotlinx.android.synthetic.main.fragment_attendee.view.stripePayment import kotlinx.android.synthetic.main.fragment_attendee.view.ticketDetails import kotlinx.android.synthetic.main.fragment_attendee.view.ticketsRecycler import kotlinx.android.synthetic.main.fragment_attendee.view.time -import kotlinx.android.synthetic.main.fragment_attendee.view.view +import kotlinx.android.synthetic.main.fragment_attendee.view.ticketTableDetails import kotlinx.android.synthetic.main.fragment_attendee.view.year import kotlinx.android.synthetic.main.fragment_attendee.view.yearText import kotlinx.android.synthetic.main.fragment_attendee.view.cardNumber @@ -67,6 +66,7 @@ import kotlinx.android.synthetic.main.fragment_attendee.view.countryPickerContai import org.fossasia.openevent.general.BuildConfig import org.fossasia.openevent.general.R import org.fossasia.openevent.general.attendees.forms.CustomForm +import org.fossasia.openevent.general.auth.User import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.EventId import org.fossasia.openevent.general.event.EventUtils @@ -78,10 +78,12 @@ import org.fossasia.openevent.general.utils.Utils.isNetworkConnected import org.fossasia.openevent.general.utils.extensions.nonNull import org.fossasia.openevent.general.utils.nullToEmpty import org.koin.androidx.viewmodel.ext.android.viewModel -import java.util.Currency import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.longSnackbar import org.jetbrains.anko.design.snackbar +import java.util.Calendar +import java.util.Currency +import kotlin.collections.ArrayList class AttendeeFragment : Fragment() { @@ -89,28 +91,27 @@ class AttendeeFragment : Fragment() { private val attendeeViewModel by viewModel() private val ticketsRecyclerAdapter: TicketDetailsRecyclerAdapter = TicketDetailsRecyclerAdapter() private val attendeeRecyclerAdapter: AttendeeRecyclerAdapter = AttendeeRecyclerAdapter() - private lateinit var linearLayoutManager: LinearLayoutManager private val safeArgs: AttendeeFragmentArgs by navArgs() - private lateinit var eventId: EventId - private var ticketIdAndQty: List>? = null - private var selectedPaymentOption: Int = -1 - private lateinit var paymentCurrency: String - private var expiryMonth: Int = -1 - private var expiryYear: String? = null - private var cardBrand: String? = null private lateinit var API_KEY: String - private var singleTicket = false - private var identifierList = ArrayList() - private var editTextList = ArrayList() - private var amount: Float = 0.0f override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - eventId = EventId(safeArgs.eventId) - ticketIdAndQty = safeArgs.ticketIdAndQty?.value - singleTicket = ticketIdAndQty?.map { it.second }?.sum() == 1 + if (attendeeViewModel.ticketIdAndQty == null) { + attendeeViewModel.ticketIdAndQty = safeArgs.ticketIdAndQty?.value + attendeeViewModel.singleTicket = safeArgs.ticketIdAndQty?.value?.map { it.second }?.sum() == 1 + } API_KEY = BuildConfig.STRIPE_API_KEY + + attendeeRecyclerAdapter.setEventId(safeArgs.eventId) + if (attendeeViewModel.paymentCurrency.isNotBlank()) + ticketsRecyclerAdapter.setCurrency(attendeeViewModel.paymentCurrency) + safeArgs.ticketIdAndQty?.value?.let { + val quantities = it.map { pair -> pair.second }.filter { it != 0 } + ticketsRecyclerAdapter.setQuantity(quantities) + attendeeRecyclerAdapter.setQuantity(quantities) + } + attendeeViewModel.forms.value?.let { attendeeRecyclerAdapter.setCustomForm(it) } } override fun onCreateView( @@ -122,106 +123,267 @@ class AttendeeFragment : Fragment() { setToolbar(activity, getString(R.string.attendee_details)) setHasOptionsMenu(true) - val paragraph = SpannableStringBuilder() - val startText = getString(R.string.start_text) - val termsText = getString(R.string.terms_text) - val middleText = getString(R.string.middle_text) - val privacyText = getString(R.string.privacy_text) + attendeeViewModel.message + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.longSnackbar(it) + }) - paragraph.append(startText) - paragraph.append(" $termsText") - paragraph.append(" $middleText") - paragraph.append(" $privacyText") + attendeeViewModel.progress + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.progressBarAttendee.isVisible = it + rootView.register.isEnabled = !it + }) - val termsSpan = object : ClickableSpan() { - override fun updateDrawState(ds: TextPaint?) { - super.updateDrawState(ds) - ds?.isUnderlineText = false - } + setupEventInfo() + setupTicketDetailTable() + setupUser() + setupAttendeeDetails() + setupCustomForms() + setupPaymentOptions() + setupCountryOptions() + setupCardNumber() + setupCardType() + setupMonthOptions() + setupYearOptions() + setupTermsAndCondition() + setupRegisterOrder() - override fun onClick(widget: View) { - Utils.openUrl(requireContext(), getString(R.string.terms_of_service)) - } - } + return rootView + } - val privacyPolicySpan = object : ClickableSpan() { - override fun updateDrawState(ds: TextPaint?) { - super.updateDrawState(ds) - ds?.isUnderlineText = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val attendeeDetailChangeListener = object : AttendeeDetailChangeListener { + override fun onAttendeeDetailChanged(attendee: Attendee, position: Int) { + attendeeViewModel.attendees[position] = attendee } + } + attendeeRecyclerAdapter.apply { + attendeeChangeListener = attendeeDetailChangeListener + } + } - override fun onClick(widget: View) { - Utils.openUrl(requireContext(), getString(R.string.privacy_policy)) - } + override fun onResume() { + super.onResume() + if (!isNetworkConnected(context)) { + rootView.progressBarAttendee.isVisible = false + rootView.attendeeScrollView.longSnackbar(getString(R.string.no_internet_connection_message)) } + } - paragraph.setSpan(termsSpan, startText.length, startText.length + termsText.length + 2, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - paragraph.setSpan(privacyPolicySpan, paragraph.length - privacyText.length, paragraph.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) // -1 so that we don't include "." in the link + override fun onDestroyView() { + super.onDestroyView() + attendeeRecyclerAdapter.attendeeChangeListener = null + } - rootView.accept.text = paragraph - rootView.accept.movementMethod = LinkMovementMethod.getInstance() + private fun setupEventInfo() { + attendeeViewModel.event + .nonNull() + .observe(viewLifecycleOwner, Observer { + loadEventDetailsUI(it) + }) + val currentEvent = attendeeViewModel.event.value + if (currentEvent == null) + attendeeViewModel.loadEvent(safeArgs.eventId) + else + loadEventDetailsUI(currentEvent) + } + + private fun setupTicketDetailTable() { + rootView.qty.text = " — ${attendeeViewModel.ticketIdAndQty?.map { it.second }?.sum()} items" rootView.ticketsRecycler.layoutManager = LinearLayoutManager(activity) rootView.ticketsRecycler.adapter = ticketsRecyclerAdapter rootView.ticketsRecycler.isNestedScrollingEnabled = false + rootView.ticketTableDetails.setOnClickListener { + attendeeViewModel.ticketDetailsVisible = !attendeeViewModel.ticketDetailsVisible + loadTicketDetailsTableUI(attendeeViewModel.ticketDetailsVisible) + } + loadTicketDetailsTableUI(attendeeViewModel.ticketDetailsVisible) + + attendeeViewModel.totalAmount + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.paymentSelectorContainer.visibility = if (it > 0) View.VISIBLE else View.GONE + rootView.countryPickerContainer.visibility = if (it > 0) View.VISIBLE else View.GONE + rootView.amount.text = "Total: ${attendeeViewModel.paymentCurrency}$it" + }) + + attendeeViewModel.tickets + .nonNull() + .observe(viewLifecycleOwner, Observer { tickets -> + ticketsRecyclerAdapter.addAll(tickets) + if (!attendeeViewModel.singleTicket) + attendeeRecyclerAdapter.addAllTickets(tickets) + }) + + val currentTickets = attendeeViewModel.tickets.value + val currentTotalPrice = attendeeViewModel.totalAmount.value + if (currentTickets != null && currentTotalPrice != null) { + rootView.paymentSelector.visibility = if (currentTotalPrice > 0) View.VISIBLE else View.GONE + rootView.amount.text = "Total: ${attendeeViewModel.paymentCurrency}$currentTotalPrice" + + ticketsRecyclerAdapter.addAll(currentTickets) + attendeeRecyclerAdapter.addAllTickets(currentTickets) + } else { + attendeeViewModel.getTickets() + } + } + + private fun setupUser() { + attendeeViewModel.user + .nonNull() + .observe(viewLifecycleOwner, Observer { user -> + loadUserUI(user) + if (attendeeViewModel.singleTicket) { + val pos = attendeeViewModel.ticketIdAndQty?.map { it.second }?.indexOf(1) + val ticket = pos?.let { it1 -> attendeeViewModel.ticketIdAndQty?.get(it1)?.first?.toLong() } ?: -1 + val attendee = Attendee(id = attendeeViewModel.getId(), + firstname = rootView.firstName.text.toString(), + lastname = rootView.lastName.text.toString(), + city = getAttendeeField("city"), + address = getAttendeeField("address"), + state = getAttendeeField("state"), + email = rootView.email.text.toString(), + ticket = TicketId(ticket), + event = EventId(safeArgs.eventId)) + attendeeViewModel.attendees.clear() + attendeeViewModel.attendees.add(attendee) + } + }) + + rootView.signOut.setOnClickListener { + AlertDialog.Builder(requireContext()).setMessage(getString(R.string.message)) + .setPositiveButton(getString(R.string.logout)) { _, _ -> + attendeeViewModel.logout() + activity?.onBackPressed() + } + .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> dialog.cancel() } + .show() + } + + val currentUser = attendeeViewModel.user.value + if (currentUser == null) + attendeeViewModel.loadUser() + else + loadUserUI(currentUser) + } + + private fun setupAttendeeDetails() { + if (attendeeViewModel.singleTicket) + rootView.attendeeRecycler.visibility = View.GONE rootView.attendeeRecycler.layoutManager = LinearLayoutManager(activity) rootView.attendeeRecycler.adapter = attendeeRecyclerAdapter rootView.attendeeRecycler.isNestedScrollingEnabled = false - linearLayoutManager = LinearLayoutManager(context) - linearLayoutManager.orientation = RecyclerView.VERTICAL - rootView.ticketsRecycler.layoutManager = linearLayoutManager - - attendeeViewModel.ticketDetails(ticketIdAndQty) + if (attendeeViewModel.attendees.isEmpty()) { + if (attendeeViewModel.singleTicket) { + val pos = attendeeViewModel.ticketIdAndQty?.map { it.second }?.indexOf(1) + val ticket = pos?.let { it1 -> attendeeViewModel.ticketIdAndQty?.get(it1)?.first?.toLong() } ?: -1 + val attendee = Attendee(id = attendeeViewModel.getId(), + firstname = rootView.firstName.text.toString(), + lastname = rootView.lastName.text.toString(), + city = getAttendeeField("city"), + address = getAttendeeField("address"), + state = getAttendeeField("state"), + email = rootView.email.text.toString(), + ticket = TicketId(ticket), + event = EventId(safeArgs.eventId)) + attendeeViewModel.attendees.add(attendee) + } else { + attendeeViewModel.ticketIdAndQty?.let { + it.forEach { pair -> + repeat(pair.second) { + attendeeViewModel.attendees.add(Attendee( + id = attendeeViewModel.getId(), + firstname = "", lastname = "", city = getAttendeeField("city"), + address = getAttendeeField("address"), state = getAttendeeField("state"), + email = "", ticket = TicketId(pair.first.toLong()), event = EventId(safeArgs.eventId) + )) + } + } + } + } + } + attendeeRecyclerAdapter.addAllAttendees(attendeeViewModel.attendees) + } - attendeeViewModel.updatePaymentSelectorVisibility(ticketIdAndQty) - val paymentOptions = ArrayList() - paymentOptions.add(getString(R.string.paypal)) - paymentOptions.add(getString(R.string.stripe)) - attendeeViewModel.paymentSelectorVisibility + private fun setupCustomForms() { + attendeeViewModel.forms .nonNull() .observe(viewLifecycleOwner, Observer { - if (it) { - rootView.paymentSelector.visibility = View.VISIBLE - } else { - rootView.paymentSelector.visibility = View.GONE + if (attendeeViewModel.singleTicket) { + fillInformationSection(it) + if (it.isNotEmpty()) { + rootView.moreAttendeeInformation.visibility = View.VISIBLE + } } + attendeeRecyclerAdapter.setCustomForm(it) }) - rootView.paymentSelector.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, - paymentOptions) - rootView.paymentSelector.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(p0: AdapterView<*>?) { - // Do nothing - } - override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) { - selectedPaymentOption = position - if (position == paymentOptions.indexOf(getString(R.string.stripe))) - rootView.stripePayment.visibility = View.VISIBLE - else - rootView.stripePayment.visibility = View.GONE + val currentForms = attendeeViewModel.forms.value + if (currentForms == null) { + attendeeViewModel.getCustomFormsForAttendees(safeArgs.eventId) + } else { + if (attendeeViewModel.singleTicket) { + fillInformationSection(currentForms) + if (currentForms.isNotEmpty()) { + rootView.moreAttendeeInformation.visibility = View.VISIBLE + } } + attendeeRecyclerAdapter.setCustomForm(currentForms) } + } - attendeeViewModel.initializeSpinner() - + private fun setupCountryOptions() { ArrayAdapter.createFromResource( requireContext(), R.array.country_arrays, android.R.layout.simple_spinner_dropdown_item ).also { adapter -> rootView.countryPicker.adapter = adapter - autoSetCurrentCountry() + if (attendeeViewModel.countryPosition == -1) + autoSetCurrentCountry() + else + rootView.countryPicker.setSelection(attendeeViewModel.countryPosition) } + rootView.countryPicker.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { /*Do nothing*/ } - rootView.cardNumber.addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable?) { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + attendeeViewModel.countryPosition = position } + } + rootView.countryPickerContainer.visibility = if (attendeeViewModel.singleTicket) View.VISIBLE else View.GONE + } + + private fun setupPaymentOptions() { + val paymentOptions = ArrayList() + paymentOptions.add(getString(R.string.paypal)) + paymentOptions.add(getString(R.string.stripe)) + rootView.paymentSelector.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, + paymentOptions) + rootView.paymentSelector.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(p0: AdapterView<*>?) { /*Do nothing*/ } - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) { + attendeeViewModel.selectedPaymentOption = position + if (position == paymentOptions.indexOf(getString(R.string.stripe))) + rootView.stripePayment.visibility = View.VISIBLE + else + rootView.stripePayment.visibility = View.GONE } + } + if (attendeeViewModel.selectedPaymentOption != -1) + rootView.paymentSelector.setSelection(attendeeViewModel.selectedPaymentOption) + } + + private fun setupCardNumber() { + rootView.cardNumber.addTextChangedListener(object : TextWatcher { + override fun afterTextChanged(s: Editable?) { /*Do Nothing*/ } + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { /*Do Nothing*/ } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if (s == null || s.length < 3) { @@ -248,7 +410,7 @@ class AttendeeFragment : Fragment() { setCardSelectorAndError(pos, visibility = false, error = false) } - fun setCardSelectorAndError(pos: Int, visibility: Boolean, error: Boolean) { + private fun setCardSelectorAndError(pos: Int, visibility: Boolean, error: Boolean) { rootView.cardSelector.setSelection(pos, true) rootView.cardSelector.isVisible = visibility if (error) { @@ -258,175 +420,135 @@ class AttendeeFragment : Fragment() { rootView.cardNumber.error = null } }) + } + + private fun setupMonthOptions() { + val month = ArrayList() + month.add(getString(R.string.month_string)) + month.add(getString(R.string.january)) + month.add(getString(R.string.february)) + month.add(getString(R.string.march)) + month.add(getString(R.string.april)) + month.add(getString(R.string.may)) + month.add(getString(R.string.june)) + month.add(getString(R.string.july)) + month.add(getString(R.string.august)) + month.add(getString(R.string.september)) + month.add(getString(R.string.october)) + month.add(getString(R.string.november)) + month.add(getString(R.string.december)) + rootView.month.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, - attendeeViewModel.month) + month) rootView.month.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(p0: AdapterView<*>?) { - // Do nothing - } + override fun onNothingSelected(p0: AdapterView<*>?) { /* Do nothing */ } override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { - expiryMonth = p2 - rootView.monthText.text = attendeeViewModel.month[p2] + attendeeViewModel.monthSelectedPosition = p2 + rootView.monthText.text = month[p2] } } rootView.monthText.setOnClickListener { rootView.month.performClick() } + rootView.month.setSelection(attendeeViewModel.monthSelectedPosition) + } + + private fun setupYearOptions() { + val year = ArrayList() + val currentYear = Calendar.getInstance().get(Calendar.YEAR) + year.add(getString(R.string.year_string)) + val a = currentYear + 20 + for (i in currentYear..a) { + year.add(i.toString()) + } + rootView.year.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, - attendeeViewModel.year) + year) rootView.year.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(p0: AdapterView<*>?) { - // Do nothing - } + override fun onNothingSelected(p0: AdapterView<*>?) { /* Do nothing */ } - override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { - expiryYear = attendeeViewModel.year[p2] - if (expiryYear == "Year") - expiryYear = "2017" // invalid year, if the user hasn't selected the year - rootView.yearText.text = attendeeViewModel.year[p2] + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, pos: Int, p3: Long) { + attendeeViewModel.yearSelectedPosition = pos + rootView.yearText.text = year[pos] } } rootView.yearText.setOnClickListener { rootView.year.performClick() } + rootView.year.setSelection(attendeeViewModel.yearSelectedPosition) + } + + private fun setupCardType() { + val cardType = ArrayList() + cardType.add(getString(R.string.select_card)) + cardType.add(getString(R.string.american_express_pay_message)) + cardType.add(getString(R.string.mastercard_pay_message)) + cardType.add(getString(R.string.visa_pay_message)) + cardType.add(getString(R.string.discover_pay_message)) + cardType.add(getString(R.string.diners_pay_message)) + cardType.add(getString(R.string.unionpay_pay_message)) + rootView.cardSelector.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, - attendeeViewModel.cardType) + cardType) rootView.cardSelector.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(p0: AdapterView<*>?) { - } - - override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { - cardBrand = attendeeViewModel.cardType[p2] - rootView.selectCard.text = cardBrand - } - } - attendeeViewModel.qtyList - .nonNull() - .observe(viewLifecycleOwner, Observer { - ticketsRecyclerAdapter.setQty(it) - }) + override fun onNothingSelected(p0: AdapterView<*>?) { /* Do nothing */ } - rootView.view.setOnClickListener { - val currentVisibility: Boolean = attendeeViewModel.ticketDetailsVisibility.value ?: false - if (rootView.view.text == context?.getString(R.string.view)) { - rootView.ticketDetails.visibility = View.VISIBLE - rootView.view.text = context?.getString(R.string.hide) - } else { - attendeeViewModel.ticketDetailsVisibility.value = !currentVisibility - rootView.ticketDetails.visibility = View.GONE - rootView.view.text = context?.getString(R.string.view) + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, pos: Int, p3: Long) { + attendeeViewModel.cardTypePosition = pos + rootView.selectCard.text = cardType[pos] } } - attendeeViewModel.ticketDetailsVisibility - .nonNull() - .observe(viewLifecycleOwner, Observer { - rootView.view.text = if (it) getString(R.string.hide) else getString(R.string.view) - rootView.ticketDetails.visibility = if (it) View.VISIBLE else View.GONE - }) - - attendeeViewModel.message - .nonNull() - .observe(viewLifecycleOwner, Observer { - rootView.longSnackbar(it) - }) - - attendeeViewModel.progress - .nonNull() - .observe(viewLifecycleOwner, Observer { - rootView.progressBarAttendee.isVisible = it - rootView.register.isEnabled = !it - }) - - attendeeViewModel.event - .nonNull() - .observe(viewLifecycleOwner, Observer { - loadEventDetails(it) - }) - - attendeeViewModel.totalAmount - .nonNull() - .observe(viewLifecycleOwner, Observer { - amount = it - }) - - attendeeRecyclerAdapter.eventId = eventId - attendeeViewModel.tickets - .nonNull() - .observe(viewLifecycleOwner, Observer { tickets -> - ticketsRecyclerAdapter.addAll(tickets) - ticketsRecyclerAdapter.notifyDataSetChanged() - if (!singleTicket) - tickets.forEach { ticket -> - val pos = ticketIdAndQty?.map { it.first }?.indexOf(ticket.id) - val iterations = pos?.let { ticketIdAndQty?.get(it)?.second } ?: 0 - for (i in 0 until iterations) - attendeeRecyclerAdapter.add(Attendee(attendeeViewModel.getId()), ticket) - attendeeRecyclerAdapter.notifyDataSetChanged() - } - }) + rootView.cardSelector.setSelection(attendeeViewModel.cardTypePosition) + } - attendeeViewModel.totalQty - .nonNull() - .observe(viewLifecycleOwner, Observer { - rootView.qty.text = " — $it items" - }) + private fun setupTermsAndCondition() { + val paragraph = SpannableStringBuilder() + val startText = getString(R.string.start_text) + val termsText = getString(R.string.terms_text) + val middleText = getString(R.string.middle_text) + val privacyText = getString(R.string.privacy_text) - attendeeViewModel.countryVisibility - .nonNull() - .observe(viewLifecycleOwner, Observer { - if (singleTicket) { - rootView.countryPickerContainer.visibility = if (it) View.VISIBLE else View.GONE - } - }) + paragraph.append(startText) + paragraph.append(" $termsText") + paragraph.append(" $middleText") + paragraph.append(" $privacyText") - attendeeViewModel.paymentCompleted - .nonNull() - .observe(viewLifecycleOwner, Observer { - if (it) - openOrderCompletedFragment() - }) + val termsSpan = object : ClickableSpan() { + override fun updateDrawState(ds: TextPaint?) { + super.updateDrawState(ds) + ds?.isUnderlineText = false + } - attendeeViewModel.loadUser() - attendeeViewModel.loadEvent(safeArgs.eventId) + override fun onClick(widget: View) { + Utils.openUrl(requireContext(), getString(R.string.terms_of_service)) + } + } - attendeeViewModel.attendee - .nonNull() - .observe(viewLifecycleOwner, Observer { user -> - helloUser.text = "Hello ${user.firstName.nullToEmpty()}" - firstName.text = Editable.Factory.getInstance().newEditable(user.firstName.nullToEmpty()) - lastName.text = Editable.Factory.getInstance().newEditable(user.lastName.nullToEmpty()) - email.text = Editable.Factory.getInstance().newEditable(user.email.nullToEmpty()) - }) + val privacyPolicySpan = object : ClickableSpan() { + override fun updateDrawState(ds: TextPaint?) { + super.updateDrawState(ds) + ds?.isUnderlineText = false + } - rootView.signOut.setOnClickListener { - AlertDialog.Builder(requireContext()).setMessage(resources.getString(R.string.message)) - .setPositiveButton(resources.getString(R.string.logout)) { _, _ -> - attendeeViewModel.logout() - activity?.onBackPressed() - } - .setNegativeButton(resources.getString(R.string.cancel)) { dialog, _ -> dialog.cancel() } - .show() + override fun onClick(widget: View) { + Utils.openUrl(requireContext(), getString(R.string.privacy_policy)) + } } - attendeeViewModel.getCustomFormsForAttendees(eventId.id) + paragraph.setSpan(termsSpan, startText.length, startText.length + termsText.length + 2, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + paragraph.setSpan(privacyPolicySpan, paragraph.length - privacyText.length, paragraph.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) // -1 so that we don't include "." in the link - attendeeViewModel.forms - .nonNull() - .observe(viewLifecycleOwner, Observer { - if (singleTicket) - fillInformationSection(it) - attendeeRecyclerAdapter.setCustomForm(it) - if (singleTicket) - if (!it.isEmpty()) { - rootView.moreAttendeeInformation.visibility = View.VISIBLE - } - attendeeRecyclerAdapter.notifyDataSetChanged() - }) + rootView.accept.text = paragraph + rootView.accept.movementMethod = LinkMovementMethod.getInstance() + } + private fun setupRegisterOrder() { rootView.register.setOnClickListener { if (!isNetworkConnected(context)) { rootView.attendeeScrollView.longSnackbar(getString(R.string.no_internet_connection_message)) @@ -441,34 +563,12 @@ class AttendeeFragment : Fragment() { builder.setTitle(R.string.confirmation_dialog) builder.setPositiveButton(android.R.string.yes) { dialog, which -> - val attendees = ArrayList() - if (singleTicket) { - val pos = ticketIdAndQty?.map { it.second }?.indexOf(1) - val ticket = pos?.let { it1 -> ticketIdAndQty?.get(it1)?.first?.toLong() } ?: -1 - val attendee = Attendee(id = attendeeViewModel.getId(), - firstname = firstName.text.toString(), - lastname = lastName.text.toString(), - city = getAttendeeField("city"), - address = getAttendeeField("address"), - state = getAttendeeField("state"), - email = email.text.toString(), - ticket = TicketId(ticket), - event = eventId) - attendees.add(attendee) - } else { - attendees.addAll(attendeeRecyclerAdapter.attendeeList) - } + val attendees = attendeeViewModel.attendees if (attendeeViewModel.areAttendeeEmailsValid(attendees)) { val country = rootView.countryPicker.selectedItem.toString() - attendeeViewModel.createAttendees(attendees, country, paymentOptions[selectedPaymentOption]) - - attendeeViewModel.isAttendeeCreated.observe(viewLifecycleOwner, Observer { isAttendeeCreated -> - if (isAttendeeCreated && selectedPaymentOption == - paymentOptions.indexOf(getString(R.string.stripe))) { - sendToken() - } - }) + val paymentOption = rootView.paymentSelector.selectedItem.toString() + attendeeViewModel.createAttendees(attendees, country, paymentOption) } else { rootView.attendeeScrollView.longSnackbar(getString(R.string.invalid_email_address_message)) } @@ -478,22 +578,27 @@ class AttendeeFragment : Fragment() { rootView.snackbar(getString(R.string.order_not_completed)) } builder.show() - - attendeeViewModel.ticketSoldOut - .nonNull() - .observe(this, Observer { - showTicketSoldOutDialog(it) - }) } - return rootView - } - override fun onResume() { - super.onResume() - if (!isNetworkConnected(context)) { - rootView.progressBarAttendee.isVisible = false - rootView.attendeeScrollView.longSnackbar(getString(R.string.no_internet_connection_message)) - } + attendeeViewModel.isAttendeeCreated.observe(viewLifecycleOwner, Observer { isAttendeeCreated -> + if (isAttendeeCreated && + rootView.paymentSelector.selectedItem.toString() == getString(R.string.stripe)) { + sendToken() + } + }) + + attendeeViewModel.ticketSoldOut + .nonNull() + .observe(this, Observer { + showTicketSoldOutDialog(it) + }) + + attendeeViewModel.paymentCompleted + .nonNull() + .observe(viewLifecycleOwner, Observer { + if (it) + openOrderCompletedFragment() + }) } private fun showTicketSoldOutDialog(show: Boolean) { @@ -506,9 +611,10 @@ class AttendeeFragment : Fragment() { } private fun sendToken() { - val card = Card(cardNumber.text.toString(), expiryMonth, expiryYear?.toInt(), cvc.text.toString()) + val card = Card(rootView.cardNumber.text.toString(), attendeeViewModel.monthSelectedPosition, + attendeeViewModel.yearSelectedPosition, rootView.cvc.text.toString()) card.addressCountry = rootView.countryPicker.selectedItem.toString() - card.addressZip = postalCode.text.toString() + card.addressZip = rootView.postalCode.text.toString() if (card.brand != null && card.brand != "Unknown") rootView.selectCard.text = "Pay by ${card.brand}" @@ -531,16 +637,14 @@ class AttendeeFragment : Fragment() { }) } - private fun loadEventDetails(event: Event) { + private fun loadEventDetailsUI(event: Event) { val dateString = StringBuilder() val startsAt = EventUtils.getEventDateTime(event.startsAt, event.timezone) val endsAt = EventUtils.getEventDateTime(event.endsAt, event.timezone) - val currency = Currency.getInstance(event.paymentCurrency) - paymentCurrency = currency.symbol - ticketsRecyclerAdapter.setCurrency(paymentCurrency) + attendeeViewModel.paymentCurrency = Currency.getInstance(event.paymentCurrency).symbol + ticketsRecyclerAdapter.setCurrency(attendeeViewModel.paymentCurrency) rootView.eventName.text = "${event.name} - ${EventUtils.getFormattedDate(startsAt)}" - rootView.amount.text = "Total: $paymentCurrency$amount" rootView.time.text = dateString.append(EventUtils.getFormattedDate(startsAt)) .append(" - ") .append(EventUtils.getFormattedDate(endsAt)) @@ -548,6 +652,23 @@ class AttendeeFragment : Fragment() { .append(EventUtils.getFormattedTime(startsAt)) } + private fun loadUserUI(user: User) { + rootView.helloUser.text = "Hello ${user.firstName.nullToEmpty()}" + rootView.firstName.text = Editable.Factory.getInstance().newEditable(user.firstName.nullToEmpty()) + rootView.lastName.text = Editable.Factory.getInstance().newEditable(user.lastName.nullToEmpty()) + rootView.email.text = Editable.Factory.getInstance().newEditable(user.email.nullToEmpty()) + } + + private fun loadTicketDetailsTableUI(show: Boolean) { + if (show) { + rootView.ticketDetails.visibility = View.VISIBLE + rootView.ticketTableDetails.text = context?.getString(R.string.hide) + } else { + rootView.ticketDetails.visibility = View.GONE + rootView.ticketTableDetails.text = context?.getString(R.string.view) + } + } + private fun openOrderCompletedFragment() { attendeeViewModel.paymentCompleted.value = false findNavController(rootView).navigate(AttendeeFragmentDirections @@ -574,15 +695,15 @@ class AttendeeFragment : Fragment() { inputLayout.addView(editTextSection) inputLayout.setPadding(0, 0, 0, 20) layout.addView(inputLayout) - identifierList.add(form.fieldIdentifier) - editTextList.add(editTextSection) + attendeeViewModel.identifierList.add(form.fieldIdentifier) + attendeeViewModel.editTextList.add(editTextSection) } } } private fun getAttendeeField(identifier: String): String { - val index = identifierList.indexOf(identifier) - return if (index == -1) "" else index.let { editTextList[it] }.text.toString() + val index = attendeeViewModel.identifierList.indexOf(identifier) + return if (index == -1) "" else index.let { attendeeViewModel.editTextList[it] }.text.toString() } private fun autoSetCurrentCountry() { diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeRecyclerAdapter.kt index fd3fc8428..77353cb7f 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeRecyclerAdapter.kt @@ -5,32 +5,45 @@ import android.view.LayoutInflater import android.view.ViewGroup import org.fossasia.openevent.general.R import org.fossasia.openevent.general.attendees.forms.CustomForm -import org.fossasia.openevent.general.event.EventId import org.fossasia.openevent.general.ticket.Ticket class AttendeeRecyclerAdapter : RecyclerView.Adapter() { - val attendeeList = ArrayList() - val ticketList = ArrayList() - var eventId = EventId(-1) - val customForm = ArrayList() + private val attendeeList = ArrayList() + private val ticketList = ArrayList() + private var qty = ArrayList() + private val customForm = ArrayList() + private var eventId: Long = -1 + var attendeeChangeListener: AttendeeDetailChangeListener? = null - fun addAll(attendeeList: List, ticketList: List) { - if (attendeeList.isNotEmpty()) - this.attendeeList.clear() - this.attendeeList.addAll(attendeeList) - if (ticketList.isNotEmpty()) - this.ticketList.clear() - this.ticketList.addAll(ticketList) + fun setEventId(newId: Long) { + eventId = newId } - fun setCustomForm(customForm: List) { - this.customForm.clear() - this.customForm.addAll(customForm) + fun setQuantity(ticketQuantities: List) { + if (qty.isNotEmpty())qty.clear() + qty.addAll(ticketQuantities) + } + + fun addAllTickets(tickets: List) { + if (ticketList.isNotEmpty()) ticketList.clear() + tickets.forEachIndexed { index, ticket -> + repeat(qty[index]) { + ticketList.add(ticket) + } + } + notifyDataSetChanged() + } + + fun addAllAttendees(attendees: List) { + if (attendeeList.isNotEmpty()) attendeeList.clear() + attendeeList.addAll(attendees) + notifyDataSetChanged() } - fun add(attendeeList: Attendee, ticket: Ticket) { - this.attendeeList.add(attendeeList) - this.ticketList.add(ticket) + fun setCustomForm(customForm: List) { + if (customForm.isNotEmpty()) this.customForm.clear() + this.customForm.addAll(customForm) + notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AttendeeViewHolder { @@ -39,10 +52,18 @@ class AttendeeRecyclerAdapter : RecyclerView.Adapter() { } override fun onBindViewHolder(holder: AttendeeViewHolder, position: Int) { - holder.bind(this, position) + holder.apply { + if (attendeeList.size == ticketList.size) + bind(attendeeList[position], ticketList[position], customForm, position, eventId) + onAttendeeDetailChanged = attendeeChangeListener + } } override fun getItemCount(): Int { return attendeeList.size } } + +interface AttendeeDetailChangeListener { + fun onAttendeeDetailChanged(attendee: Attendee, position: Int) +} diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewHolder.kt index f2bd613a8..3f49c81d0 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewHolder.kt @@ -8,62 +8,56 @@ import android.view.View import android.widget.EditText import kotlinx.android.synthetic.main.item_attendee.view.* import org.fossasia.openevent.general.attendees.forms.CustomForm +import org.fossasia.openevent.general.event.EventId +import org.fossasia.openevent.general.ticket.Ticket import org.fossasia.openevent.general.ticket.TicketId class AttendeeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private var identifierList = ArrayList() private var editTextList = ArrayList() - lateinit var textWatcher: TextWatcher + var onAttendeeDetailChanged: AttendeeDetailChangeListener? = null - fun bind(attendeeRecyclerAdapter: AttendeeRecyclerAdapter, position: Int) { + fun bind(attendee: Attendee, ticket: Ticket, customForm: List, position: Int, eventId: Long) { - setText(itemView.attendeeItemCountry, attendeeRecyclerAdapter.attendeeList[position].country) - setText(itemView.attendeeItemLastName, attendeeRecyclerAdapter.attendeeList[position].lastname) - setText(itemView.attendeeItemEmail, attendeeRecyclerAdapter.attendeeList[position].email) - setText(itemView.attendeeItemFirstName, attendeeRecyclerAdapter.attendeeList[position].firstname) - itemView.attendeeItemTicketName.text = "Ticket Name - ${attendeeRecyclerAdapter.ticketList[position].name}" + itemView.attendeeItemCountry.setText(attendee.country) + itemView.attendeeItemLastName.setText(attendee.lastname) + itemView.attendeeItemEmail.setText(attendee.email) + itemView.attendeeItemFirstName.setText(attendee.firstname) + itemView.attendeeItemTicketName.text = "Ticket Name - ${ticket.name}" - textWatcher = object : TextWatcher { - override fun afterTextChanged(p0: Editable?) { - val id = attendeeRecyclerAdapter.attendeeList[position].id - attendeeRecyclerAdapter.attendeeList.removeAt(position) - val attendee = Attendee(id, firstname = itemView.attendeeItemFirstName.text.toString(), - lastname = itemView.attendeeItemLastName.text.toString(), - email = itemView.attendeeItemEmail.text.toString(), - city = getAttendeeField("city"), - address = getAttendeeField("address"), - state = getAttendeeField("state"), - country = itemView.attendeeItemCountry.text.toString(), - ticket = TicketId(attendeeRecyclerAdapter.ticketList[position].id.toLong()), - event = attendeeRecyclerAdapter.eventId) - attendeeRecyclerAdapter.attendeeList.add(position, attendee) + val textWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable?) { + val newAttendee = Attendee( + id = attendee.id, + firstname = itemView.attendeeItemFirstName.text.toString(), + lastname = itemView.attendeeItemLastName.text.toString(), + email = itemView.attendeeItemEmail.text.toString(), + city = getAttendeeField("city"), + address = getAttendeeField("address"), + state = getAttendeeField("state"), + country = itemView.attendeeItemCountry.text.toString(), + ticket = TicketId(ticket.id.toLong()), + event = EventId(eventId)) + onAttendeeDetailChanged?.onAttendeeDetailChanged(newAttendee, position) } - override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { - } - - override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { - } + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { /*Do nothing*/ } + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { /*Do nothing*/ } } itemView.attendeeItemFirstName.addTextChangedListener(textWatcher) itemView.attendeeItemLastName.addTextChangedListener(textWatcher) itemView.attendeeItemEmail.addTextChangedListener(textWatcher) itemView.attendeeItemCountry.addTextChangedListener(textWatcher) + fillInformationSection(customForm, textWatcher) - fillInformationSection(attendeeRecyclerAdapter.customForm) - if (attendeeRecyclerAdapter.customForm.isEmpty()) itemView.moreAttendeeInformation.visibility = View.GONE - val price = attendeeRecyclerAdapter.ticketList[position].price + if (customForm.isEmpty()) itemView.moreAttendeeInformation.visibility = View.GONE + val price = ticket.price if ((price != null && price.equals(0.toFloat())) || price == null) { itemView.countryArea.visibility = View.GONE } } - fun setText(editText: EditText, string: String?) { - if (!string.isNullOrEmpty()) - editText.setText(string) - } - - private fun fillInformationSection(forms: List) { + private fun fillInformationSection(forms: List, textWatcher: TextWatcher) { val layout = itemView.attendeeInformation for (form in forms) { if (form.type == "text") { @@ -80,7 +74,7 @@ class AttendeeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } } - fun getAttendeeField(identifier: String): String { + private fun getAttendeeField(identifier: String): String { val index = identifierList.indexOf(identifier) return if (index == -1) "" else index.let { editTextList[it] }.text.toString() } diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewModel.kt index d525d1905..f071f88af 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeViewModel.kt @@ -1,6 +1,7 @@ package org.fossasia.openevent.general.attendees import android.util.Patterns +import android.widget.EditText import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -25,7 +26,6 @@ import org.fossasia.openevent.general.ticket.Ticket import org.fossasia.openevent.general.ticket.TicketService import org.fossasia.openevent.general.utils.HttpErrors import timber.log.Timber -import java.util.Calendar class AttendeeViewModel( private val attendeeService: AttendeeService, @@ -47,119 +47,64 @@ class AttendeeViewModel( val message: LiveData = mutableMessage private val mutableEvent = MutableLiveData() val event: LiveData = mutableEvent - private val mutableAttendee = MutableLiveData() - val attendee: LiveData = mutableAttendee - private val mutablePaymentSelectorVisibility = MutableLiveData() - val paymentSelectorVisibility: LiveData = mutablePaymentSelectorVisibility - private val mutableTotalAmount = MutableLiveData() + private val mutableUser = MutableLiveData() + val user: LiveData = mutableUser + private val mutableTotalAmount = MutableLiveData(0F) val totalAmount: LiveData = mutableTotalAmount - private val mutableCountryVisibility = MutableLiveData() - val countryVisibility: LiveData = mutableCountryVisibility - private val mutableTotalQty = MutableLiveData() - val totalQty: LiveData = mutableTotalQty - private val mutableQtyList = MutableLiveData>() - val qtyList: LiveData> = mutableQtyList val paymentCompleted = MutableLiveData() - val ticketDetailsVisibility = MutableLiveData() - private val mutableTickets = MutableLiveData>() - val tickets: LiveData> = mutableTickets + private val mutableTickets = MutableLiveData>() + val tickets: LiveData> = mutableTickets private val mutableForms = MutableLiveData>() val forms: LiveData> = mutableForms private val mutableIsAttendeeCreated = MutableLiveData() val isAttendeeCreated: LiveData = mutableIsAttendeeCreated - val month = ArrayList() - val year = ArrayList() val attendees = ArrayList() - val cardType = ArrayList() - var isAllDetailsFilled = true + private val attendeesForOrder = ArrayList() + private val ticketsForOrder = ArrayList() + private lateinit var paymentOption: String + private lateinit var countryForOrder: String private var createAttendeeIterations = 0 - var country: String? = null - private set var orderIdentifier: String? = null private set - private lateinit var paymentOption: String private lateinit var confirmOrder: ConfirmOrder - fun getId() = authHolder.getId() - - fun initializeSpinner() { - // initialize months - month.add(resource.getString(R.string.month_string)) - month.add(resource.getString(R.string.january)) - month.add(resource.getString(R.string.february)) - month.add(resource.getString(R.string.march)) - month.add(resource.getString(R.string.april)) - month.add(resource.getString(R.string.may)) - month.add(resource.getString(R.string.june)) - month.add(resource.getString(R.string.july)) - month.add(resource.getString(R.string.august)) - month.add(resource.getString(R.string.september)) - month.add(resource.getString(R.string.october)) - month.add(resource.getString(R.string.november)) - month.add(resource.getString(R.string.december)) - - // initialize years - val currentYear = Calendar.getInstance().get(Calendar.YEAR) - year.add(resource.getString(R.string.year_string)) - val a = currentYear + 20 - for (i in currentYear..a) { - year.add(i.toString()) - } + // Retained information + var countryPosition: Int = -1 + var ticketIdAndQty: List>? = null + var selectedPaymentOption: Int = -1 + var singleTicket = false + var monthSelectedPosition: Int = 0 + var yearSelectedPosition: Int = 0 + var cardTypePosition: Int = 0 + var identifierList = ArrayList() + var editTextList = ArrayList() + var paymentCurrency: String = "" + var ticketDetailsVisible = false - // initialize card types - cardType.add(resource.getString(R.string.select_card)) - cardType.add(resource.getString(R.string.american_express_pay_message)) - cardType.add(resource.getString(R.string.mastercard_pay_message)) - cardType.add(resource.getString(R.string.visa_pay_message)) - cardType.add(resource.getString(R.string.discover_pay_message)) - cardType.add(resource.getString(R.string.diners_pay_message)) - cardType.add(resource.getString(R.string.unionpay_pay_message)) - } + fun getId() = authHolder.getId() - fun updatePaymentSelectorVisibility(ticketIdAndQty: List>?) { + fun getTickets() { val ticketIds = ArrayList() val qty = ArrayList() - mutableTotalQty.value = 0 - ticketIdAndQty?.forEach { if (it.second > 0) { ticketIds.add(it.first) qty.add(it.second) - mutableTotalQty.value = totalQty.value?.plus(it.second) } } - mutableQtyList.value = qty - compositeDisposable += ticketService.getTicketPriceWithIds(ticketIds) + compositeDisposable += ticketService.getTicketsWithIds(ticketIds) .withDefaultSchedulers() - .subscribe({ prices -> - var total = 0.toFloat() + .subscribe({ tickets -> + var prices = 0F var index = 0 - prices?.forEach { - total += it * qty[index++] + tickets.forEach { + it.price?.let { price -> prices += price * qty[index++] } } - mutableTotalAmount.value = total - mutableCountryVisibility.value = total > 0 - mutablePaymentSelectorVisibility.value = total != 0.toFloat() - }, { - Timber.e(it, "Error Loading tickets!") - }) - } - - fun ticketDetails(ticketIdAndQty: List>?) { - val ticketIds = ArrayList() - ticketIdAndQty?.forEach { - if (it.second > 0) { - ticketIds.add(it.first) - } - } - - compositeDisposable += ticketService.getTicketsWithIds(ticketIds) - .withDefaultSchedulers() - .subscribe({ - mutableTickets.value = it as MutableList? + mutableTickets.value = tickets + mutableTotalAmount.value = prices }, { Timber.e(it, "Error Loading tickets!") }) @@ -175,13 +120,13 @@ class AttendeeViewModel( if (createAttendeeIterations == totalAttendee) mutableProgress.value = false }.subscribe({ - attendees.add(it) - if (attendees.size == totalAttendee) { + attendeesForOrder.add(it) + if (attendeesForOrder.size == totalAttendee) { loadTicketsAndCreateOrder() mutableIsAttendeeCreated.value = true mutableMessage.value = resource.getString(R.string.create_attendee_success_message) } - Timber.d("Success! %s", attendees.toList().toString()) + Timber.d("Success! %s", attendeesForOrder.toList().toString()) }, { if (createAttendeeIterations + 1 == totalAttendee) if (it.message.equals(HttpErrors.CONFLICT)) { @@ -195,10 +140,10 @@ class AttendeeViewModel( } fun createAttendees(attendees: List, country: String?, paymentOption: String) { - this.country = country + attendeesForOrder.clear() + this.countryForOrder = country ?: "" this.paymentOption = paymentOption - this.attendees.clear() - isAllDetailsFilled = true + var isAllDetailsFilled = true createAttendeeIterations = 0 attendees.forEach { if (it.email.isNullOrBlank() || it.firstname.isNullOrBlank() || it.lastname.isNullOrBlank()) { @@ -217,11 +162,8 @@ class AttendeeViewModel( } private fun loadTicketsAndCreateOrder() { - if (this.tickets.value == null) { - this.mutableTickets.value = ArrayList() - } - this.tickets.value?.clear() - attendees.forEach { + ticketsForOrder.clear() + attendeesForOrder.forEach { loadTicket(it.ticket?.id) } } @@ -234,9 +176,9 @@ class AttendeeViewModel( compositeDisposable += ticketService.getTicketDetails(ticketId) .withDefaultSchedulers() .subscribe({ - tickets.value?.add(it) - Timber.d("Loaded tickets! %s", tickets.value?.toList().toString()) - if (tickets.value?.size == attendees.size) { + ticketsForOrder.add(it) + Timber.d("Loaded tickets! %s", ticketsForOrder.toList().toString()) + if (ticketsForOrder.size == attendeesForOrder.size) { createOrder() } }, { @@ -245,17 +187,16 @@ class AttendeeViewModel( } private fun createOrder() { - val attendeeList = attendees.map { AttendeeId(it.id) }.toList() - var amount: Float = totalAmount.value ?: 0F + val attendeeList = attendeesForOrder.map { AttendeeId(it.id) }.toList() + val amount: Float = totalAmount.value ?: 0F var paymentMode: String? = paymentOption.toLowerCase() if (amount <= 0) { paymentMode = resource.getString(R.string.free) - amount = 0F } val eventId = event.value?.id if (eventId != null) { val order = Order( - getId(), paymentMode, country, "pending", amount, + getId(), paymentMode, countryForOrder, "pending", amount, attendees = attendeeList, event = EventId(eventId) ) compositeDisposable += orderService.placeOrder(order) @@ -333,7 +274,6 @@ class AttendeeViewModel( mutableProgress.value = false }.subscribe({ mutableMessage.value = it.message - if (it.status != null && it.status) { confirmOrderStatus(orderIdentifier.toString(), confirmOrder) Timber.d("Successfully charged for the order!") @@ -356,7 +296,7 @@ class AttendeeViewModel( mutableEvent.value = it }, { Timber.e(it, "Error fetching event %d", id) - mutableMessage.value = "Error fetching event" + mutableMessage.value = resource.getString(R.string.error_fetching_event_message) }) } @@ -368,7 +308,7 @@ class AttendeeViewModel( compositeDisposable += attendeeService.getAttendeeDetails(id) .withDefaultSchedulers() .subscribe({ - mutableAttendee.value = it + mutableUser.value = it }, { Timber.e(it, "Error fetching user %d", id) }) diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDao.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDao.kt index dbec01fa9..807c6526c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDao.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDao.kt @@ -21,9 +21,6 @@ interface TicketDao { @Query("SELECT * from Ticket WHERE id = :id") fun getTicketDetails(id: Long): Single - @Query("SELECT price from Ticket WHERE id in (:ids)") - fun getTicketPriceWithIds(ids: List): Single> - @Query("SELECT * from Ticket WHERE id in (:ids)") fun getTicketsWithIds(ids: List): Single> } diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsRecyclerAdapter.kt index 997b6f096..a0968673a 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsRecyclerAdapter.kt @@ -15,25 +15,26 @@ class TicketDetailsRecyclerAdapter : RecyclerView.Adapter) { + if (qty.isNotEmpty())qty.clear() + qty.addAll(ticketQuantities) + notifyDataSetChanged() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TicketDetailsViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_ticket_details, parent, false) return TicketDetailsViewHolder(view) } - fun setQty(qty: ArrayList) { - this.qty = qty - } - override fun onBindViewHolder(holder: TicketDetailsViewHolder, position: Int) { - val event = tickets[position] - - holder.bind(event, qty, eventCurrency) + holder.bind(tickets[position], qty[position], eventCurrency) } override fun getItemCount(): Int { diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsViewHolder.kt index bde4e2a33..a54478062 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketDetailsViewHolder.kt @@ -5,7 +5,7 @@ import android.view.View import kotlinx.android.synthetic.main.item_ticket_details.view.* class TicketDetailsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - fun bind(ticket: Ticket, qty: ArrayList, eventCurrency: String?) { + fun bind(ticket: Ticket, ticketQuantity: Int, eventCurrency: String?) { itemView.ticketName.text = ticket.name if (ticket.price != null) { @@ -21,8 +21,8 @@ class TicketDetailsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView itemView.price.text = "Free" } - val subTotal: Float? = ticket.price?.times(qty[adapterPosition]) - itemView.qty.text = qty[adapterPosition].toString() + val subTotal: Float? = ticket.price?.times(ticketQuantity) + itemView.qty.text = ticketQuantity.toString() itemView.subTotal.text = "$$subTotal" } } diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketService.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketService.kt index e4e1e8227..68c5919c8 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketService.kt @@ -25,10 +25,6 @@ class TicketService(private val ticketApi: TicketApi, private val ticketDao: Tic return ticketDao.getTicketDetails(id) } - fun getTicketPriceWithIds(ids: List): Single> { - return ticketDao.getTicketPriceWithIds(ids) - } - fun getTicketsWithIds(ids: List): Single> { return ticketDao.getTicketsWithIds(ids) } diff --git a/app/src/main/res/layout/fragment_attendee.xml b/app/src/main/res/layout/fragment_attendee.xml index e5eee4a7b..b9fa81d7d 100644 --- a/app/src/main/res/layout/fragment_attendee.xml +++ b/app/src/main/res/layout/fragment_attendee.xml @@ -63,7 +63,7 @@ tools:text="10 items" /> - + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginBottom="@dimen/layout_margin_small"> + + + CVC Postal Code Payment Info + Payment Not you? Sign out All fees included in price