From 5689d3fdfea8364055e21cb6a399bfb691453cff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 7 May 2019 21:11:34 +0000 Subject: [PATCH 01/35] chore(deps): bump lifecycle_version from 2.1.0-alpha04 to 2.2.0-alpha01 Bumps `lifecycle_version` from 2.1.0-alpha04 to 2.2.0-alpha01. Updates `lifecycle-extensions` from 2.1.0-alpha04 to 2.2.0-alpha01 Updates `lifecycle-common-java8` from 2.1.0-alpha04 to 2.2.0-alpha01 Updates `lifecycle-reactivestreams` from 2.1.0-alpha04 to 2.2.0-alpha01 Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 0e51f4861..bd64ee611 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,7 +95,7 @@ repositories { } dependencies { - def lifecycle_version = "2.1.0-alpha04" + def lifecycle_version = "2.2.0-alpha01" def koin_version = "1.0.2" def roomVersion = "2.1.0-alpha07" def ktx_version = "1.0.0" From 61b588a0d41336d04f8be0e78f326002eaf4379f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 9 May 2019 00:41:52 +0530 Subject: [PATCH 02/35] chore(deps): bump recyclerview from 1.1.0-alpha04 to 1.1.0-alpha05 (#1726) Bumps recyclerview from 1.1.0-alpha04 to 1.1.0-alpha05. Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index bd64ee611..471879cd3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -108,7 +108,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.1.0-alpha04' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha04' + implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05' implementation 'com.google.android.material:material:1.1.0-alpha06' implementation "androidx.browser:browser:1.0.0" implementation 'androidx.exifinterface:exifinterface:1.0.0' From 01897add326cdb3f6f992e4716b28a710cb448a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 9 May 2019 00:42:18 +0530 Subject: [PATCH 03/35] chore(deps): bump nav_version from 2.1.0-alpha02 to 2.1.0-alpha03 (#1724) Bumps `nav_version` from 2.1.0-alpha02 to 2.1.0-alpha03. Updates `navigation-fragment-ktx` from 2.1.0-alpha02 to 2.1.0-alpha03 Updates `navigation-ui-ktx` from 2.1.0-alpha02 to 2.1.0-alpha03 Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 471879cd3..55e691f10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -100,7 +100,7 @@ dependencies { def roomVersion = "2.1.0-alpha07" def ktx_version = "1.0.0" def ktx2_version = "2.0.0" - def nav_version = "2.1.0-alpha02" + def nav_version = "2.1.0-alpha03" def anko_version = "0.10.8" implementation fileTree(dir: 'libs', include: ['*.jar']) From 3a2cfea3eaa689b54119823cfb72784129144055 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 9 May 2019 00:42:31 +0530 Subject: [PATCH 04/35] chore(deps): bump appcompat from 1.1.0-alpha04 to 1.1.0-alpha05 (#1723) Bumps appcompat from 1.1.0-alpha04 to 1.1.0-alpha05. Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 55e691f10..87f85005d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -105,7 +105,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.multidex:multidex:2.0.1' - implementation 'androidx.appcompat:appcompat:1.1.0-alpha04' + implementation 'androidx.appcompat:appcompat:1.1.0-alpha05' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05' From 47def26cb765c188c710fd27c3de3058ada89f00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 9 May 2019 00:42:44 +0530 Subject: [PATCH 05/35] chore(deps): bump roomVersion from 2.1.0-alpha07 to 2.1.0-beta01 (#1721) Bumps `roomVersion` from 2.1.0-alpha07 to 2.1.0-beta01. Updates `room-runtime` from 2.1.0-alpha07 to 2.1.0-beta01 Updates `room-rxjava2` from 2.1.0-alpha07 to 2.1.0-beta01 Updates `room-compiler` from 2.1.0-alpha07 to 2.1.0-beta01 Updates `room-testing` from 2.1.0-alpha07 to 2.1.0-beta01 Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 87f85005d..853a54249 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -97,7 +97,7 @@ repositories { dependencies { def lifecycle_version = "2.2.0-alpha01" def koin_version = "1.0.2" - def roomVersion = "2.1.0-alpha07" + def roomVersion = "2.1.0-beta01" def ktx_version = "1.0.0" def ktx2_version = "2.0.0" def nav_version = "2.1.0-alpha03" From 8fee9a0eef2fd5442aa11741311b1faff94fd71a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 9 May 2019 03:01:51 +0530 Subject: [PATCH 06/35] chore(deps): bump threetenbp from 1.3.8 to 1.4.0 (#1739) Bumps [threetenbp](https://github.com/ThreeTen/threetenbp) from 1.3.8 to 1.4.0. - [Release notes](https://github.com/ThreeTen/threetenbp/releases) - [Changelog](https://github.com/ThreeTen/threetenbp/blob/master/RELEASE-NOTES.md) - [Commits](https://github.com/ThreeTen/threetenbp/compare/v1.3.8...v1.4.0) Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 853a54249..ed35935e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation "io.mockk:mockk:1.9.3" - testImplementation 'org.threeten:threetenbp:1.3.8' + testImplementation 'org.threeten:threetenbp:1.4.0' testImplementation "org.koin:koin-test:$koin_version" testImplementation 'androidx.arch.core:core-testing:2.0.1' androidTestImplementation 'androidx.test:runner:1.1.1' From 99b15324f4181cd1f948df379ebfd4be644be68a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 9 May 2019 03:02:11 +0530 Subject: [PATCH 07/35] chore(deps): bump constraintlayout from 2.0.0-alpha5 to 2.0.0-beta1 (#1738) Bumps constraintlayout from 2.0.0-alpha5 to 2.0.0-beta1. Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ed35935e1..6effc3054 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,7 +106,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.appcompat:appcompat:1.1.0-alpha05' - implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05' implementation 'com.google.android.material:material:1.1.0-alpha06' From bc9508451ac5ebd1706708f77b848f2b416ac348 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Fri, 10 May 2019 06:11:57 +0200 Subject: [PATCH 08/35] feat: Add enlarged QR Image + Increase brightness (#1727) Detail: - Set up click listener to enlarge QR Image - Increate brightness when clicking on QR Image. - Setting margin of created QR image small so that QR Image Dialog can contain large image. Fixes: #1215 --- .../general/order/OrderDetailsFragment.kt | 29 +++++- .../order/OrderDetailsRecyclerAdapter.kt | 14 ++- .../general/order/OrderDetailsViewHolder.kt | 95 ++++++++++--------- .../general/order/OrderDetailsViewModel.kt | 3 +- .../openevent/general/order/QrCode.kt | 5 +- .../layout-land/item_card_order_details.xml | 2 + .../res/layout/item_card_order_details.xml | 2 + app/src/main/res/layout/item_enlarged_qr.xml | 9 ++ 8 files changed, 108 insertions(+), 51 deletions(-) create mode 100644 app/src/main/res/layout/item_enlarged_qr.xml diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt index 7d31a77ce..463d48c33 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt @@ -1,5 +1,6 @@ package org.fossasia.openevent.general.order +import android.app.AlertDialog import android.content.Intent import android.graphics.Bitmap import android.graphics.Canvas @@ -24,6 +25,7 @@ import kotlinx.android.synthetic.main.fragment_order_details.view.orderDetailCoo import kotlinx.android.synthetic.main.fragment_order_details.view.orderDetailsRecycler import kotlinx.android.synthetic.main.fragment_order_details.view.progressBar import kotlinx.android.synthetic.main.item_card_order_details.view.orderDetailCardView +import kotlinx.android.synthetic.main.item_enlarged_qr.view.enlargedQrImage import org.fossasia.openevent.general.BuildConfig import org.fossasia.openevent.general.R import org.fossasia.openevent.general.utils.extensions.nonNull @@ -102,7 +104,15 @@ class OrderDetailsFragment : Fragment() { } } - ordersRecyclerAdapter.setListener(eventDetailsListener) + ordersRecyclerAdapter.setSeeEventListener(eventDetailsListener) + + val qrImageListener = object : OrderDetailsRecyclerAdapter.QrImageClickListener { + override fun onClick(qrImage: Bitmap) { + showEnlargedQrImage(qrImage) + } + } + + ordersRecyclerAdapter.setQrImageClickListener(qrImageListener) orderDetailsViewModel.progress .nonNull() @@ -141,6 +151,23 @@ class OrderDetailsFragment : Fragment() { } } + private fun showEnlargedQrImage(bitmap: Bitmap) { + val brightAttributes = activity?.window?.attributes + orderDetailsViewModel.brightness = brightAttributes?.screenBrightness + brightAttributes?.screenBrightness = 1f + activity?.window?.attributes = brightAttributes + + val dialogLayout = layoutInflater.inflate(R.layout.item_enlarged_qr, null) + dialogLayout.enlargedQrImage.setImageBitmap(bitmap) + AlertDialog.Builder(requireContext()) + .setOnDismissListener { + val attributes = activity?.window?.attributes + attributes?.screenBrightness = orderDetailsViewModel.brightness + activity?.window?.attributes = attributes + }.setView(dialogLayout) + .create().show() + } + private fun shareCurrentTicket() { val currentTicketViewHolder = rootView.orderDetailsRecycler.findViewHolderForAdapterPosition(orderDetailsViewModel.currentTicketPosition) diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt index 9e45822d8..f2c1aeac5 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt @@ -1,5 +1,6 @@ package org.fossasia.openevent.general.order +import android.graphics.Bitmap import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.ViewGroup @@ -14,6 +15,7 @@ class OrderDetailsRecyclerAdapter : RecyclerView.Adapter private var event: Event? = null private var orderIdentifier: String? = null private var eventDetailsListener: EventDetailsListener? = null + private var onQrImageClicked: QrImageClickListener? = null fun addAll(attendeeList: List) { if (attendees.isNotEmpty()) @@ -25,10 +27,14 @@ class OrderDetailsRecyclerAdapter : RecyclerView.Adapter this.event = event } - fun setListener(listener: EventDetailsListener) { + fun setSeeEventListener(listener: EventDetailsListener) { eventDetailsListener = listener } + fun setQrImageClickListener(listener: QrImageClickListener) { + onQrImageClicked = listener + } + fun setOrderIdentifier(orderId: String?) { orderIdentifier = orderId } @@ -40,7 +46,7 @@ class OrderDetailsRecyclerAdapter : RecyclerView.Adapter override fun onBindViewHolder(holder: OrderDetailsViewHolder, position: Int) { val order = attendees[position] - holder.bind(order, event, orderIdentifier, eventDetailsListener) + holder.bind(order, event, orderIdentifier, eventDetailsListener, onQrImageClicked) } override fun getItemCount(): Int { @@ -50,4 +56,8 @@ class OrderDetailsRecyclerAdapter : RecyclerView.Adapter interface EventDetailsListener { fun onClick(eventID: Long) } + + interface QrImageClickListener { + fun onClick(qrImage: Bitmap) + } } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt index c81461800..d2dfd6e17 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt @@ -32,67 +32,70 @@ class OrderDetailsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) attendee: Attendee, event: Event?, orderIdentifier: String?, - eventDetailsListener: OrderDetailsRecyclerAdapter.EventDetailsListener? + eventDetailsListener: OrderDetailsRecyclerAdapter.EventDetailsListener?, + qrImageClickListener: OrderDetailsRecyclerAdapter.QrImageClickListener? ) { - if (event != null) { - val formattedDateTime = EventUtils.getEventDateTime(event.startsAt, event.timezone) - val formattedDate = EventUtils.getFormattedDateShort(formattedDateTime) - val formattedTime = EventUtils.getFormattedTime(formattedDateTime) - val timezone = EventUtils.getFormattedTimeZone(formattedDateTime) + if (event == null) return + val formattedDateTime = EventUtils.getEventDateTime(event.startsAt, event.timezone) + val formattedDate = EventUtils.getFormattedDateShort(formattedDateTime) + val formattedTime = EventUtils.getFormattedTime(formattedDateTime) + val timezone = EventUtils.getFormattedTimeZone(formattedDateTime) - itemView.eventName.text = event.name - itemView.location.text = event.locationName - itemView.date.text = "$formattedDate\n$formattedTime $timezone" - itemView.eventSummary.text = event.description?.stripHtml() + itemView.eventName.text = event.name + itemView.location.text = event.locationName + itemView.date.text = "$formattedDate\n$formattedTime $timezone" + itemView.eventSummary.text = event.description?.stripHtml() - if (event.organizerName.isNullOrEmpty()) { - itemView.organizerLabel.visibility = View.GONE - } else { - itemView.organizer.text = event.organizerName - } - itemView.map.setOnClickListener { - val mapUrl = loadMapUrl(event) - val mapIntent = Intent(Intent.ACTION_VIEW, Uri.parse(mapUrl)) - val packageManager = itemView.context?.packageManager - if (packageManager != null && mapIntent.resolveActivity(packageManager) != null) { - itemView.context.startActivity(mapIntent) - } + if (event.organizerName.isNullOrEmpty()) { + itemView.organizerLabel.visibility = View.GONE + } else { + itemView.organizer.text = event.organizerName + } + itemView.map.setOnClickListener { + val mapUrl = loadMapUrl(event) + val mapIntent = Intent(Intent.ACTION_VIEW, Uri.parse(mapUrl)) + val packageManager = itemView.context?.packageManager + if (packageManager != null && mapIntent.resolveActivity(packageManager) != null) { + itemView.context.startActivity(mapIntent) } - if (!attendee.pdfUrl.isNullOrBlank()) { - itemView.downloadButton.isEnabled = true - itemView.downloadButton.setOnClickListener { - itemView.context.browse(attendee.pdfUrl) - } + } + if (!attendee.pdfUrl.isNullOrBlank()) { + itemView.downloadButton.isEnabled = true + itemView.downloadButton.setOnClickListener { + itemView.context.browse(attendee.pdfUrl) } + } - itemView.calendar.setOnClickListener { - val intent = Intent(Intent.ACTION_INSERT) - intent.type = "vnd.android.cursor.item/event" - intent.putExtra(CalendarContract.Events.TITLE, event.name) - intent.putExtra(CalendarContract.Events.DESCRIPTION, event.description?.stripHtml()) - intent.putExtra(CalendarContract.Events.EVENT_LOCATION, event.locationName) - intent.putExtra(CalendarContract.Events.CALENDAR_TIME_ZONE, event.timezone) - intent.putExtra( - CalendarContract.EXTRA_EVENT_BEGIN_TIME, - EventUtils.getTimeInMilliSeconds(event.startsAt, event.timezone)) - intent.putExtra( - CalendarContract.EXTRA_EVENT_END_TIME, - EventUtils.getTimeInMilliSeconds(event.endsAt, event.timezone)) - itemView.context.startActivity(intent) - } + itemView.calendar.setOnClickListener { + val intent = Intent(Intent.ACTION_INSERT) + intent.type = "vnd.android.cursor.item/event" + intent.putExtra(CalendarContract.Events.TITLE, event.name) + intent.putExtra(CalendarContract.Events.DESCRIPTION, event.description?.stripHtml()) + intent.putExtra(CalendarContract.Events.EVENT_LOCATION, event.locationName) + intent.putExtra(CalendarContract.Events.CALENDAR_TIME_ZONE, event.timezone) + intent.putExtra( + CalendarContract.EXTRA_EVENT_BEGIN_TIME, + EventUtils.getTimeInMilliSeconds(event.startsAt, event.timezone)) + intent.putExtra( + CalendarContract.EXTRA_EVENT_END_TIME, + EventUtils.getTimeInMilliSeconds(event.endsAt, event.timezone)) + itemView.context.startActivity(intent) + } - itemView.eventDetails.setOnClickListener { - eventDetailsListener?.onClick(event.id) - } + itemView.eventDetails.setOnClickListener { + eventDetailsListener?.onClick(event.id) } itemView.name.text = "${attendee.firstname} ${attendee.lastname}" val ticketIdentifier = "$orderIdentifier-${attendee.id}" itemView.orderIdentifier.text = ticketIdentifier - val bitmap = qrCode.generateQrBitmap(ticketIdentifier, 200, 200) + val bitmap = qrCode.generateQrBitmap(ticketIdentifier, 400, 400) if (bitmap != null) { itemView.qrCodeView.setImageBitmap(bitmap) + itemView.qrCodeView.setOnClickListener { + qrImageClickListener?.onClick(bitmap) + } } else { itemView.qrCodeView.visibility = View.GONE } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt index f3dcb05b5..dc64365f6 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt @@ -29,9 +29,10 @@ class OrderDetailsViewModel( private val mutableProgress = MutableLiveData() val progress: LiveData = mutableProgress var currentTicketPosition: Int = 0 + var brightness: Float? = 0f fun loadEvent(id: Long) { - if (id.equals(-1)) { + if (id == -1L) { throw IllegalStateException("ID should never be -1") } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/QrCode.kt b/app/src/main/java/org/fossasia/openevent/general/order/QrCode.kt index 4738deca7..34ef8b8cb 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/QrCode.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/QrCode.kt @@ -2,6 +2,7 @@ package org.fossasia.openevent.general.order import android.graphics.Bitmap import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType import com.google.zxing.MultiFormatWriter import com.google.zxing.WriterException import com.journeyapps.barcodescanner.BarcodeEncoder @@ -13,7 +14,9 @@ class QrCode { fun generateQrBitmap(text: String?, width: Int, height: Int): Bitmap? { try { - val bitMatrix = multiFormatWriter.encode(text, BarcodeFormat.QR_CODE, width, height) + val hint = HashMap() + hint[EncodeHintType.MARGIN] = 1 + val bitMatrix = multiFormatWriter.encode(text, BarcodeFormat.QR_CODE, width, height, hint) return barcodeEncoder.createBitmap(bitMatrix) } catch (e: WriterException) { Timber.d(e, "Writer Exception") diff --git a/app/src/main/res/layout-land/item_card_order_details.xml b/app/src/main/res/layout-land/item_card_order_details.xml index d6f20b467..7fac21ad3 100644 --- a/app/src/main/res/layout-land/item_card_order_details.xml +++ b/app/src/main/res/layout-land/item_card_order_details.xml @@ -205,6 +205,8 @@ android:layout_height="@dimen/event_details_image" android:layout_gravity="center" android:layout_margin="@dimen/layout_margin_medium" + android:padding="@dimen/padding_large" + android:foreground="?selectableItemBackground" android:scaleType="centerCrop" /> + From 2c7388d134b2675698128b3e74e430054cbc3c36 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Fri, 10 May 2019 20:59:36 +0200 Subject: [PATCH 09/35] fix: Retain chip state on screen rotation in SearchResultFragment (#1742) Details: - Save chip state inside ViewModel and retain its check state in onCreateView() Fixes: #1598 --- .../general/search/SearchResultsFragment.kt | 13 +++++++++---- .../openevent/general/search/SearchViewModel.kt | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt index 3d9a8bc93..52e5243d0 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt @@ -60,8 +60,8 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener bindScope(getOrCreateScope(Scopes.SEARCH_RESULTS_FRAGMENT.toString())) days = resources.getStringArray(R.array.days) - eventDate = safeArgs.date - eventType = safeArgs.type + eventDate = searchViewModel.savedTime ?: safeArgs.date + eventType = searchViewModel.savedType ?: safeArgs.type searchViewModel.loadEventTypes() searchViewModel.eventTypes @@ -74,7 +74,7 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { rootView = inflater.inflate(R.layout.fragment_search_results, container, false) - setChips(safeArgs.date, safeArgs.type) + setChips( eventDate, eventType) setToolbar(activity, getString(R.string.search_results)) setHasOptionsMenu(true) @@ -217,6 +217,7 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener val date = eventDate val freeEvents = safeArgs.freeEvents val sortBy = safeArgs.sort + searchViewModel.setChipNotClickable() searchViewModel.searchEvent = query searchViewModel.loadEvents(location, date, type, freeEvents, sortBy) } @@ -258,7 +259,9 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) { if (isChecked) { - if (buttonView?.text == "Clear All") { + if (buttonView?.text == getString(R.string.clear_all)) { + searchViewModel.savedTime = null + searchViewModel.savedType = null eventDate = getString(R.string.anytime) eventType = getString(R.string.anything) rootView.noSearchResults.isVisible = false @@ -271,6 +274,7 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener } days.forEach { if (it == buttonView?.text) { + searchViewModel.savedTime = it eventDate = it setChips(date = it) rootView.noSearchResults.isVisible = false @@ -283,6 +287,7 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener } eventTypesList?.forEach { if (it.name == buttonView?.text) { + searchViewModel.savedType = it.name eventType = it.name setChips(type = it.name) rootView.noSearchResults.isVisible = false diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt index d7d6cbbd6..90a06a24e 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt @@ -74,6 +74,10 @@ class SearchViewModel( savedTime = preference.getString(SAVED_TIME) } + fun setChipNotClickable() { + mutableChipClickable.value = false + } + fun loadEvents(location: String, time: String, type: String, freeEvents: Boolean, sortBy: String) { if (mutableEvents.value != null) { mutableChipClickable.value = true From 0a91109d9424a4c3a83631050992b0450505c5d5 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Sat, 11 May 2019 09:05:45 +0200 Subject: [PATCH 10/35] feat: Add empty view for FAQ section (#1743) Detail: - Add empty view for FAQ section - Improve empty view for event fragments Fixes: #1737 --- .../openevent/general/event/EventsFragment.kt | 4 +- .../general/event/faq/EventFAQFragment.kt | 5 +- .../general/event/faq/EventFAQViewModel.kt | 2 +- .../general/order/OrdersUnderUserFragment.kt | 2 +- app/src/main/res/drawable/ic_faq.xml | 5 ++ .../main/res/layout/fragment_event_faq.xml | 43 ++++++++++++++++- app/src/main/res/layout/fragment_events.xml | 46 ++++++++++++++++--- app/src/main/res/values/strings.xml | 2 + 8 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 app/src/main/res/drawable/ic_faq.xml diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt index d3217dc31..a36856a6a 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt @@ -19,7 +19,7 @@ import kotlinx.android.synthetic.main.fragment_events.view.locationTextView import kotlinx.android.synthetic.main.fragment_events.view.progressBar import kotlinx.android.synthetic.main.fragment_events.view.shimmerEvents import kotlinx.android.synthetic.main.fragment_events.view.swiperefresh -import kotlinx.android.synthetic.main.fragment_events.view.noEventsMessage +import kotlinx.android.synthetic.main.fragment_events.view.eventsEmptyView import kotlinx.android.synthetic.main.fragment_events.view.eventsNestedScrollView import org.fossasia.openevent.general.R import org.fossasia.openevent.general.ScrollToTop @@ -203,7 +203,7 @@ class EventsFragment : Fragment(), ScrollToTop { } private fun showEmptyMessage(itemCount: Int) { - rootView.noEventsMessage.visibility = if (itemCount == 0) View.VISIBLE else View.GONE + rootView.eventsEmptyView.visibility = if (itemCount == 0) View.VISIBLE else View.GONE } override fun onStop() { diff --git a/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQFragment.kt index 05d29cdb6..b77a42065 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQFragment.kt @@ -5,12 +5,11 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_event_faq.view.faqContainer +import kotlinx.android.synthetic.main.fragment_event_faq.view.faqEmptyView import kotlinx.android.synthetic.main.fragment_event_faq.view.faqRv import org.fossasia.openevent.general.R import org.fossasia.openevent.general.about.AboutEventFragmentArgs @@ -33,7 +32,7 @@ class EventFAQFragment : Fragment() { eventFAQViewModel.eventFAQ.observe(viewLifecycleOwner, Observer { faqAdapter.addAll(it) - rootView.faqContainer.isVisible = !it.isEmpty() + rootView.faqEmptyView.visibility = if (it.isEmpty()) View.VISIBLE else View.GONE }) eventFAQViewModel.loadEventFaq(safeArgs.eventId) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQViewModel.kt index b29cf65f1..969985387 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/faq/EventFAQViewModel.kt @@ -21,7 +21,7 @@ class EventFAQViewModel(private val eventService: EventService, private val reso val error: LiveData = mutableError fun loadEventFaq(id: Long) { - if (id.equals(-1)) { + if (id == -1L) { mutableError.value = Resource().getString(R.string.error_fetching_event_message) return } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt index 196c9e868..5bee56e3a 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt @@ -55,7 +55,7 @@ class OrdersUnderUserFragment : Fragment(), ScrollToTop { rootView = inflater.inflate(R.layout.fragment_orders_under_user, container, false) when (safeArgs.showExpired) { true -> { - setToolbar(activity, "Past Tickets") + setToolbar(activity, getString(R.string.past_tickets)) setHasOptionsMenu(true) navAnimGone(activity?.navigation, requireContext()) } diff --git a/app/src/main/res/drawable/ic_faq.xml b/app/src/main/res/drawable/ic_faq.xml new file mode 100644 index 000000000..93be57d0b --- /dev/null +++ b/app/src/main/res/drawable/ic_faq.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/layout/fragment_event_faq.xml b/app/src/main/res/layout/fragment_event_faq.xml index 437f301d0..71d533158 100644 --- a/app/src/main/res/layout/fragment_event_faq.xml +++ b/app/src/main/res/layout/fragment_event_faq.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_events.xml b/app/src/main/res/layout/fragment_events.xml index 3287b19c1..539c2384f 100644 --- a/app/src/main/res/layout/fragment_events.xml +++ b/app/src/main/res/layout/fragment_events.xml @@ -85,13 +85,47 @@ - + android:padding="@dimen/padding_large" + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + + + + + + + + + + + View Not seeing your tickets? Learn more about how to find them. No past tickets. + Past Tickets Past > Find my tickets No tickets available! @@ -316,6 +317,7 @@ Feedback submitted Successfully Be the first to write a review Frequently Asked Questions + There are no frequenly asked questions for this event. Filter Done Filter Search From ed5bfd2cb0ca297473cc3ef7ce1985a6a00d9860 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Sat, 11 May 2019 20:57:53 +0200 Subject: [PATCH 11/35] feat: Add session fragment Add session fragment to show all details of a session. Fix network call on session + adding sessions to database. Fixes: #1691 --- .../1.json | 1077 ----------------- .../{2.json => 5.json} | 308 ++++- .../openevent/general/OpenEventDatabase.kt | 12 +- .../general/common/RecyclerViewCallbacks.kt | 12 + .../fossasia/openevent/general/di/Modules.kt | 19 +- .../general/event/EventDetailsFragment.kt | 20 +- .../general/event/EventDetailsViewModel.kt | 12 +- .../openevent/general/event/EventService.kt | 8 +- .../openevent/general/sessions/Session.kt | 13 +- .../openevent/general/sessions/SessionDao.kt | 27 + .../general/sessions/SessionFragment.kt | 197 +++ .../sessions/SessionRecyclerAdapter.kt | 11 +- .../general/sessions/SessionService.kt | 21 + .../general/sessions/SessionViewHolder.kt | 8 + .../general/sessions/SessionViewModel.kt | 55 + .../MicroLocation.kt} | 8 +- .../microlocation/MicroLocationConverter.kt | 14 + .../sessions/{ => sessiontype}/SessionType.kt | 6 +- .../sessiontype/SessionTypeConverter.kt | 15 + .../main/res/drawable/ic_language_black.xml | 9 + app/src/main/res/layout/fragment_session.xml | 200 +++ app/src/main/res/layout/item_session.xml | 1 + app/src/main/res/layout/item_speaker.xml | 3 +- .../main/res/navigation/navigation_graph.xml | 17 + 24 files changed, 962 insertions(+), 1111 deletions(-) delete mode 100644 app/schemas/org.fossasia.openevent.general.OpenEventDatabase/1.json rename app/schemas/org.fossasia.openevent.general.OpenEventDatabase/{2.json => 5.json} (80%) create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/SessionDao.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/SessionService.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt rename app/src/main/java/org/fossasia/openevent/general/sessions/{Microlocation.kt => microlocation/MicroLocation.kt} (76%) create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocationConverter.kt rename app/src/main/java/org/fossasia/openevent/general/sessions/{ => sessiontype}/SessionType.kt (77%) create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionTypeConverter.kt create mode 100644 app/src/main/res/drawable/ic_language_black.xml create mode 100644 app/src/main/res/layout/fragment_session.xml diff --git a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/1.json b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/1.json deleted file mode 100644 index 51d7dd1a5..000000000 --- a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/1.json +++ /dev/null @@ -1,1077 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 1, - "identityHash": "0b3cd25764884626e03f56b0600d1c76", - "entities": [ - { - "tableName": "Event", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `identifier` TEXT NOT NULL, `startsAt` TEXT NOT NULL, `endsAt` TEXT NOT NULL, `timezone` TEXT NOT NULL, `privacy` TEXT NOT NULL, `paymentCountry` TEXT, `paypalEmail` TEXT, `thumbnailImageUrl` TEXT, `schedulePublishedOn` TEXT, `paymentCurrency` TEXT, `organizerDescription` TEXT, `originalImageUrl` TEXT, `onsiteDetails` TEXT, `organizerName` TEXT, `largeImageUrl` TEXT, `deletedAt` TEXT, `ticketUrl` TEXT, `locationName` TEXT, `codeOfConduct` TEXT, `state` TEXT, `searchableLocationName` TEXT, `description` TEXT, `pentabarfUrl` TEXT, `xcalUrl` TEXT, `logoUrl` TEXT, `externalEventUrl` TEXT, `iconImageUrl` TEXT, `icalUrl` TEXT, `createdAt` TEXT, `bankDetails` TEXT, `chequeDetails` TEXT, `isComplete` INTEGER NOT NULL, `latitude` REAL, `longitude` REAL, `refundPolicy` TEXT, `canPayByStripe` INTEGER NOT NULL, `canPayByCheque` INTEGER NOT NULL, `canPayByBank` INTEGER NOT NULL, `canPayByPaypal` INTEGER NOT NULL, `canPayOnsite` INTEGER NOT NULL, `isSponsorsEnabled` INTEGER NOT NULL, `hasOrganizerInfo` INTEGER NOT NULL, `isSessionsSpeakersEnabled` INTEGER NOT NULL, `isTicketingEnabled` INTEGER NOT NULL, `isTaxEnabled` INTEGER NOT NULL, `isMapShown` INTEGER NOT NULL, `favorite` INTEGER NOT NULL, `eventTopic` INTEGER, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "identifier", - "columnName": "identifier", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "startsAt", - "columnName": "startsAt", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "endsAt", - "columnName": "endsAt", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "timezone", - "columnName": "timezone", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privacy", - "columnName": "privacy", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "paymentCountry", - "columnName": "paymentCountry", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "paypalEmail", - "columnName": "paypalEmail", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "thumbnailImageUrl", - "columnName": "thumbnailImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "schedulePublishedOn", - "columnName": "schedulePublishedOn", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "paymentCurrency", - "columnName": "paymentCurrency", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "organizerDescription", - "columnName": "organizerDescription", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "originalImageUrl", - "columnName": "originalImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "onsiteDetails", - "columnName": "onsiteDetails", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "organizerName", - "columnName": "organizerName", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "largeImageUrl", - "columnName": "largeImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ticketUrl", - "columnName": "ticketUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "locationName", - "columnName": "locationName", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "codeOfConduct", - "columnName": "codeOfConduct", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "searchableLocationName", - "columnName": "searchableLocationName", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "pentabarfUrl", - "columnName": "pentabarfUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "xcalUrl", - "columnName": "xcalUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "logoUrl", - "columnName": "logoUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "externalEventUrl", - "columnName": "externalEventUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "iconImageUrl", - "columnName": "iconImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "icalUrl", - "columnName": "icalUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "bankDetails", - "columnName": "bankDetails", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "chequeDetails", - "columnName": "chequeDetails", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isComplete", - "columnName": "isComplete", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latitude", - "columnName": "latitude", - "affinity": "REAL", - "notNull": false - }, - { - "fieldPath": "longitude", - "columnName": "longitude", - "affinity": "REAL", - "notNull": false - }, - { - "fieldPath": "refundPolicy", - "columnName": "refundPolicy", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "canPayByStripe", - "columnName": "canPayByStripe", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canPayByCheque", - "columnName": "canPayByCheque", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canPayByBank", - "columnName": "canPayByBank", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canPayByPaypal", - "columnName": "canPayByPaypal", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canPayOnsite", - "columnName": "canPayOnsite", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isSponsorsEnabled", - "columnName": "isSponsorsEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasOrganizerInfo", - "columnName": "hasOrganizerInfo", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isSessionsSpeakersEnabled", - "columnName": "isSessionsSpeakersEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isTicketingEnabled", - "columnName": "isTicketingEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isTaxEnabled", - "columnName": "isTaxEnabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isMapShown", - "columnName": "isMapShown", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "favorite", - "columnName": "favorite", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "eventTopic", - "columnName": "eventTopic", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [ - { - "name": "index_Event_eventTopic", - "unique": false, - "columnNames": [ - "eventTopic" - ], - "createSql": "CREATE INDEX `index_Event_eventTopic` ON `${TABLE_NAME}` (`eventTopic`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "User", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT, `lastName` TEXT, `email` TEXT, `contact` TEXT, `details` TEXT, `thumbnailImageUrl` TEXT, `iconImageUrl` TEXT, `smallImageUrl` TEXT, `avatarUrl` TEXT, `facebookUrl` TEXT, `twitterUrl` TEXT, `instagramUrl` TEXT, `googlePlusUrl` TEXT, `originalImageUrl` TEXT, `isVerified` INTEGER NOT NULL, `isAdmin` INTEGER, `isSuperAdmin` INTEGER, `createdAt` TEXT, `lastAccessedAt` TEXT, `deletedAt` TEXT, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "firstName", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lastName", - "columnName": "lastName", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "details", - "columnName": "details", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "thumbnailImageUrl", - "columnName": "thumbnailImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "iconImageUrl", - "columnName": "iconImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "smallImageUrl", - "columnName": "smallImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "avatarUrl", - "columnName": "avatarUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "facebookUrl", - "columnName": "facebookUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "twitterUrl", - "columnName": "twitterUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "instagramUrl", - "columnName": "instagramUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "googlePlusUrl", - "columnName": "googlePlusUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "originalImageUrl", - "columnName": "originalImageUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isVerified", - "columnName": "isVerified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAdmin", - "columnName": "isAdmin", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isSuperAdmin", - "columnName": "isSuperAdmin", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lastAccessedAt", - "columnName": "lastAccessedAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "deletedAt", - "columnName": "deletedAt", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SocialLink", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `link` TEXT NOT NULL, `name` TEXT NOT NULL, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "link", - "columnName": "link", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [ - { - "name": "index_SocialLink_event", - "unique": false, - "columnNames": [ - "event" - ], - "createSql": "CREATE INDEX `index_SocialLink_event` ON `${TABLE_NAME}` (`event`)" - } - ], - "foreignKeys": [ - { - "table": "Event", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "event" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Ticket", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `description` TEXT, `type` TEXT, `name` TEXT NOT NULL, `maxOrder` INTEGER NOT NULL, `isFeeAbsorbed` INTEGER, `isDescriptionVisible` INTEGER, `price` REAL, `position` TEXT, `quantity` TEXT, `isHidden` INTEGER, `salesStartsAt` TEXT, `salesEndsAt` TEXT, `minOrder` INTEGER NOT NULL, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "maxOrder", - "columnName": "maxOrder", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFeeAbsorbed", - "columnName": "isFeeAbsorbed", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isDescriptionVisible", - "columnName": "isDescriptionVisible", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "price", - "columnName": "price", - "affinity": "REAL", - "notNull": false - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "quantity", - "columnName": "quantity", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isHidden", - "columnName": "isHidden", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "salesStartsAt", - "columnName": "salesStartsAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "salesEndsAt", - "columnName": "salesEndsAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "minOrder", - "columnName": "minOrder", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [ - { - "name": "index_Ticket_event", - "unique": false, - "columnNames": [ - "event" - ], - "createSql": "CREATE INDEX `index_Ticket_event` ON `${TABLE_NAME}` (`event`)" - } - ], - "foreignKeys": [ - { - "table": "Event", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "event" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Attendee", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstname` TEXT, `lastname` TEXT, `email` TEXT, `address` TEXT, `city` TEXT, `state` TEXT, `country` TEXT, `isCheckedIn` INTEGER, `pdfUrl` TEXT, `ticketId` TEXT, `event` INTEGER, `ticket` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`ticket`) REFERENCES `Ticket`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstname", - "columnName": "firstname", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "lastname", - "columnName": "lastname", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "city", - "columnName": "city", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "country", - "columnName": "country", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "isCheckedIn", - "columnName": "isCheckedIn", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "pdfUrl", - "columnName": "pdfUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ticketId", - "columnName": "ticketId", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "ticket", - "columnName": "ticket", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [ - { - "name": "index_Attendee_event", - "unique": false, - "columnNames": [ - "event" - ], - "createSql": "CREATE INDEX `index_Attendee_event` ON `${TABLE_NAME}` (`event`)" - } - ], - "foreignKeys": [ - { - "table": "Event", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "event" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "Ticket", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "ticket" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "EventTopic", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER, `name` TEXT, `slug` TEXT, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "slug", - "columnName": "slug", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [ - { - "name": "index_EventTopic_event", - "unique": false, - "columnNames": [ - "event" - ], - "createSql": "CREATE INDEX `index_EventTopic_event` ON `${TABLE_NAME}` (`event`)" - } - ], - "foreignKeys": [ - { - "table": "Event", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "event" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "Order", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `paymentMode` TEXT, `country` TEXT, `status` TEXT, `amount` REAL, `identifier` TEXT, `orderNotes` TEXT, `completedAt` TEXT, `city` TEXT, `address` TEXT, `createdAt` TEXT, `zipcode` TEXT, `paidVia` TEXT, `discountCodeId` TEXT, `ticketsPdfUrl` TEXT, `transactionId` TEXT, `event` INTEGER, `attendees` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`attendees`) REFERENCES `Attendee`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "paymentMode", - "columnName": "paymentMode", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "country", - "columnName": "country", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "amount", - "columnName": "amount", - "affinity": "REAL", - "notNull": false - }, - { - "fieldPath": "identifier", - "columnName": "identifier", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "orderNotes", - "columnName": "orderNotes", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "completedAt", - "columnName": "completedAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "city", - "columnName": "city", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "zipcode", - "columnName": "zipcode", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "paidVia", - "columnName": "paidVia", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "discountCodeId", - "columnName": "discountCodeId", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "ticketsPdfUrl", - "columnName": "ticketsPdfUrl", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "transactionId", - "columnName": "transactionId", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "attendees", - "columnName": "attendees", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [ - { - "name": "index_Order_event", - "unique": false, - "columnNames": [ - "event" - ], - "createSql": "CREATE INDEX `index_Order_event` ON `${TABLE_NAME}` (`event`)" - } - ], - "foreignKeys": [ - { - "table": "Event", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "event" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "Attendee", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "attendees" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "CustomForm", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `form` TEXT NOT NULL, `fieldIdentifier` TEXT NOT NULL, `type` TEXT NOT NULL, `isRequired` INTEGER, `isIncluded` INTEGER, `isFixed` INTEGER, `ticketsNumber` INTEGER, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "form", - "columnName": "form", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fieldIdentifier", - "columnName": "fieldIdentifier", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isRequired", - "columnName": "isRequired", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isIncluded", - "columnName": "isIncluded", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "isFixed", - "columnName": "isFixed", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "ticketsNumber", - "columnName": "ticketsNumber", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "INTEGER", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [ - { - "table": "Event", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "event" - ], - "referencedColumns": [ - "id" - ] - } - ] - } - ], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"0b3cd25764884626e03f56b0600d1c76\")" - ] - } -} diff --git a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/2.json b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json similarity index 80% rename from app/schemas/org.fossasia.openevent.general.OpenEventDatabase/2.json rename to app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json index 4373ff2b0..b7d65d73b 100644 --- a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/2.json +++ b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json @@ -1,8 +1,8 @@ { "formatVersion": 1, "database": { - "version": 3, - "identityHash": "1badd5e17f181e602314a4244c04cf56", + "version": 5, + "identityHash": "77cfd0b7528a803833b63c64c6d1db89", "entities": [ { "tableName": "Event", @@ -1098,7 +1098,7 @@ }, { "tableName": "Speaker", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `email` TEXT, `photoUrl` TEXT, `shortBiography` TEXT, `longBiography` TEXT, `speakingExperience` TEXT, `location` TEXT, `country` TEXT, `city` TEXT, `organisation` TEXT, `gender` TEXT, `website` TEXT, `twitter` TEXT, `facebook` TEXT, `linkedin` TEXT, `github` TEXT, `isFeatured` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `email` TEXT, `photoUrl` TEXT, `shortBiography` TEXT, `longBiography` TEXT, `speakingExperience` TEXT, `position` TEXT, `mobile` TEXT, `location` TEXT, `country` TEXT, `city` TEXT, `organisation` TEXT, `gender` TEXT, `website` TEXT, `twitter` TEXT, `facebook` TEXT, `linkedin` TEXT, `github` TEXT, `isFeatured` INTEGER NOT NULL, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -1142,6 +1142,18 @@ "affinity": "TEXT", "notNull": false }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mobile", + "columnName": "mobile", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "location", "columnName": "location", @@ -1284,12 +1296,298 @@ ] } ] + }, + { + "tableName": "Sponsor", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `description` TEXT, `url` TEXT, `logoUrl` TEXT, `level` INTEGER NOT NULL, `type` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "logoUrl", + "columnName": "logoUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "level", + "columnName": "level", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SponsorWithEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`event_id` INTEGER NOT NULL, `sponsor_id` INTEGER NOT NULL, PRIMARY KEY(`event_id`, `sponsor_id`), FOREIGN KEY(`event_id`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`sponsor_id`) REFERENCES `Sponsor`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "eventId", + "columnName": "event_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sponsorId", + "columnName": "sponsor_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "event_id", + "sponsor_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_SponsorWithEvent_event_id", + "unique": false, + "columnNames": [ + "event_id" + ], + "createSql": "CREATE INDEX `index_SponsorWithEvent_event_id` ON `${TABLE_NAME}` (`event_id`)" + }, + { + "name": "index_SponsorWithEvent_sponsor_id", + "unique": false, + "columnNames": [ + "sponsor_id" + ], + "createSql": "CREATE INDEX `index_SponsorWithEvent_sponsor_id` ON `${TABLE_NAME}` (`sponsor_id`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "event_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Sponsor", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "sponsor_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Session", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `shortAbstract` TEXT, `comments` TEXT, `longAbstract` TEXT, `level` TEXT, `signupUrl` TEXT, `endsAt` TEXT, `language` TEXT, `title` TEXT, `startsAt` TEXT, `slidesUrl` TEXT, `averageRating` REAL, `submittedAt` TEXT, `deletedAt` TEXT, `subtitle` TEXT, `createdAt` TEXT, `state` TEXT, `lastModifiedAt` TEXT, `videoUrl` TEXT, `audioUrl` TEXT, `sessionType` TEXT, `microlocation` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortAbstract", + "columnName": "shortAbstract", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comments", + "columnName": "comments", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "longAbstract", + "columnName": "longAbstract", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "level", + "columnName": "level", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "signupUrl", + "columnName": "signupUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endsAt", + "columnName": "endsAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "startsAt", + "columnName": "startsAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "slidesUrl", + "columnName": "slidesUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "averageRating", + "columnName": "averageRating", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "submittedAt", + "columnName": "submittedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subtitle", + "columnName": "subtitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastModifiedAt", + "columnName": "lastModifiedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "videoUrl", + "columnName": "videoUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "audioUrl", + "columnName": "audioUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sessionType", + "columnName": "sessionType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "microlocation", + "columnName": "microlocation", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_Session_sessionType", + "unique": false, + "columnNames": [ + "sessionType" + ], + "createSql": "CREATE INDEX `index_Session_sessionType` ON `${TABLE_NAME}` (`sessionType`)" + }, + { + "name": "index_Session_microlocation", + "unique": false, + "columnNames": [ + "microlocation" + ], + "createSql": "CREATE INDEX `index_Session_microlocation` ON `${TABLE_NAME}` (`microlocation`)" + } + ], + "foreignKeys": [] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"1badd5e17f181e602314a4244c04cf56\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"77cfd0b7528a803833b63c64c6d1db89\")" ] } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt b/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt index 198d50ba9..acdd1bd97 100644 --- a/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt +++ b/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt @@ -20,6 +20,10 @@ import org.fossasia.openevent.general.event.topic.EventTopicsDao import org.fossasia.openevent.general.event.types.EventTypeConverter import org.fossasia.openevent.general.order.Order import org.fossasia.openevent.general.order.OrderDao +import org.fossasia.openevent.general.sessions.Session +import org.fossasia.openevent.general.sessions.SessionDao +import org.fossasia.openevent.general.sessions.microlocation.MicroLocationConverter +import org.fossasia.openevent.general.sessions.sessiontype.SessionTypeConverter import org.fossasia.openevent.general.social.SocialLink import org.fossasia.openevent.general.social.SocialLinksDao import org.fossasia.openevent.general.speakers.Speaker @@ -36,10 +40,10 @@ import org.fossasia.openevent.general.ticket.TicketIdConverter @Database(entities = [Event::class, User::class, SocialLink::class, Ticket::class, Attendee::class, EventTopic::class, Order::class, CustomForm::class, Speaker::class, SpeakerWithEvent::class, Sponsor::class, - SponsorWithEvent::class], version = 4) + SponsorWithEvent::class, Session::class], version = 5) @TypeConverters(EventIdConverter::class, EventTopicConverter::class, EventTypeConverter::class, - EventSubTopicConverter::class, TicketIdConverter::class, - AttendeeIdConverter::class, ListAttendeeIdConverter::class) + EventSubTopicConverter::class, TicketIdConverter::class, MicroLocationConverter::class, + AttendeeIdConverter::class, ListAttendeeIdConverter::class, SessionTypeConverter::class) abstract class OpenEventDatabase : RoomDatabase() { abstract fun eventDao(): EventDao @@ -63,4 +67,6 @@ abstract class OpenEventDatabase : RoomDatabase() { abstract fun sponsorDao(): SponsorDao abstract fun sponsorWithEventDao(): SponsorWithEventDao + + abstract fun sessionDao(): SessionDao } diff --git a/app/src/main/java/org/fossasia/openevent/general/common/RecyclerViewCallbacks.kt b/app/src/main/java/org/fossasia/openevent/general/common/RecyclerViewCallbacks.kt index a32fd1465..bd76a35e3 100644 --- a/app/src/main/java/org/fossasia/openevent/general/common/RecyclerViewCallbacks.kt +++ b/app/src/main/java/org/fossasia/openevent/general/common/RecyclerViewCallbacks.kt @@ -38,3 +38,15 @@ interface SpeakerClickListener { */ fun onClick(speakerId: Long) } + +/** + * The callback interface for Speaker item clicks + */ +interface SessionClickListener { + /** + * The function to be invoked when a speaker item is clicked + * + * @param sessionId The ID of the clicked session + */ + fun onClick(sessionId: Long) +} diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index 633cc543d..b742d04b3 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -74,11 +74,13 @@ import org.fossasia.openevent.general.search.SearchTypeViewModel import org.fossasia.openevent.general.search.LocationServiceImpl import org.fossasia.openevent.general.auth.SmartAuthViewModel import org.fossasia.openevent.general.connectivity.MutableConnectionLiveData -import org.fossasia.openevent.general.sessions.Microlocation import org.fossasia.openevent.general.sessions.Session import org.fossasia.openevent.general.sessions.SessionApi -import org.fossasia.openevent.general.sessions.SessionType +import org.fossasia.openevent.general.sessions.SessionService import org.fossasia.openevent.general.event.faq.EventFAQViewModel +import org.fossasia.openevent.general.sessions.SessionViewModel +import org.fossasia.openevent.general.sessions.microlocation.MicroLocation +import org.fossasia.openevent.general.sessions.sessiontype.SessionType import org.fossasia.openevent.general.settings.SettingsViewModel import org.fossasia.openevent.general.social.SocialLink import org.fossasia.openevent.general.social.SocialLinkApi @@ -178,13 +180,14 @@ val apiModule = module { factory { AuthHolder(get()) } factory { AuthService(get(), get(), get()) } - factory { EventService(get(), get(), get(), get(), get(), get(), get(), get(), get()) } + factory { EventService(get(), get(), get(), get(), get(), get(), get(), get()) } factory { SpeakerService(get(), get(), get()) } factory { SponsorService(get(), get(), get()) } factory { TicketService(get(), get()) } factory { SocialLinksService(get(), get()) } factory { AttendeeService(get(), get(), get()) } factory { OrderService(get(), get(), get()) } + factory { SessionService(get(), get()) } factory { Resource() } factory { MutableConnectionLiveData() } } @@ -194,7 +197,8 @@ val viewModelModule = module { viewModel { EventsViewModel(get(), get(), get(), get()) } viewModel { ProfileViewModel(get(), get()) } viewModel { SignUpViewModel(get(), get(), get()) } - viewModel { EventDetailsViewModel(get(), get(), get(), get(), get()) } + viewModel { EventDetailsViewModel(get(), get(), get(), get(), get(), get()) } + viewModel { SessionViewModel(get(), get()) } viewModel { SearchViewModel(get(), get(), get(), get()) } viewModel { AttendeeViewModel(get(), get(), get(), get(), get(), get(), get()) } viewModel { SearchLocationViewModel(get(), get()) } @@ -255,7 +259,7 @@ val networkModule = module { AttendeeId::class.java, Charge::class.java, Paypal::class.java, ConfirmOrder::class.java, CustomForm::class.java, EventLocation::class.java, EventType::class.java, EventSubTopic::class.java, Feedback::class.java, Speaker::class.java, - Session::class.java, SessionType::class.java, Microlocation::class.java, + Session::class.java, SessionType::class.java, MicroLocation::class.java, Sponsor::class.java, EventFAQ::class.java) Retrofit.Builder() @@ -282,6 +286,11 @@ val databaseModule = module { database.eventDao() } + factory { + val database: OpenEventDatabase = get() + database.sessionDao() + } + factory { val database: OpenEventDatabase = get() database.userDao() diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt index b0ec40332..ba004eeff 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt @@ -55,6 +55,7 @@ import kotlinx.android.synthetic.main.dialog_feedback.view.feedbackTextInputLayo import kotlinx.android.synthetic.main.dialog_feedback.view.feedbackrating import kotlinx.android.synthetic.main.fragment_event.eventCoordinatorLayout import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.common.SessionClickListener import org.fossasia.openevent.general.common.SpeakerClickListener import org.fossasia.openevent.general.databinding.FragmentEventBinding import org.fossasia.openevent.general.event.EventUtils.loadMapUrl @@ -124,7 +125,13 @@ class EventDetailsFragment : Fragment() { }) eventViewModel.loadEventFeedback(safeArgs.eventId) eventViewModel.fetchEventSpeakers(safeArgs.eventId) - eventViewModel.loadEventSessions(safeArgs.eventId) + val allSessions = eventViewModel.eventSessions.value + if (allSessions == null) { + eventViewModel.fetchEventSessions(safeArgs.eventId) + } else { + sessionsAdapter.addAll(allSessions) + rootView.sessionContainer.isVisible = allSessions.isNotEmpty() + } eventViewModel.fetchEventSponsors(safeArgs.eventId) } @@ -232,11 +239,22 @@ class EventDetailsFragment : Fragment() { rootView.speakersContainer.visibility = View.VISIBLE } }) + + val sessionClickListener: SessionClickListener = object : SessionClickListener { + override fun onClick(sessionId: Long) { + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToSession(sessionId)) + } + } + val linearLayoutManagerSessions = LinearLayoutManager(context) linearLayoutManagerSessions.orientation = LinearLayoutManager.HORIZONTAL rootView.sessionsRv.layoutManager = linearLayoutManagerSessions rootView.sessionsRv.adapter = sessionsAdapter + sessionsAdapter.apply { + onSessionClick = sessionClickListener + } eventViewModel.eventSessions.observe(viewLifecycleOwner, Observer { sessionsAdapter.addAll(it) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt index b96452734..c9f05bb35 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt @@ -15,6 +15,7 @@ import org.fossasia.openevent.general.common.SingleLiveEvent import org.fossasia.openevent.general.data.Resource import org.fossasia.openevent.general.event.feedback.Feedback import org.fossasia.openevent.general.sessions.Session +import org.fossasia.openevent.general.sessions.SessionService import org.fossasia.openevent.general.speakers.Speaker import org.fossasia.openevent.general.speakers.SpeakerService import org.fossasia.openevent.general.sponsor.Sponsor @@ -27,6 +28,7 @@ class EventDetailsViewModel( private val authHolder: AuthHolder, private val speakerService: SpeakerService, private val sponsorService: SponsorService, + private val sessionService: SessionService, private val resource: Resource ) : ViewModel() { @@ -44,7 +46,7 @@ class EventDetailsViewModel( val eventFeedback: LiveData> = mutableEventFeedback private val mutableEventSessions = MutableLiveData>() val eventSessions: LiveData> = mutableEventSessions - var eventSpeakers: LiveData> = MutableLiveData() + private var eventSpeakers: LiveData> = MutableLiveData() fun isLoggedIn() = authHolder.isLoggedIn() @@ -119,8 +121,8 @@ class EventDetailsViewModel( }) } - fun loadEventSessions(id: Long) { - compositeDisposable += eventService.getEventSessions(id) + fun fetchEventSessions(id: Long) { + compositeDisposable += sessionService.fetchSessionForEvent(id) .withDefaultSchedulers() .subscribe({ mutableEventSessions.value = it @@ -132,8 +134,8 @@ class EventDetailsViewModel( fun loadMap(event: Event): String { // location handling val BASE_URL = "https://api.mapbox.com/v4/mapbox.emerald/pin-l-marker+673ab7" - val LOCATION = "(" + event.longitude + "," + event.latitude + ")/" + event.longitude + "," + event.latitude - return BASE_URL + LOCATION + ",15/900x500.png?access_token=" + MAPBOX_KEY + val LOCATION = "(${event.longitude},${event.latitude})/${event.longitude},${event.latitude}" + return "$BASE_URL$LOCATION,15/900x500.png?access_token=$MAPBOX_KEY" } fun setFavorite(eventId: Long, favorite: Boolean) { diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt index 664204437..e640ea547 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt @@ -14,8 +14,6 @@ import org.fossasia.openevent.general.event.topic.EventTopicApi import org.fossasia.openevent.general.event.topic.EventTopicsDao import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.event.types.EventTypesApi -import org.fossasia.openevent.general.sessions.Session -import org.fossasia.openevent.general.sessions.SessionApi import java.util.Locale.filter class EventService( @@ -26,8 +24,7 @@ class EventService( private val eventTypesApi: EventTypesApi, private val eventLocationApi: EventLocationApi, private val eventFeedbackApi: FeedbackApi, - private val eventFAQApi: EventFAQApi, - private val eventSessionApi: SessionApi + private val eventFAQApi: EventFAQApi ) { fun getEvents(): Flowable> { @@ -77,9 +74,6 @@ class EventService( fun submitFeedback(feedback: Feedback): Single { return eventFeedbackApi.postfeedback(feedback) } - fun getEventSessions(id: Long): Single> { - return eventSessionApi.getSessionsForEvent(id) - } fun getSearchEvents(eventName: String, sortBy: String): Single> { return eventApi.searchEvents(sortBy, eventName).flatMap { apiList -> var eventIds = apiList.map { it.id }.toList() diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt index dd918a535..1a8f20ea1 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt @@ -1,16 +1,25 @@ package org.fossasia.openevent.general.sessions +import androidx.annotation.NonNull +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.annotation.JsonNaming import com.github.jasminb.jsonapi.LongIdHandler import com.github.jasminb.jsonapi.annotations.Id import com.github.jasminb.jsonapi.annotations.Relationship import com.github.jasminb.jsonapi.annotations.Type +import org.fossasia.openevent.general.sessions.microlocation.MicroLocation +import org.fossasia.openevent.general.sessions.sessiontype.SessionType @Type("session") @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) +@Entity data class Session( @Id(LongIdHandler::class) + @PrimaryKey + @NonNull val id: Long, val shortAbstract: String? = null, val comments: String? = null, @@ -31,8 +40,10 @@ data class Session( val lastModifiedAt: String? = null, val videoUrl: String? = null, val audioUrl: String? = null, + @ColumnInfo(index = true) @Relationship("session-type", resolve = true) var sessionType: SessionType? = null, + @ColumnInfo(index = true) @Relationship("microlocation", resolve = true) - var microlocation: Microlocation? = null + var microlocation: MicroLocation? = null ) diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionDao.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionDao.kt new file mode 100644 index 000000000..1e68db8f5 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionDao.kt @@ -0,0 +1,27 @@ +package org.fossasia.openevent.general.sessions + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy.REPLACE +import androidx.room.Query +import io.reactivex.Flowable + +@Dao +interface SessionDao { + + @Insert(onConflict = REPLACE) + fun insertSessions(sessions: List) + + @Insert(onConflict = REPLACE) + fun insertSession(session: Session) + + @Query("SELECT * FROM Session WHERE id =:id") + fun getSessionById(id: Long): Flowable + + @Query("SELECT * FROM Session") + fun getAllSessions(): LiveData> + + @Query("DELETE FROM Session") + fun deleteCurrentSessions() +} diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt new file mode 100644 index 000000000..c0d530902 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt @@ -0,0 +1,197 @@ +package org.fossasia.openevent.general.sessions + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.CalendarContract +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.navigation.fragment.navArgs +import com.squareup.picasso.Picasso +import kotlinx.android.synthetic.main.fragment_session.view.* +import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.event.EventUtils +import org.fossasia.openevent.general.utils.Utils +import org.fossasia.openevent.general.utils.Utils.setToolbar +import org.fossasia.openevent.general.utils.extensions.nonNull +import org.jetbrains.anko.design.snackbar +import org.koin.androidx.viewmodel.ext.android.viewModel + +const val LINE_COUNT_ABSTRACT = 3 + +class SessionFragment : Fragment() { + private lateinit var rootView: View + private val sessionViewModel by viewModel() + private val safeArgs: SessionFragmentArgs by navArgs() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + rootView = inflater.inflate(R.layout.fragment_session, container, false) + + setToolbar(activity) + setHasOptionsMenu(true) + + sessionViewModel.error + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.snackbar(it) + }) + + sessionViewModel.session + .nonNull() + .observe(viewLifecycleOwner, Observer { + makeSessionView(it) + }) + + sessionViewModel.loadSession(safeArgs.sessionId) + + return rootView + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + activity?.onBackPressed() + true + } + else -> super.onOptionsItemSelected(item) + } + } + + private fun makeSessionView(session: Session) { + when (session.title.isNullOrBlank()) { + true -> rootView.sessionDetailName.visibility = View.GONE + false -> { + rootView.sessionDetailName.text = session.title + setToolbar(activity, session.title) + } + } + + val type = session.sessionType + if (type == null) { + rootView.sessionDetailType.visibility = View.GONE + } else { + rootView.sessionDetailType.text = "Type: ${type.name}" + } + + val locationInfo = session.microlocation + if (locationInfo == null) { + rootView.sessionDetailLocationInfoContainer.visibility = View.GONE + rootView.sessionDetailLocationContainer.visibility = View.GONE + } else { + rootView.sessionDetailInfoLocation.text = locationInfo.name + rootView.sessionDetailLocation.text = locationInfo.name + if (locationInfo.latitude.isNullOrBlank() || locationInfo.longitude.isNullOrBlank()) { + rootView.sessionDetailLocationImageMap.visibility = View.GONE + } else { + rootView.sessionDetailLocationContainer.setOnClickListener { + startMap(locationInfo.latitude, locationInfo.longitude) + } + rootView.sessionDetailLocationImageMap.setOnClickListener { + startMap(locationInfo.latitude, locationInfo.longitude) + } + Picasso.get() + .load(sessionViewModel.loadMap(locationInfo.latitude, locationInfo.longitude)) + .placeholder(R.drawable.ic_map_black) + .error(R.drawable.ic_map_black) + .into(rootView.sessionDetailLocationImageMap) + } + } + + when (session.language.isNullOrBlank()) { + true -> rootView.sessionDetailLanguageContainer.visibility = View.GONE + false -> rootView.sessionDetailLanguage.text = session.language + } + + when (session.startsAt.isNullOrBlank()) { + true -> rootView.sessionDetailStartTime.visibility = View.GONE + false -> { + val formattedStartTime = EventUtils.getEventDateTime(session.startsAt, "") + val formattedTime = EventUtils.getFormattedTime(formattedStartTime) + val formattedDate = EventUtils.getFormattedDate(formattedStartTime) + val timezone = EventUtils.getFormattedTimeZone(formattedStartTime) + rootView.sessionDetailStartTime.text = "$formattedTime $timezone/ $formattedDate" + } + } + when (session.endsAt.isNullOrBlank()) { + true -> rootView.sessionDetailEndTime.visibility = View.GONE + false -> { + val formattedEndTime = EventUtils.getEventDateTime(session.endsAt, "") + val formattedTime = EventUtils.getFormattedTime(formattedEndTime) + val formattedDate = EventUtils.getFormattedDate(formattedEndTime) + val timezone = EventUtils.getFormattedTimeZone(formattedEndTime) + rootView.sessionDetailEndTime.text = "- $formattedTime $timezone/ $formattedDate" + } + } + if (session.startsAt.isNullOrBlank() && session.endsAt.isNullOrBlank()) + rootView.sessionDetailTimeContainer.visibility = View.GONE + else + rootView.sessionDetailTimeContainer.setOnClickListener { + saveSessionToCalendar(session) + } + + val description = session.longAbstract ?: session.shortAbstract + when (description.isNullOrBlank()) { + true -> rootView.sessionDetailAbstractContainer.visibility = View.GONE + false -> { + rootView.sessionDetailAbstract.text = description + val sessionAbstractClickListener = View.OnClickListener { + if (rootView.sessionDetailAbstractSeeMore.text == getString(R.string.see_more)) { + rootView.sessionDetailAbstractSeeMore.text = getString(R.string.see_less) + rootView.sessionDetailAbstract.minLines = 0 + rootView.sessionDetailAbstract.maxLines = Int.MAX_VALUE + } else { + rootView.sessionDetailAbstractSeeMore.text = getString(R.string.see_more) + rootView.sessionDetailAbstract.setLines(LINE_COUNT_ABSTRACT + 1) + } + } + + rootView.sessionDetailAbstract.post { + if (rootView.sessionDetailAbstract.lineCount > LINE_COUNT_ABSTRACT) { + rootView.sessionDetailAbstractSeeMore.visibility = View.VISIBLE + rootView.sessionDetailAbstractContainer.setOnClickListener(sessionAbstractClickListener) + } + } + } + } + + when (session.signupUrl.isNullOrBlank()) { + true -> rootView.sessionDetailSignUpButton.visibility = View.GONE + false -> rootView.sessionDetailSignUpButton.setOnClickListener { + context?.let { Utils.openUrl(it, session.signupUrl) } + } + } + } + + private fun saveSessionToCalendar(session: Session) { + val intent = Intent(Intent.ACTION_INSERT) + intent.type = "vnd.android.cursor.item/event" + intent.putExtra(CalendarContract.Events.TITLE, session.title) + intent.putExtra(CalendarContract.Events.DESCRIPTION, session.shortAbstract) + intent.putExtra(CalendarContract.Events.EVENT_LOCATION, session.microlocation?.name) + + if (session.startsAt != null && session.endsAt != null) { + val formattedStartTime = EventUtils.getEventDateTime(session.startsAt, "") + val timezone = EventUtils.getFormattedTimeZone(formattedStartTime) + intent.putExtra(CalendarContract.Events.EVENT_TIMEZONE, timezone) + intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, + EventUtils.getTimeInMilliSeconds(session.startsAt, timezone)) + intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, + EventUtils.getTimeInMilliSeconds(session.endsAt, timezone)) + } + + startActivity(intent) + } + + private fun startMap(latitude: String, longitude: String) { + val mapUrl = "geo:<$latitude>,<$longitude>?q=<$latitude>,<$longitude>" + val mapIntent = Intent(Intent.ACTION_VIEW, Uri.parse(mapUrl)) + val packageManager = activity?.packageManager + if (packageManager != null && mapIntent.resolveActivity(packageManager) != null) { + startActivity(mapIntent) + } + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionRecyclerAdapter.kt index 6e00e5014..17e6a4162 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionRecyclerAdapter.kt @@ -4,9 +4,11 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.common.SessionClickListener class SessionRecyclerAdapter : RecyclerView.Adapter() { - val sessionList = ArrayList() + private val sessionList = ArrayList() + var onSessionClick: SessionClickListener? = null fun addAll(sessionList: List) { if (sessionList.isNotEmpty()) @@ -21,9 +23,12 @@ class SessionRecyclerAdapter : RecyclerView.Adapter() { } override fun onBindViewHolder(holder: SessionViewHolder, position: Int) { - val speaker = sessionList[position] + val session = sessionList[position] - holder.bind(speaker) + holder.apply { + bind(session) + sessionClickListener = onSessionClick + } } override fun getItemCount(): Int { diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionService.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionService.kt new file mode 100644 index 000000000..4d4f24bb2 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionService.kt @@ -0,0 +1,21 @@ +package org.fossasia.openevent.general.sessions + +import io.reactivex.Flowable +import io.reactivex.Single + +class SessionService( + private val sessionApi: SessionApi, + private val sessionDao: SessionDao +) { + fun fetchSessionForEvent(id: Long): Single> { + return sessionApi.getSessionsForEvent(id) + .doOnSuccess { sessions -> + sessionDao.deleteCurrentSessions() + sessionDao.insertSessions(sessions) + } + } + + fun fetchSession(id: Long): Flowable { + return sessionDao.getSessionById(id) + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt index e68515e95..fbc8dbc2d 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt @@ -8,11 +8,15 @@ import kotlinx.android.synthetic.main.item_session.view.sessionType import kotlinx.android.synthetic.main.item_session.view.sessiontime import kotlinx.android.synthetic.main.item_session.view.shortAbstract import kotlinx.android.synthetic.main.item_session.view.title +import org.fossasia.openevent.general.common.SessionClickListener import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.utils.nullToEmpty import org.fossasia.openevent.general.utils.stripHtml class SessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var sessionClickListener: SessionClickListener? = null + fun bind(session: Session) { itemView.title.text = session.title session.sessionType.let { @@ -36,5 +40,9 @@ class SessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { true -> itemView.shortAbstract.isVisible = false false -> itemView.shortAbstract.text = shortBio } + + itemView.setOnClickListener { + sessionClickListener?.onClick(session.id) + } } } diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt new file mode 100644 index 000000000..b186d464f --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt @@ -0,0 +1,55 @@ +package org.fossasia.openevent.general.sessions + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import io.reactivex.android.schedulers.AndroidSchedulers +import org.fossasia.openevent.general.BuildConfig.MAPBOX_KEY +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.common.SingleLiveEvent +import org.fossasia.openevent.general.data.Resource +import timber.log.Timber + +class SessionViewModel( + private val sessionService: SessionService, + private val resource: Resource +) : ViewModel() { + private val compositeDisposable = CompositeDisposable() + + private val mutableSession = MutableLiveData() + val session: LiveData = mutableSession + private val mutableError = SingleLiveEvent() + val error: LiveData = mutableError + + fun loadSession(id: Long) { + if (id == -1L) { + mutableError.value = resource.getString(R.string.error_fetching_event_message) + return + } + + compositeDisposable.add(sessionService.fetchSession(id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + mutableSession.value = it + }, { + Timber.e(it, "Error fetching session id $id") + mutableError.value = resource.getString(R.string.error_fetching_event_message) + }) + ) + } + + fun loadMap(latitude: String, longitude: String): String { + // location handling + val BASE_URL = "https://api.mapbox.com/v4/mapbox.emerald/pin-l-marker+673ab7" + val LOCATION = "($longitude,$latitude)/$longitude,$latitude" + return "$BASE_URL$LOCATION,15/900x500.png?access_token=$MAPBOX_KEY" + } + + override fun onCleared() { + super.onCleared() + compositeDisposable.clear() + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/Microlocation.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocation.kt similarity index 76% rename from app/src/main/java/org/fossasia/openevent/general/sessions/Microlocation.kt rename to app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocation.kt index e601ea7bb..967356655 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/Microlocation.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocation.kt @@ -1,5 +1,7 @@ -package org.fossasia.openevent.general.sessions +package org.fossasia.openevent.general.sessions.microlocation +import androidx.room.Entity +import androidx.room.PrimaryKey import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.annotation.JsonNaming import com.github.jasminb.jsonapi.LongIdHandler @@ -8,8 +10,10 @@ import com.github.jasminb.jsonapi.annotations.Type @Type("microlocation") @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) -data class Microlocation( +@Entity +data class MicroLocation( @Id(LongIdHandler::class) + @PrimaryKey val id: Long, val name: String, val room: String?, diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocationConverter.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocationConverter.kt new file mode 100644 index 000000000..a6a1fdb95 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/microlocation/MicroLocationConverter.kt @@ -0,0 +1,14 @@ +package org.fossasia.openevent.general.sessions.microlocation + +import androidx.room.TypeConverter +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper + +class MicroLocationConverter { + @TypeConverter + fun toMicroLoation(json: String) = + jacksonObjectMapper().readerFor(MicroLocation::class.java).readValue(json) + + @TypeConverter + fun toJson(microLocation: MicroLocation?) = ObjectMapper().writeValueAsString(microLocation) +} diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionType.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionType.kt similarity index 77% rename from app/src/main/java/org/fossasia/openevent/general/sessions/SessionType.kt rename to app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionType.kt index 5d3a7a7b2..be8984069 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionType.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionType.kt @@ -1,5 +1,7 @@ -package org.fossasia.openevent.general.sessions +package org.fossasia.openevent.general.sessions.sessiontype +import androidx.room.Entity +import androidx.room.PrimaryKey import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.annotation.JsonNaming import com.github.jasminb.jsonapi.LongIdHandler @@ -8,8 +10,10 @@ import com.github.jasminb.jsonapi.annotations.Type @Type("session-type") @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) +@Entity data class SessionType( @Id(LongIdHandler::class) + @PrimaryKey val id: Long, val name: String, val length: String?, diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionTypeConverter.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionTypeConverter.kt new file mode 100644 index 000000000..f485608d6 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/sessiontype/SessionTypeConverter.kt @@ -0,0 +1,15 @@ +package org.fossasia.openevent.general.sessions.sessiontype + +import androidx.room.TypeConverter +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper + +class SessionTypeConverter { + + @TypeConverter + fun toSessionType(json: String): SessionType? = + jacksonObjectMapper().readerFor(SessionType::class.java).readValue(json) + + @TypeConverter + fun toJson(sessionType: SessionType?) = ObjectMapper().writeValueAsString(sessionType) +} diff --git a/app/src/main/res/drawable/ic_language_black.xml b/app/src/main/res/drawable/ic_language_black.xml new file mode 100644 index 000000000..180a16a03 --- /dev/null +++ b/app/src/main/res/drawable/ic_language_black.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_session.xml b/app/src/main/res/layout/fragment_session.xml new file mode 100644 index 000000000..70d03b1bb --- /dev/null +++ b/app/src/main/res/layout/fragment_session.xml @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_session.xml b/app/src/main/res/layout/item_session.xml index 4db0652d6..6edc27d9e 100644 --- a/app/src/main/res/layout/item_session.xml +++ b/app/src/main/res/layout/item_session.xml @@ -7,6 +7,7 @@ android:layout_margin="@dimen/layout_margin_medium" android:elevation="@dimen/card_elevation" app:cardBackgroundColor="@android:color/white" + android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="@dimen/card_corner_radius"> + app:cardBackgroundColor="@android:color/white" + android:foreground="?android:attr/selectableItemBackground"> + + + + Date: Mon, 13 May 2019 13:47:58 +0530 Subject: [PATCH 12/35] feat: Update settings and profile fragment for non authenticated users (#1754) --- .../openevent/general/auth/ProfileFragment.kt | 16 ++++-- .../general/settings/SettingsFragment.kt | 10 +++- app/src/main/res/layout/fragment_profile.xml | 11 ++++ app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/settings.xml | 55 ++++++++++--------- 5 files changed, 61 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt index d3a4b73f0..26f252bcd 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt @@ -23,6 +23,7 @@ import kotlinx.android.synthetic.main.fragment_profile.view.logoutLL import kotlinx.android.synthetic.main.fragment_profile.view.manageEventsLL import kotlinx.android.synthetic.main.fragment_profile.view.settingsLL import kotlinx.android.synthetic.main.fragment_profile.view.ticketIssuesLL +import kotlinx.android.synthetic.main.fragment_profile.view.loginButton import org.fossasia.openevent.general.CircleTransform import org.fossasia.openevent.general.R import org.fossasia.openevent.general.utils.Utils @@ -41,7 +42,7 @@ class ProfileFragment : Fragment() { private fun redirectToLogin() { findNavController(rootView).navigate(ProfileFragmentDirections - .actionProfileToLogin(getString(R.string.log_in_first)) + .actionProfileToLogin() ) } @@ -51,9 +52,13 @@ class ProfileFragment : Fragment() { override fun onStart() { super.onStart() - if (!profileViewModel.isLoggedIn()) { - redirectToLogin() - } + handleLayoutVisibility(profileViewModel.isLoggedIn()) + } + + private fun handleLayoutVisibility(isLoggedIn: Boolean) { + rootView.editProfileRL.isVisible = isLoggedIn + rootView.logoutLL.isVisible = isLoggedIn + rootView.loginButton.isVisible = !isLoggedIn } override fun onCreateView( @@ -93,7 +98,7 @@ class ProfileFragment : Fragment() { } }) - fetchProfile() + if (profileViewModel.isLoggedIn()) fetchProfile() rootView.manageEventsLL.setOnClickListener { startOrgaApp("com.eventyay.organizer") } @@ -106,6 +111,7 @@ class ProfileFragment : Fragment() { } rootView.logoutLL.setOnClickListener { showLogoutDialog() } + rootView.loginButton.setOnClickListener { redirectToLogin() } return rootView } diff --git a/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt index c41131f4d..7c9e03d50 100644 --- a/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt @@ -56,8 +56,8 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceChangeListener { setHasOptionsMenu(true) // Set Email - preferenceScreen.findPreference(getString(R.string.key_profile)) - .summary = safeArgs.email + preferenceScreen.findPreference(getString(R.string.key_account)) + .summary = if (safeArgs.email.isNullOrEmpty()) getString(R.string.not_logged_in) else safeArgs.email // Set Build Version preferenceScreen.findPreference(getString(R.string.key_version)) @@ -65,6 +65,12 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceChangeListener { preferenceScreen.findPreference(getString(R.string.key_timezone_switch)) .setDefaultValue(timeZonePreference.getBoolean("useEventTimeZone", false)) + + preferenceScreen.findPreference(getString(R.string.key_profile)).isVisible = profileViewModel.isLoggedIn() + preferenceScreen.findPreference(getString(R.string.key_change_password)).isVisible = + profileViewModel.isLoggedIn() + preferenceScreen.findPreference(getString(R.string.key_timezone_switch)).isVisible = + profileViewModel.isLoggedIn() } override fun onPreferenceTreeClick(preference: Preference?): Boolean { diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 025a8a224..5bfcc6fc0 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -217,9 +217,20 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:visibility="gone" android:elevation="@dimen/card_elevation" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5c0f2cdd1..e7de46236 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,7 +53,7 @@ Username Log out Settings - Login + Log In I forgot my password We just sent you an email with a link to reset\nyour password Check your email! @@ -162,6 +162,7 @@ version about rating + account Rate Us Open Event Android Suggest Improvement @@ -188,6 +189,7 @@ Legal Visit Website Visit_Website + No account logged in Are you sure you want to log out? diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 62e2a98d4..13323c7fc 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -2,34 +2,13 @@ - - - - - - - - - - + + + + + + + + + + From a94fb571d9b6c7cc71f8481a428440bfb033a62c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 14 May 2019 14:11:29 +0530 Subject: [PATCH 13/35] chore(deps): bump mapbox-sdk-services from 4.7.0 to 4.8.0 (#1760) Bumps [mapbox-sdk-services](https://github.com/mapbox/mapbox-java) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mapbox/mapbox-java/releases) - [Changelog](https://github.com/mapbox/mapbox-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/mapbox/mapbox-java/compare/v4.7.0...v4.8.0) Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6effc3054..90857563c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -176,7 +176,7 @@ dependencies { implementation "org.jetbrains.anko:anko-design:$anko_version" //Mapbox java sdk - implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:4.7.0' + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:4.8.0' // Stetho debugImplementation 'com.facebook.stetho:stetho:1.5.1' From dd24a8c6da27eb3e08629060b21c47b1a84a63e5 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Tue, 14 May 2019 15:11:50 +0200 Subject: [PATCH 14/35] fix: Updating feedback section after making new one (#1747) Detail: - Add livedata object to observe new submitted feedback. - Extract string + fix small bugs Fixes: #1746 --- .../general/event/EventDetailsFragment.kt | 16 ++++++---- .../general/event/EventDetailsViewModel.kt | 29 ++++++++++++------- .../event/feedback/FeedbackRecyclerAdapter.kt | 7 ++++- app/src/main/res/values/strings.xml | 4 +++ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt index ba004eeff..a63743b95 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt @@ -45,7 +45,6 @@ import kotlinx.android.synthetic.main.content_event.view.speakerRv import kotlinx.android.synthetic.main.content_event.view.speakersContainer import kotlinx.android.synthetic.main.content_event.view.sponsorsRecyclerView import kotlinx.android.synthetic.main.content_event.view.sponsorsSummaryContainer -import kotlinx.android.synthetic.main.content_event.view.feedbackContainer import kotlinx.android.synthetic.main.fragment_event.view.buttonTickets import kotlinx.android.synthetic.main.fragment_event.view.eventErrorCard import kotlinx.android.synthetic.main.fragment_event.view.container @@ -152,10 +151,11 @@ class EventDetailsFragment : Fragment() { loadTicketFragment() } - eventViewModel.error + eventViewModel.popMessage .nonNull() .observe(viewLifecycleOwner, Observer { - showEventErrorScreen(true) + rootView.snackbar(it) + showEventErrorScreen(it == getString(R.string.error_fetching_event_message)) }) eventViewModel.eventFeedback.observe(viewLifecycleOwner, Observer { @@ -167,9 +167,16 @@ class EventDetailsFragment : Fragment() { rootView.feedbackRv.isVisible = true rootView.noFeedBackTv.isVisible = false } - rootView.feedbackContainer.isVisible = it.isNotEmpty() }) + eventViewModel.submittedFeedback + .nonNull() + .observe(viewLifecycleOwner, Observer { + feedbackAdapter.add(it) + rootView.feedbackRv.isVisible = true + rootView.noFeedBackTv.isVisible = false + }) + rootView.feedbackBtn.setOnClickListener { checkForAuthentication() } @@ -517,7 +524,6 @@ class EventDetailsFragment : Fragment() { eventViewModel.submitFeedback(layout.feedback.text.toString(), layout.feedbackrating.rating, safeArgs.eventId) - rootView.snackbar(R.string.feedback_submitted) } .setNegativeButton(getString(R.string.cancel)) { dialog, _ -> dialog.cancel() diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt index c9f05bb35..d969e96b4 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt @@ -38,12 +38,14 @@ class EventDetailsViewModel( val progress: LiveData = mutableProgress private val mutableUser = MutableLiveData() val user: LiveData = mutableUser - private val mutableError = SingleLiveEvent() - val error: LiveData = mutableError + private val mutablePopMessage = SingleLiveEvent() + val popMessage: LiveData = mutablePopMessage private val mutableEvent = MutableLiveData() val event: LiveData = mutableEvent private val mutableEventFeedback = MutableLiveData>() val eventFeedback: LiveData> = mutableEventFeedback + private val mutableSubmittedFeedback = MutableLiveData() + val submittedFeedback: LiveData = mutableSubmittedFeedback private val mutableEventSessions = MutableLiveData>() val eventSessions: LiveData> = mutableEventSessions private var eventSpeakers: LiveData> = MutableLiveData() @@ -59,6 +61,8 @@ class EventDetailsViewModel( mutableEventFeedback.value = it }, { Timber.e(it, "Error fetching events feedback") + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.feedback)) }) } @@ -68,9 +72,10 @@ class EventDetailsViewModel( compositeDisposable += eventService.submitFeedback(feedback) .withDefaultSchedulers() .subscribe({ - //Do Nothing + mutablePopMessage.value = resource.getString(R.string.feedback_submitted) + mutableSubmittedFeedback.value = it }, { - it.message.toString() == "HTTP 400 BAD REQUEST" + mutablePopMessage.value = resource.getString(R.string.error_submitting_feedback) }) } fun fetchEventSpeakers(id: Long) { @@ -80,7 +85,8 @@ class EventDetailsViewModel( //Do Nothing }, { Timber.e(it, "Error fetching speaker for event id %d", id) - mutableError.value = resource.getString(R.string.error_fetching_event_message) + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.speakers)) }) } @@ -96,15 +102,16 @@ class EventDetailsViewModel( //Do Nothing }, { Timber.e(it, "Error fetching sponsor for event id %d", id) - mutableError.value = resource.getString(R.string.error_fetching_event_message) + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.sponsors)) }) } fun loadEventSponsors(id: Long): LiveData> = sponsorService.fetchSponsorsFromDb(id) fun loadEvent(id: Long) { - if (id.equals(-1)) { - mutableError.value = resource.getString(R.string.error_fetching_event_message) + if (id == -1L) { + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_message) return } compositeDisposable += eventService.getEvent(id) @@ -117,7 +124,7 @@ class EventDetailsViewModel( mutableEvent.value = it }, { Timber.e(it, "Error fetching event %d", id) - mutableError.value = resource.getString(R.string.error_fetching_event_message) + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_message) }) } @@ -127,6 +134,8 @@ class EventDetailsViewModel( .subscribe({ mutableEventSessions.value = it }, { + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.sessions)) Timber.e(it, "Error fetching events sessions") }) } @@ -145,7 +154,7 @@ class EventDetailsViewModel( Timber.d("Success") }, { Timber.e(it, "Error") - mutableError.value = resource.getString(R.string.error) + mutablePopMessage.value = resource.getString(R.string.error) }) } diff --git a/app/src/main/java/org/fossasia/openevent/general/event/feedback/FeedbackRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/event/feedback/FeedbackRecyclerAdapter.kt index 510a376c2..771d53aa8 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/feedback/FeedbackRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/feedback/FeedbackRecyclerAdapter.kt @@ -6,7 +6,7 @@ import androidx.recyclerview.widget.RecyclerView import org.fossasia.openevent.general.R class FeedbackRecyclerAdapter : RecyclerView.Adapter() { - val feedbackList = ArrayList() + private val feedbackList = ArrayList() fun addAll(feedbackList: List) { if (feedbackList.isNotEmpty()) @@ -15,6 +15,11 @@ class FeedbackRecyclerAdapter : RecyclerView.Adapter() { notifyDataSetChanged() } + fun add(feedback: Feedback) { + feedbackList.add(0, feedback) + notifyItemInserted(0) + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedbackViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_feedback, parent, false) return FeedbackViewHolder(view) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e7de46236..bee11caf0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -302,6 +302,9 @@ Please provide first name and last name! Error updating user! User updated successfully! + "Error fetching %1$s for the event + Fail on submitting the feedback + Feedback submitted Failure @@ -326,6 +329,7 @@ Free stuff only Speakers Sponsors + Sessions Some description about this sponsor: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. name starts-at From 5cf13bd7dca38a155a09895aef55ea6eef3084c7 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Tue, 14 May 2019 16:32:37 +0200 Subject: [PATCH 15/35] feat: Add delete user photo feature (#1757) Detail: - Update user photo by default image (encoded) Fixes: #1017 --- .../general/auth/EditProfileFragment.kt | 32 ++++++++++++++++--- app/src/main/res/values/strings.xml | 3 ++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt index 4e7124255..c868a57de 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt @@ -14,6 +14,7 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer @@ -127,11 +128,7 @@ class EditProfileFragment : Fragment() { }) rootView.profilePhotoFab.setOnClickListener { - if (permissionGranted) { - showFileChooser() - } else { - requestPermissions(READ_STORAGE, REQUEST_CODE) - } + showEditPhotoDialog() } return rootView @@ -152,6 +149,31 @@ class EditProfileFragment : Fragment() { } } + private fun showEditPhotoDialog() { + AlertDialog.Builder(requireContext()) + .setMessage(getString(R.string.edit_profile_photo_message)) + .setNegativeButton(getString(R.string.delete)) { _, _ -> + clearAvatar() + }.setPositiveButton(getString(R.string.new_photo)) { _, _ -> + if (permissionGranted) { + showFileChooser() + } else { + requestPermissions(READ_STORAGE, REQUEST_CODE) + } + }.create().show() + } + + private fun clearAvatar() { + val drawable = requireDrawable(requireContext(), R.drawable.ic_account_circle_grey) + Picasso.get() + .load(R.drawable.ic_account_circle_grey) + .placeholder(drawable) + .transform(CircleTransform()) + .into(rootView.profilePhoto) + editProfileViewModel.encodedImage = encodeImage(drawable.toBitmap(120, 120)) + editProfileViewModel.avatarUpdated = true + } + private fun encodeImage(bitmap: Bitmap): String { val baos = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bee11caf0..26ac36448 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -70,6 +70,9 @@ Welcome back! Update Profile Photo + Edit your profile picture + Delete + New Photo eventyay From 86242f2475444eaa930d70637b6250207513c335 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Tue, 14 May 2019 16:33:41 +0200 Subject: [PATCH 16/35] fix: Retain searchview state in SearchFragment (#1756) Detail: Retain search query in SearchFragment and require focus when making search on screen rotation Fixes: #862 --- .../fossasia/openevent/general/search/SearchFragment.kt | 8 ++++++++ .../fossasia/openevent/general/search/SearchViewModel.kt | 2 ++ app/src/main/res/menu/search.xml | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt index be408efcb..4eb6f186a 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt @@ -127,11 +127,19 @@ class SearchFragment : Fragment() { rootView.fabSearch.setOnClickListener { queryListener.onQueryTextSubmit(searchView.query.toString()) } + + if (searchViewModel.isQuerying) { + searchItem.expandActionView() + searchView.setQuery(searchViewModel.searchViewQuery, false) + searchView.clearFocus() + } super.onPrepareOptionsMenu(menu) } override fun onDestroyView() { super.onDestroyView() + searchViewModel.isQuerying = !searchView.isIconified + searchViewModel.searchViewQuery = searchView.query.toString() searchView.isSaveEnabled = false } diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt index 90a06a24e..e092bdeab 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt @@ -53,6 +53,8 @@ class SearchViewModel( private val savedNextToNextMonth = getNextToNextMonth() private val mutableEventTypes = MutableLiveData>() val eventTypes: LiveData> = mutableEventTypes + var searchViewQuery: String = "" + var isQuerying = false fun loadEventTypes() { compositeDisposable += eventService.getEventTypes() diff --git a/app/src/main/res/menu/search.xml b/app/src/main/res/menu/search.xml index 0ebf0d440..ff58e6004 100644 --- a/app/src/main/res/menu/search.xml +++ b/app/src/main/res/menu/search.xml @@ -7,5 +7,5 @@ android:title="@string/search" app:iconTint="@android:color/white" app:actionViewClass="android.widget.SearchView" - app:showAsAction="ifRoom|collapseActionView" /> + app:showAsAction="always|collapseActionView" /> From b3cec1f484580dbe6fd94f1815dd5b6807d15b39 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Tue, 14 May 2019 21:00:26 +0200 Subject: [PATCH 17/35] fix: Reset default value for order Detail: - Set default value of order to 0 instead of null Fixes: #1621 --- .../openevent/general/attendees/AttendeeViewModel.kt | 6 +++--- .../main/java/org/fossasia/openevent/general/order/Order.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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 e6530c09a..d525d1905 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 @@ -246,11 +246,11 @@ class AttendeeViewModel( private fun createOrder() { val attendeeList = attendees.map { AttendeeId(it.id) }.toList() - var amount = totalAmount.value + var amount: Float = totalAmount.value ?: 0F var paymentMode: String? = paymentOption.toLowerCase() - if (amount == null || amount <= 0) { + if (amount <= 0) { paymentMode = resource.getString(R.string.free) - amount = null + amount = 0F } val eventId = event.value?.id if (eventId != null) { diff --git a/app/src/main/java/org/fossasia/openevent/general/order/Order.kt b/app/src/main/java/org/fossasia/openevent/general/order/Order.kt index 116265dfd..6ed5b477c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/Order.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/Order.kt @@ -31,7 +31,7 @@ data class Order( val paymentMode: String? = null, val country: String? = null, val status: String? = null, - val amount: Float? = null, + val amount: Float = 0F, val identifier: String? = null, val orderNotes: String? = null, val completedAt: String? = null, From 9ec3104ff50c6b94174512ed06b464656f8468a4 Mon Sep 17 00:00:00 2001 From: Aman Bansal Date: Wed, 15 May 2019 02:17:57 +0530 Subject: [PATCH 18/35] feat: Notification Fragment (#1622) --- .../fossasia/openevent/general/di/Modules.kt | 12 +- .../openevent/general/event/EventUtils.kt | 24 +++ .../openevent/general/event/EventsFragment.kt | 19 ++- .../general/notification/Notification.kt | 21 +++ .../general/notification/NotificationApi.kt | 24 +++ .../notification/NotificationFragment.kt | 141 ++++++++++++++++++ .../notification/NotificationService.kt | 11 ++ .../notification/NotificationViewModel.kt | 66 ++++++++ .../NotificationsRecyclerAdapter.kt | 29 ++++ .../notification/NotificationsViewHolder.kt | 29 ++++ .../res/drawable/ic_notifications_none.xml | 5 + .../res/drawable/ic_notifications_white.xml | 5 + .../main/res/layout/fragment_notification.xml | 76 ++++++++++ .../res/layout/item_card_notification.xml | 62 ++++++++ .../placeholder_item_card_notification.xml | 47 ++++++ app/src/main/res/menu/events.xml | 11 ++ .../main/res/navigation/navigation_graph.xml | 12 ++ app/src/main/res/values-bn-rIN/strings.xml | 4 + app/src/main/res/values-hi-rIN/strings.xml | 3 + app/src/main/res/values-vi/strings.xml | 3 + app/src/main/res/values/strings.xml | 5 + 21 files changed, 607 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/Notification.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/NotificationApi.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/NotificationService.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/NotificationViewModel.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/NotificationsRecyclerAdapter.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/notification/NotificationsViewHolder.kt create mode 100644 app/src/main/res/drawable/ic_notifications_none.xml create mode 100644 app/src/main/res/drawable/ic_notifications_white.xml create mode 100644 app/src/main/res/layout/fragment_notification.xml create mode 100644 app/src/main/res/layout/item_card_notification.xml create mode 100644 app/src/main/res/layout/placeholder_item_card_notification.xml create mode 100644 app/src/main/res/menu/events.xml diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index b742d04b3..7127a0d1c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -55,6 +55,10 @@ import org.fossasia.openevent.general.event.types.EventTypesApi import org.fossasia.openevent.general.event.topic.SimilarEventsViewModel import org.fossasia.openevent.general.favorite.FavoriteEventsRecyclerAdapter import org.fossasia.openevent.general.favorite.FavoriteEventsViewModel +import org.fossasia.openevent.general.notification.Notification +import org.fossasia.openevent.general.notification.NotificationApi +import org.fossasia.openevent.general.notification.NotificationService +import org.fossasia.openevent.general.notification.NotificationViewModel import org.fossasia.openevent.general.order.Charge import org.fossasia.openevent.general.order.ConfirmOrder import org.fossasia.openevent.general.order.Order @@ -176,6 +180,10 @@ val apiModule = module { val retrofit: Retrofit = get() retrofit.create(SponsorApi::class.java) } + single { + val retrofit: Retrofit = get() + retrofit.create(NotificationApi::class.java) + } factory { AuthHolder(get()) } factory { AuthService(get(), get(), get()) } @@ -190,6 +198,7 @@ val apiModule = module { factory { SessionService(get(), get()) } factory { Resource() } factory { MutableConnectionLiveData() } + factory { NotificationService(get()) } } val viewModelModule = module { @@ -219,6 +228,7 @@ val viewModelModule = module { viewModel { SmartAuthViewModel() } viewModel { SpeakerViewModel(get(), get()) } viewModel { SponsorsViewModel(get(), get()) } + viewModel { NotificationViewModel(get(), get(), get(), get()) } } val networkModule = module { @@ -260,7 +270,7 @@ val networkModule = module { CustomForm::class.java, EventLocation::class.java, EventType::class.java, EventSubTopic::class.java, Feedback::class.java, Speaker::class.java, Session::class.java, SessionType::class.java, MicroLocation::class.java, - Sponsor::class.java, EventFAQ::class.java) + Sponsor::class.java, EventFAQ::class.java, Notification::class.java) Retrofit.Builder() .client(get()) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt index 40bcd4726..e2f51d1a9 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt @@ -207,6 +207,30 @@ object EventUtils { } } + fun getFormattedDateWithoutWeekday(date: ZonedDateTime): String { + val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("MMM d, y") + return try { + dateFormat.format(date) + } catch (e: IllegalArgumentException) { + Timber.e(e, "Error formatting Date") + "" + } + } + + fun getFormattedWeekDay(date: ZonedDateTime): String { + val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("EEE") + return try { + dateFormat.format(date) + } catch (e: IllegalArgumentException) { + Timber.e(e, "Error formatting Date") + "" + } + } + + fun getDayDifferenceFromToday(date: String): Long { + return (System.currentTimeMillis() - EventUtils.getTimeInMilliSeconds(date, null)) / (1000 * 60 * 60 * 24) + } + /** * share event detail along with event image * if image loading is successful then imageView tag will be set to String diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt index a36856a6a..95eb754a0 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt @@ -5,6 +5,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer @@ -75,7 +78,7 @@ class EventsFragment : Fragment(), ScrollToTop { savedInstanceState: Bundle? ): View? { rootView = inflater.inflate(R.layout.fragment_events, container, false) - + setHasOptionsMenu(true) if (preference.getString(SAVED_LOCATION).isNullOrEmpty()) { findNavController(requireActivity(), R.id.frameContainer).navigate(R.id.welcomeFragment) } @@ -197,6 +200,20 @@ class EventsFragment : Fragment(), ScrollToTop { type = hashTag)) } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.events, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.notifications -> { + findNavController(rootView).navigate(EventsFragmentDirections.actionEventsToNotification()) + true + } + else -> super.onOptionsItemSelected(item) + } + } + private fun showNoInternetScreen(show: Boolean) { rootView.homeScreenLL.visibility = if (!show) View.VISIBLE else View.GONE rootView.noInternetCard.visibility = if (show) View.VISIBLE else View.GONE diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/Notification.kt b/app/src/main/java/org/fossasia/openevent/general/notification/Notification.kt new file mode 100644 index 000000000..e433e0632 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/Notification.kt @@ -0,0 +1,21 @@ +package org.fossasia.openevent.general.notification + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import com.github.jasminb.jsonapi.IntegerIdHandler +import com.github.jasminb.jsonapi.annotations.Id +import com.github.jasminb.jsonapi.annotations.Type +import io.reactivex.annotations.NonNull + +@Type("notification") +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) +data class Notification( + @Id(IntegerIdHandler::class) + @NonNull + val id: Long, + val message: String? = null, + val receivedAt: String? = null, + val isRead: Boolean? = null, + val title: String? = null, + val deletedAt: String? = null +) diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationApi.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationApi.kt new file mode 100644 index 000000000..1c159bede --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationApi.kt @@ -0,0 +1,24 @@ +package org.fossasia.openevent.general.notification + +import io.reactivex.Completable +import io.reactivex.Single +import retrofit2.http.DELETE +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PATCH +import retrofit2.http.Path + +interface NotificationApi { + + @GET("users/{userId}/notifications?sort=message") + fun getNotifications(@Path("userId") userId: Long): Single> + + @PATCH("notifications/{notification_id}") + fun updateNotification( + @Path("notification_id") notificationId: Long, + @Body notification: Notification + ): Single> + + @DELETE("notifications/{notification_id}") + fun deleteNotification(@Path("notification_id") notificationId: Long): Completable +} diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt new file mode 100644 index 000000000..b56694182 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt @@ -0,0 +1,141 @@ +package org.fossasia.openevent.general.notification + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.navigation.Navigation +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.android.synthetic.main.content_no_internet.view.retry +import kotlinx.android.synthetic.main.content_no_internet.view.noInternetCard +import kotlinx.android.synthetic.main.fragment_notification.view.notificationRecycler +import kotlinx.android.synthetic.main.fragment_notification.view.swiperefresh +import kotlinx.android.synthetic.main.fragment_notification.view.shimmerNotifications +import kotlinx.android.synthetic.main.fragment_notification.view.notificationCoordinatorLayout +import kotlinx.android.synthetic.main.fragment_notification.view.noNotification +import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.auth.LoginFragmentArgs +import org.fossasia.openevent.general.utils.Utils +import org.fossasia.openevent.general.utils.Utils.setToolbar +import org.fossasia.openevent.general.utils.extensions.nonNull +import org.jetbrains.anko.design.snackbar +import org.koin.androidx.viewmodel.ext.android.viewModel + +class NotificationFragment : Fragment() { + private val notificationViewModel by viewModel() + private val recyclerAdapter = NotificationsRecyclerAdapter() + private lateinit var rootView: View + + override fun onStart() { + super.onStart() + if (!notificationViewModel.isLoggedIn()) { + redirectToLogin() + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + rootView = inflater.inflate(R.layout.fragment_notification, container, false) + setToolbar(activity, getString(R.string.title_notifications), true) + setHasOptionsMenu(true) + + if (notificationViewModel.isLoggedIn()) { + initObservers() + if (notificationViewModel.notifications.value == null) { + notificationViewModel.getNotifications() + } + rootView.notificationRecycler.layoutManager = LinearLayoutManager(requireContext()) + rootView.notificationRecycler.adapter = recyclerAdapter + } + return rootView + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + rootView.retry.setOnClickListener { + notificationViewModel.getNotifications() + } + + rootView.swiperefresh.setOnRefreshListener { + notificationViewModel.getNotifications() + } + } + + private fun initObservers() { + notificationViewModel.notifications + .nonNull() + .observe(viewLifecycleOwner, Observer { + showNoNotifications(it.isEmpty()) + recyclerAdapter.addAll(it) + recyclerAdapter.notifyDataSetChanged() + }) + + notificationViewModel.error + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.swiperefresh.isRefreshing = false + rootView.notificationCoordinatorLayout.snackbar(it) + }) + + notificationViewModel.progress + .nonNull() + .observe(viewLifecycleOwner, Observer { + if (it) { + rootView.shimmerNotifications.startShimmer() + rootView.noNotification.isVisible = false + rootView.notificationRecycler.isVisible = false + } else { + rootView.shimmerNotifications.stopShimmer() + rootView.swiperefresh.isRefreshing = it + } + rootView.shimmerNotifications.isVisible = it + }) + + notificationViewModel.noInternet + .nonNull() + .observe(viewLifecycleOwner, Observer { + if (it) { + rootView.notificationCoordinatorLayout + .snackbar(resources.getString(R.string.no_internet_connection_message)) + rootView.swiperefresh.isRefreshing = !it + } + showNoInternet(it && notificationViewModel.notifications.value.isNullOrEmpty()) + }) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + activity?.onBackPressed() + true + } + else -> super.onOptionsItemSelected(item) + } + } + + private fun showNoInternet(visible: Boolean) { + rootView.noInternetCard.isVisible = visible + rootView.notificationRecycler.isVisible = !visible + } + + private fun showNoNotifications(visible: Boolean) { + rootView.noNotification.isVisible = visible + rootView.notificationRecycler.isVisible = !visible + } + + private fun redirectToLogin() { + LoginFragmentArgs(getString(R.string.log_in_first)) + .toBundle() + .also { + Navigation.findNavController(rootView).navigate(R.id.loginFragment, it, Utils.getAnimFade()) + } + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationService.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationService.kt new file mode 100644 index 000000000..5cd32b1c9 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationService.kt @@ -0,0 +1,11 @@ +package org.fossasia.openevent.general.notification + +import io.reactivex.Single + +class NotificationService( + private val notificationApi: NotificationApi +) { + fun getNotifications(userId: Long): Single> { + return notificationApi.getNotifications(userId) + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationViewModel.kt new file mode 100644 index 000000000..d8dcc8162 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationViewModel.kt @@ -0,0 +1,66 @@ +package org.fossasia.openevent.general.notification + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.auth.AuthHolder +import org.fossasia.openevent.general.common.SingleLiveEvent +import org.fossasia.openevent.general.data.Network +import org.fossasia.openevent.general.data.Resource +import org.fossasia.openevent.general.utils.extensions.withDefaultSchedulers +import timber.log.Timber + +class NotificationViewModel( + private val notificationService: NotificationService, + private val authHolder: AuthHolder, + private val network: Network, + private val resource: Resource +) : ViewModel() { + + private val compositeDisposable = CompositeDisposable() + private val mutableNotifications = MutableLiveData>() + val notifications: LiveData> = mutableNotifications + + private val mutableProgress = MutableLiveData() + val progress: LiveData = mutableProgress + + private val mutableNoInternet = SingleLiveEvent() + val noInternet: LiveData = mutableNoInternet + + private val mutableError = SingleLiveEvent() + val error: LiveData = mutableError + + fun getId() = authHolder.getId() + + fun isLoggedIn() = authHolder.isLoggedIn() + + fun getNotifications() { + + if (!isConnected()) { + return + } + + compositeDisposable += notificationService.getNotifications(getId()) + .withDefaultSchedulers() + .doOnSubscribe { + mutableProgress.value = true + }.doFinally { + mutableProgress.value = false + }.subscribe({ + mutableNotifications.value = it + Timber.d("Notification retrieve successful") + }, { + mutableError.value = resource.getString(R.string.msg_failed_to_load_notification) + Timber.d(it, resource.getString(R.string.msg_failed_to_load_notification)) + }) + } + + fun isConnected(): Boolean { + val isConnected = network.isNetworkConnected() + mutableNoInternet.value = !isConnected + return isConnected + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationsRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationsRecyclerAdapter.kt new file mode 100644 index 000000000..17de5f036 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationsRecyclerAdapter.kt @@ -0,0 +1,29 @@ +package org.fossasia.openevent.general.notification + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.fossasia.openevent.general.R + +class NotificationsRecyclerAdapter : RecyclerView.Adapter() { + + private val notificationList = ArrayList() + + fun addAll(list: List) { + notificationList.clear() + notificationList.addAll(list) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationsViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_card_notification, parent, false) + return NotificationsViewHolder(view) + } + + override fun onBindViewHolder(holder: NotificationsViewHolder, position: Int) { + holder.bind(notificationList[position]) + } + + override fun getItemCount(): Int { + return notificationList.size + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationsViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationsViewHolder.kt new file mode 100644 index 000000000..227f62ba0 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationsViewHolder.kt @@ -0,0 +1,29 @@ +package org.fossasia.openevent.general.notification + +import android.text.method.LinkMovementMethod +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.item_card_notification.view.* +import org.fossasia.openevent.general.event.EventUtils +import org.fossasia.openevent.general.utils.stripHtml + +class NotificationsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + fun bind( + notification: Notification + + ) { + itemView.title.text = notification.title + itemView.message.text = notification.message.stripHtml() + itemView.message.movementMethod = LinkMovementMethod.getInstance() + notification.receivedAt?.let { + val dayDiff = EventUtils.getDayDifferenceFromToday(it) + val formattedDateTime = EventUtils.getEventDateTime(it, null) + itemView.time.text = when (dayDiff) { + 0L -> EventUtils.getFormattedTime(formattedDateTime) + in 1..6 -> EventUtils.getFormattedWeekDay(formattedDateTime) + else -> EventUtils.getFormattedDateWithoutWeekday(formattedDateTime) + } + } + } +} diff --git a/app/src/main/res/drawable/ic_notifications_none.xml b/app/src/main/res/drawable/ic_notifications_none.xml new file mode 100644 index 000000000..6b0bba064 --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_none.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_notifications_white.xml b/app/src/main/res/drawable/ic_notifications_white.xml new file mode 100644 index 000000000..2ad953fe2 --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_white.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_notification.xml b/app/src/main/res/layout/fragment_notification.xml new file mode 100644 index 000000000..d03fb6cd5 --- /dev/null +++ b/app/src/main/res/layout/fragment_notification.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_card_notification.xml b/app/src/main/res/layout/item_card_notification.xml new file mode 100644 index 000000000..d48ad7b0d --- /dev/null +++ b/app/src/main/res/layout/item_card_notification.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/placeholder_item_card_notification.xml b/app/src/main/res/layout/placeholder_item_card_notification.xml new file mode 100644 index 000000000..c2352d564 --- /dev/null +++ b/app/src/main/res/layout/placeholder_item_card_notification.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/events.xml b/app/src/main/res/menu/events.xml new file mode 100644 index 000000000..d2c0428a7 --- /dev/null +++ b/app/src/main/res/menu/events.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/navigation/navigation_graph.xml b/app/src/main/res/navigation/navigation_graph.xml index 63f50071c..e8c58a4fb 100644 --- a/app/src/main/res/navigation/navigation_graph.xml +++ b/app/src/main/res/navigation/navigation_graph.xml @@ -44,6 +44,13 @@ app:popExitAnim="@anim/slide_out_right" app:enterAnim="@anim/slide_in_right" app:exitAnim="@anim/slide_out_left"/> + + diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml index 8f68b6e90..26281dfdd 100644 --- a/app/src/main/res/values-bn-rIN/strings.xml +++ b/app/src/main/res/values-bn-rIN/strings.xml @@ -186,4 +186,8 @@ খুব ছোট পাসওয়ার্ড! দেশ শর্তাবলী গ্রহণ করুন! + + + আপনার কোন বিজ্ঞপ্তি নেই.. + diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index 123a72034..252665b42 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -180,5 +180,8 @@ आपका पासवर्ड और पुष्टिकरण पासवर्ड मेल नहीं खाते हैं! पासवर्ड बहुत छोटा है! + + आपके पास कोई सूचना नहीं है.. + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 2ad6f94df..907874197 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -221,4 +221,7 @@ Lưu vé sự kiện Hoàn tác Rời %1$s khỏi sự kiện yêu thích + + + Bạn không có bất kỳ thông báo nào.. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26ac36448..32693adf8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -339,4 +339,9 @@ Sort By Category + + Failed to load notifications + Notifications + You don\'t have any notification.. + From 5474509a89357a2cd98760789565f7271f864f00 Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Wed, 15 May 2019 17:02:56 +0530 Subject: [PATCH 19/35] feat: Update google smart lock with new updated password (#1764) (#1765) --- .../openevent/general/settings/SettingsFragment.kt | 14 ++++++++++++++ .../general/settings/SettingsViewModel.kt | 8 +++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt index 7c9e03d50..125c3e181 100644 --- a/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/settings/SettingsFragment.kt @@ -22,8 +22,11 @@ import kotlinx.android.synthetic.main.dialog_change_password.view.confirmNewPass import kotlinx.android.synthetic.main.dialog_change_password.view.textInputLayoutNewPassword import kotlinx.android.synthetic.main.dialog_change_password.view.textInputLayoutConfirmNewPassword import org.fossasia.openevent.general.BuildConfig +import org.fossasia.openevent.general.PLAY_STORE_BUILD_FLAVOR import org.fossasia.openevent.general.R import org.fossasia.openevent.general.auth.ProfileViewModel +import org.fossasia.openevent.general.auth.SmartAuthUtil +import org.fossasia.openevent.general.auth.SmartAuthViewModel import org.fossasia.openevent.general.utils.Utils import org.fossasia.openevent.general.utils.nullToEmpty import org.koin.androidx.viewmodel.ext.android.viewModel @@ -41,6 +44,7 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceChangeListener { private val WEBSITE_LINK: String = "https://eventyay.com/" private val settingsViewModel by viewModel() private val profileViewModel by viewModel() + private val smartAuthViewModel by viewModel() private val safeArgs: SettingsFragmentArgs by navArgs() override fun preferenceChange(evt: PreferenceChangeEvent?) { @@ -80,6 +84,16 @@ class SettingsFragment : PreferenceFragmentCompat(), PreferenceChangeListener { view?.snackbar(it) }) + settingsViewModel.updatedPassword + .nonNull() + .observe(viewLifecycleOwner, Observer { + if (BuildConfig.FLAVOR == PLAY_STORE_BUILD_FLAVOR) { + smartAuthViewModel.saveCredential(safeArgs.email.toString(), + it, + SmartAuthUtil.getCredentialsClient(requireActivity())) + } + }) + if (preference?.key == getString(R.string.key_visit_website)) { // Goes to website Utils.openUrl(requireContext(), WEBSITE_LINK) diff --git a/app/src/main/java/org/fossasia/openevent/general/settings/SettingsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/settings/SettingsViewModel.kt index 8bb8addd5..99bc42942 100644 --- a/app/src/main/java/org/fossasia/openevent/general/settings/SettingsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/settings/SettingsViewModel.kt @@ -1,6 +1,7 @@ package org.fossasia.openevent.general.settings import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign @@ -14,6 +15,8 @@ class SettingsViewModel(private val authService: AuthService) : ViewModel() { private val compositeDisposable = CompositeDisposable() private val mutableSnackBar = SingleLiveEvent() val snackBar: LiveData = mutableSnackBar + private val mutableUpdatedPassword = MutableLiveData() + val updatedPassword: LiveData = mutableUpdatedPassword fun isLoggedIn() = authService.isLoggedIn() @@ -39,7 +42,10 @@ class SettingsViewModel(private val authService: AuthService) : ViewModel() { compositeDisposable += authService.changePassword(oldPassword, newPassword) .withDefaultSchedulers() .subscribe({ - if (it.passwordChanged) mutableSnackBar.value = "Password changed successfully!" + if (it.passwordChanged) { + mutableSnackBar.value = "Password changed successfully!" + mutableUpdatedPassword.value = newPassword + } }, { if (it.message.toString() == "HTTP 400 BAD REQUEST") mutableSnackBar.value = "Incorrect Old Password provided!" From bd422f982c86edbc5101a44eb42b2959bacab2b4 Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Thu, 16 May 2019 12:23:22 +0530 Subject: [PATCH 20/35] feat: Add LeakCanary for debug implementation (#1758) --- app/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 90857563c..c1629a4a5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -184,6 +184,9 @@ dependencies { releaseImplementation 'com.github.iamareebjamal:stetho-noop:1.2.1' testImplementation 'com.github.iamareebjamal:stetho-noop:1.2.1' + //LeakCanary + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-1' + testImplementation 'junit:junit:4.12' testImplementation "io.mockk:mockk:1.9.3" testImplementation 'org.threeten:threetenbp:1.4.0' From 031053d507b53e7964f3b56fb386d50175821f38 Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Thu, 16 May 2019 15:38:55 +0530 Subject: [PATCH 21/35] chore: bump gradle from 3.3.2 to 3.4.1 (#1771) --- .../openevent/general/search/PlaceSuggestionsAdapter.kt | 2 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/search/PlaceSuggestionsAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/search/PlaceSuggestionsAdapter.kt index 5f4f5129f..ca25da435 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/PlaceSuggestionsAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/PlaceSuggestionsAdapter.kt @@ -36,7 +36,7 @@ class PlaceSuggestionsAdapter : } override fun areContentsTheSame(oldItem: CarmenFeature, newItem: CarmenFeature): Boolean { - return oldItem == newItem + return oldItem.equals(newItem) } } } diff --git a/build.gradle b/build.gradle index e6b9c532b..f87cb0e5b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9d0195a4c..3e4034c77 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jan 21 18:56:58 IST 2019 +#Thu May 16 13:42:01 IST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip From 525de36168b6e6850b3b0fa7684a189caf58cda8 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Fri, 17 May 2019 09:42:10 +0200 Subject: [PATCH 22/35] chore: Bump Koin 1.0.2 to version 2.0.0-GA4 (#1770) Details: Fix imports Remove Scope --- app/build.gradle | 2 +- .../5.json | 8 ++--- .../general/di/FlavorSpecificModules.kt | 2 +- .../general/search/GeoLocationViewModel.kt | 6 +--- .../openevent/general/OpenEventGeneral.kt | 26 ++++++++------- .../fossasia/openevent/general/di/Modules.kt | 33 +++---------------- .../openevent/general/event/EventsFragment.kt | 11 ++----- .../event/topic/SimilarEventsFragment.kt | 7 ++-- .../general/favorite/FavoriteFragment.kt | 14 ++------ .../general/search/SearchResultsFragment.kt | 11 ++----- .../general/di/FlavorSpecificModules.kt | 4 +-- .../openevent/general/DependencyTest.kt | 15 ++++----- 12 files changed, 44 insertions(+), 95 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c1629a4a5..a12607890 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -96,7 +96,7 @@ repositories { dependencies { def lifecycle_version = "2.2.0-alpha01" - def koin_version = "1.0.2" + def koin_version = "2.0.0-GA4" def roomVersion = "2.1.0-beta01" def ktx_version = "1.0.0" def ktx2_version = "2.0.0" diff --git a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json index b7d65d73b..d25d70b5d 100644 --- a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json +++ b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/5.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 5, - "identityHash": "77cfd0b7528a803833b63c64c6d1db89", + "identityHash": "1c62806166a2886751e22adada684c1a", "entities": [ { "tableName": "Event", @@ -864,7 +864,7 @@ }, { "tableName": "Order", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `paymentMode` TEXT, `country` TEXT, `status` TEXT, `amount` REAL, `identifier` TEXT, `orderNotes` TEXT, `completedAt` TEXT, `city` TEXT, `address` TEXT, `createdAt` TEXT, `zipcode` TEXT, `paidVia` TEXT, `discountCodeId` TEXT, `ticketsPdfUrl` TEXT, `transactionId` TEXT, `event` INTEGER, `attendees` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`attendees`) REFERENCES `Attendee`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `paymentMode` TEXT, `country` TEXT, `status` TEXT, `amount` REAL NOT NULL, `identifier` TEXT, `orderNotes` TEXT, `completedAt` TEXT, `city` TEXT, `address` TEXT, `createdAt` TEXT, `zipcode` TEXT, `paidVia` TEXT, `discountCodeId` TEXT, `ticketsPdfUrl` TEXT, `transactionId` TEXT, `event` INTEGER, `attendees` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`attendees`) REFERENCES `Attendee`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -894,7 +894,7 @@ "fieldPath": "amount", "columnName": "amount", "affinity": "REAL", - "notNull": false + "notNull": true }, { "fieldPath": "identifier", @@ -1587,7 +1587,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"77cfd0b7528a803833b63c64c6d1db89\")" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"1c62806166a2886751e22adada684c1a\")" ] } } \ No newline at end of file diff --git a/app/src/fdroid/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt b/app/src/fdroid/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt index c460e1e3e..463dc13f8 100644 --- a/app/src/fdroid/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt +++ b/app/src/fdroid/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt @@ -1,5 +1,5 @@ package org.fossasia.openevent.general.di -import org.koin.dsl.module.module +import org.koin.dsl.module val flavorSpecificModule = module { } diff --git a/app/src/fdroid/java/org/fossasia/openevent/general/search/GeoLocationViewModel.kt b/app/src/fdroid/java/org/fossasia/openevent/general/search/GeoLocationViewModel.kt index 3481f821c..c5f128a36 100644 --- a/app/src/fdroid/java/org/fossasia/openevent/general/search/GeoLocationViewModel.kt +++ b/app/src/fdroid/java/org/fossasia/openevent/general/search/GeoLocationViewModel.kt @@ -8,17 +8,13 @@ import org.fossasia.openevent.general.common.SingleLiveEvent class GeoLocationViewModel(locationService: LocationService) : ViewModel() { private val mutableLocation = MutableLiveData() val location: LiveData = mutableLocation - private val mutableVisibility = MutableLiveData() + private val mutableVisibility = MutableLiveData(false) val currentLocationVisibility: LiveData = mutableVisibility private val mutableOpenLocationSettings = MutableLiveData() val openLocationSettings: LiveData = mutableOpenLocationSettings private val mutableErrorMessage = SingleLiveEvent() val errorMessage: LiveData = mutableErrorMessage - init { - mutableVisibility.value = false - } - fun configure() { mutableVisibility.value = false return diff --git a/app/src/main/java/org/fossasia/openevent/general/OpenEventGeneral.kt b/app/src/main/java/org/fossasia/openevent/general/OpenEventGeneral.kt index e69370807..7cf66b7c0 100644 --- a/app/src/main/java/org/fossasia/openevent/general/OpenEventGeneral.kt +++ b/app/src/main/java/org/fossasia/openevent/general/OpenEventGeneral.kt @@ -8,10 +8,11 @@ import org.fossasia.openevent.general.di.apiModule import org.fossasia.openevent.general.di.commonModule import org.fossasia.openevent.general.di.databaseModule import org.fossasia.openevent.general.di.flavorSpecificModule -import org.fossasia.openevent.general.di.fragmentsModule import org.fossasia.openevent.general.di.networkModule import org.fossasia.openevent.general.di.viewModelModule -import org.koin.android.ext.android.startKoin +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin import timber.log.Timber class OpenEventGeneral : MultiDexApplication() { @@ -25,15 +26,18 @@ class OpenEventGeneral : MultiDexApplication() { override fun onCreate() { super.onCreate() appContext = applicationContext - startKoin(this, listOf( - commonModule, - apiModule, - viewModelModule, - networkModule, - databaseModule, - flavorSpecificModule, - fragmentsModule - )) + startKoin { + androidLogger() + androidContext(this@OpenEventGeneral) + modules( + commonModule, + apiModule, + viewModelModule, + networkModule, + databaseModule, + flavorSpecificModule + ) + } Timber.plant(Timber.DebugTree()) AndroidThreeTen.init(applicationContext) diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index 7127a0d1c..2c3354756 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -35,11 +35,8 @@ import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.EventApi import org.fossasia.openevent.general.event.EventDetailsViewModel import org.fossasia.openevent.general.event.EventId -import org.fossasia.openevent.general.event.EventLayoutType import org.fossasia.openevent.general.event.EventService -import org.fossasia.openevent.general.common.EventsDiffCallback import org.fossasia.openevent.general.data.Resource -import org.fossasia.openevent.general.event.EventsListAdapter import org.fossasia.openevent.general.event.EventsViewModel import org.fossasia.openevent.general.event.feedback.Feedback import org.fossasia.openevent.general.event.feedback.FeedbackApi @@ -53,7 +50,6 @@ import org.fossasia.openevent.general.event.topic.EventTopicApi import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.event.types.EventTypesApi import org.fossasia.openevent.general.event.topic.SimilarEventsViewModel -import org.fossasia.openevent.general.favorite.FavoriteEventsRecyclerAdapter import org.fossasia.openevent.general.favorite.FavoriteEventsViewModel import org.fossasia.openevent.general.notification.Notification import org.fossasia.openevent.general.notification.NotificationApi @@ -105,8 +101,8 @@ import org.fossasia.openevent.general.ticket.TicketService import org.fossasia.openevent.general.ticket.TicketsViewModel import org.koin.android.ext.koin.androidApplication import org.koin.android.ext.koin.androidContext -import org.koin.androidx.viewmodel.ext.koin.viewModel -import org.koin.dsl.module.module +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.jackson.JacksonConverterFactory @@ -115,6 +111,8 @@ import java.util.concurrent.TimeUnit val commonModule = module { single { Preference() } single { Network() } + single { Resource() } + single { MutableConnectionLiveData() } factory { LocationServiceImpl(androidContext()) } } @@ -196,8 +194,6 @@ val apiModule = module { factory { AttendeeService(get(), get(), get()) } factory { OrderService(get(), get(), get()) } factory { SessionService(get(), get()) } - factory { Resource() } - factory { MutableConnectionLiveData() } factory { NotificationService(get()) } } @@ -349,24 +345,3 @@ val databaseModule = module { database.sponsorDao() } } - -val fragmentsModule = module { - - factory { EventsDiffCallback() } - - scope(Scopes.EVENTS_FRAGMENT.toString()) { - EventsListAdapter(EventLayoutType.EVENTS, get()) - } - - scope(Scopes.SIMILAR_EVENTS_FRAGMENT.toString()) { - EventsListAdapter(EventLayoutType.SIMILAR_EVENTS, get()) - } - - scope(Scopes.FAVORITE_FRAGMENT.toString()) { - FavoriteEventsRecyclerAdapter(get()) - } - - scope(Scopes.SEARCH_RESULTS_FRAGMENT.toString()) { - FavoriteEventsRecyclerAdapter(get()) - } -} diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt index 95eb754a0..7937da275 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt @@ -27,14 +27,11 @@ import kotlinx.android.synthetic.main.fragment_events.view.eventsNestedScrollVie import org.fossasia.openevent.general.R import org.fossasia.openevent.general.ScrollToTop import org.fossasia.openevent.general.common.EventClickListener +import org.fossasia.openevent.general.common.EventsDiffCallback import org.fossasia.openevent.general.common.FavoriteFabClickListener import org.fossasia.openevent.general.data.Preference -import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.search.SAVED_LOCATION import org.fossasia.openevent.general.utils.extensions.nonNull -import org.koin.android.ext.android.inject -import org.koin.androidx.scope.ext.android.bindScope -import org.koin.androidx.scope.ext.android.getOrCreateScope import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import org.fossasia.openevent.general.utils.Utils.setToolbar @@ -54,15 +51,11 @@ class EventsFragment : Fragment(), ScrollToTop { private val eventsViewModel by viewModel() private lateinit var rootView: View private val preference = Preference() - private val eventsListAdapter: EventsListAdapter by inject( - scope = getOrCreateScope(Scopes.EVENTS_FRAGMENT.toString()) - ) + private val eventsListAdapter = EventsListAdapter(EventLayoutType.EVENTS, EventsDiffCallback()) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - bindScope(getOrCreateScope(Scopes.EVENTS_FRAGMENT.toString())) - eventsViewModel.events .nonNull() .observe(this, Observer { list -> diff --git a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt index 9b63a6516..62e1c03fa 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt @@ -16,17 +16,15 @@ import kotlinx.android.synthetic.main.fragment_similar_events.similarEventsDivid import kotlinx.android.synthetic.main.fragment_similar_events.similarEventsRecycler import kotlinx.android.synthetic.main.fragment_similar_events.view.similarEventsRecycler import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.common.EventClickListener +import org.fossasia.openevent.general.common.EventsDiffCallback import org.fossasia.openevent.general.event.EventsListAdapter import org.fossasia.openevent.general.common.FavoriteFabClickListener import org.fossasia.openevent.general.event.EventDetailsFragmentDirections import org.fossasia.openevent.general.event.EventLayoutType import org.fossasia.openevent.general.utils.extensions.nonNull import org.koin.android.ext.android.get -import org.koin.androidx.scope.ext.android.bindScope -import org.koin.androidx.scope.ext.android.getOrCreateScope import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber @@ -44,7 +42,6 @@ class SimilarEventsFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - bindScope(getOrCreateScope(Scopes.SIMILAR_EVENTS_FRAGMENT.toString())) similarEventsViewModel.eventId = safeArgs.eventId similarEventsViewModel.loadSimilarIdEvents(safeArgs.eventTopicId) similarEventsViewModel.loadSimilarLocationEvents(safeArgs.eventLocation.toString()) @@ -92,7 +89,7 @@ class SimilarEventsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - similarEventsListAdapter = EventsListAdapter(EventLayoutType.SIMILAR_EVENTS, get()) + similarEventsListAdapter = EventsListAdapter(EventLayoutType.SIMILAR_EVENTS, EventsDiffCallback()) val eventClickListener: EventClickListener = object : EventClickListener { override fun onClick(eventID: Long) { diff --git a/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt b/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt index 81d8e4ba1..fb6f7a9f5 100644 --- a/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt @@ -19,16 +19,13 @@ import kotlinx.android.synthetic.main.fragment_favorite.view.tomorrowChip import kotlinx.android.synthetic.main.fragment_favorite.view.weekendChip import kotlinx.android.synthetic.main.fragment_favorite.view.monthChip import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.common.EventClickListener +import org.fossasia.openevent.general.common.EventsDiffCallback import org.fossasia.openevent.general.common.FavoriteFabClickListener import org.fossasia.openevent.general.data.Preference import org.fossasia.openevent.general.search.SAVED_LOCATION import org.fossasia.openevent.general.utils.extensions.nonNull -import org.koin.android.ext.android.inject -import org.koin.androidx.scope.ext.android.bindScope -import org.koin.androidx.scope.ext.android.getOrCreateScope import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import org.fossasia.openevent.general.utils.Utils.setToolbar @@ -40,14 +37,7 @@ const val FAVORITE_EVENT_DATE_FORMAT: String = "favoriteEventDateFormat" class FavoriteFragment : Fragment() { private val favoriteEventViewModel by viewModel() private lateinit var rootView: View - private val favoriteEventsRecyclerAdapter: FavoriteEventsRecyclerAdapter by inject( - scope = getOrCreateScope(Scopes.FAVORITE_FRAGMENT.toString()) - ) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - bindScope(getOrCreateScope(Scopes.FAVORITE_FRAGMENT.toString())) - } + private val favoriteEventsRecyclerAdapter = FavoriteEventsRecyclerAdapter(EventsDiffCallback()) override fun onCreateView( inflater: LayoutInflater, diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt index 52e5243d0..6e7efd660 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt @@ -29,35 +29,30 @@ import kotlinx.android.synthetic.main.fragment_search_results.view.shimmerSearch import org.fossasia.openevent.general.R import org.fossasia.openevent.general.common.EventClickListener import org.fossasia.openevent.general.common.FavoriteFabClickListener -import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.favorite.FavoriteEventsRecyclerAdapter import org.fossasia.openevent.general.utils.Utils.setToolbar import org.fossasia.openevent.general.utils.extensions.nonNull import org.jetbrains.anko.design.longSnackbar -import org.koin.android.ext.android.inject -import org.koin.androidx.scope.ext.android.bindScope -import org.koin.androidx.scope.ext.android.getOrCreateScope import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import androidx.appcompat.view.ContextThemeWrapper +import org.fossasia.openevent.general.common.EventsDiffCallback class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener { private lateinit var rootView: View private val searchViewModel by viewModel() private val safeArgs: SearchResultsFragmentArgs by navArgs() - private val favoriteEventsRecyclerAdapter: FavoriteEventsRecyclerAdapter by inject( - scope = getOrCreateScope(Scopes.SEARCH_RESULTS_FRAGMENT.toString()) - ) + private val favoriteEventsRecyclerAdapter = FavoriteEventsRecyclerAdapter(EventsDiffCallback()) + private lateinit var days: Array private lateinit var eventDate: String private lateinit var eventType: String private var eventTypesList: List? = arrayListOf() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - bindScope(getOrCreateScope(Scopes.SEARCH_RESULTS_FRAGMENT.toString())) days = resources.getStringArray(R.array.days) eventDate = searchViewModel.savedTime ?: safeArgs.date diff --git a/app/src/playStore/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt b/app/src/playStore/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt index ef77fdc8c..c26a90790 100644 --- a/app/src/playStore/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt +++ b/app/src/playStore/java/org/fossasia/openevent/general/di/FlavorSpecificModules.kt @@ -1,8 +1,8 @@ package org.fossasia.openevent.general.di import org.fossasia.openevent.general.welcome.WelcomeViewModel -import org.koin.androidx.viewmodel.ext.koin.viewModel -import org.koin.dsl.module.module +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module val flavorSpecificModule = module { viewModel { WelcomeViewModel(get(), get()) } diff --git a/app/src/test/java/org/fossasia/openevent/general/DependencyTest.kt b/app/src/test/java/org/fossasia/openevent/general/DependencyTest.kt index 1ba83b2d7..c1d2ae383 100644 --- a/app/src/test/java/org/fossasia/openevent/general/DependencyTest.kt +++ b/app/src/test/java/org/fossasia/openevent/general/DependencyTest.kt @@ -7,19 +7,18 @@ import org.fossasia.openevent.general.di.databaseModule import org.fossasia.openevent.general.di.networkModule import org.fossasia.openevent.general.di.viewModelModule import org.junit.Test -import org.koin.android.ext.koin.with -import org.koin.standalone.StandAloneContext.startKoin +import org.koin.android.ext.koin.androidContext +import org.koin.dsl.koinApplication import org.koin.test.KoinTest -import org.koin.test.dryRun +import org.koin.test.check.checkModules import org.mockito.Mockito.mock class DependencyTest : KoinTest { @Test fun testDependencies() { - // start Koin - startKoin(listOf(commonModule, apiModule, viewModelModule, - networkModule, databaseModule)) with mock(Application::class.java) - // dry run of given module list - dryRun() + koinApplication { + androidContext(mock(Application::class.java)) + modules(commonModule, apiModule, databaseModule, networkModule, viewModelModule) + }.checkModules() } } From 866a8b7f9ed3d9829c4a6394e0dd87cc6743486c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 17 May 2019 13:19:35 +0530 Subject: [PATCH 23/35] chore(deps): bump jackson-module-kotlin from 2.9.8 to 2.9.9 (#1775) Bumps [jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) from 2.9.8 to 2.9.9. - [Release notes](https://github.com/FasterXML/jackson-module-kotlin/releases) - [Commits](https://github.com/FasterXML/jackson-module-kotlin/compare/jackson-module-kotlin-2.9.8...jackson-module-kotlin-2.9.9) Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index a12607890..df09df897 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,7 +143,7 @@ dependencies { implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'com.jakewharton.threetenabp:threetenabp:1.2.0' - implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8" + implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9" implementation 'com.github.jasminb:jsonapi-converter:0.9' implementation 'com.squareup.okhttp3:logging-interceptor:3.14.1' implementation 'com.squareup.retrofit2:retrofit:2.5.0' From 9b3c9b387e14201cfab643c544fe4c9e86371e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 17 May 2019 13:20:14 +0530 Subject: [PATCH 24/35] chore(deps): bump nav_version from 2.1.0-alpha03 to 2.1.0-alpha04 (#1773) Bumps `nav_version` from 2.1.0-alpha03 to 2.1.0-alpha04. Updates `navigation-fragment-ktx` from 2.1.0-alpha03 to 2.1.0-alpha04 Updates `navigation-ui-ktx` from 2.1.0-alpha03 to 2.1.0-alpha04 Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index df09df897..85a05a788 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -100,7 +100,7 @@ dependencies { def roomVersion = "2.1.0-beta01" def ktx_version = "1.0.0" def ktx2_version = "2.0.0" - def nav_version = "2.1.0-alpha03" + def nav_version = "2.1.0-alpha04" def anko_version = "0.10.8" implementation fileTree(dir: 'libs', include: ['*.jar']) From 92d3a366d88d46fa044f1f4622d1afa3734237de Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Fri, 17 May 2019 17:31:15 +0530 Subject: [PATCH 25/35] chore: Bump spotless from 3.16.0 to 3.23.0 and fix the violations (#1777) --- app/build.gradle | 2 +- .../openevent/general/auth/EditProfileFragment.kt | 2 +- .../openevent/general/event/EventDetailsViewModel.kt | 8 ++++---- .../openevent/general/search/SearchResultsFragment.kt | 4 ++-- .../openevent/general/speakers/SpeakerViewModel.kt | 2 +- .../fossasia/openevent/general/sponsor/SponsorService.kt | 2 +- .../openevent/general/utils/extensions/RxExtensions.kt | 6 +++--- .../fossasia/openevent/general/auth/SmartAuthViewModel.kt | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 85a05a788..0979c570a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.diffplug.gradle.spotless" version "3.16.0" + id "com.diffplug.gradle.spotless" version "3.23.0" } apply plugin: 'com.android.application' diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt index c868a57de..c88a8b30d 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt @@ -179,7 +179,7 @@ class EditProfileFragment : Fragment() { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) val bytes = baos.toByteArray() - //create temp file + // create temp file try { val tempAvatar = File(context?.cacheDir, "tempAvatar") diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt index d969e96b4..fd69b32c1 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt @@ -81,8 +81,8 @@ class EventDetailsViewModel( fun fetchEventSpeakers(id: Long) { speakerService.fetchSpeakersForEvent(id) .withDefaultSchedulers() - .subscribe ({ - //Do Nothing + .subscribe({ + // Do Nothing }, { Timber.e(it, "Error fetching speaker for event id %d", id) mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, @@ -98,8 +98,8 @@ class EventDetailsViewModel( fun fetchEventSponsors(id: Long) { sponsorService.fetchSponsorsWithEvent(id) .withDefaultSchedulers() - .subscribe ({ - //Do Nothing + .subscribe({ + // Do Nothing }, { Timber.e(it, "Error fetching sponsor for event id %d", id) mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt index 6e7efd660..345b7391c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt @@ -69,7 +69,7 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { rootView = inflater.inflate(R.layout.fragment_search_results, container, false) - setChips( eventDate, eventType) + setChips(eventDate, eventType) setToolbar(activity, getString(R.string.search_results)) setHasOptionsMenu(true) @@ -134,7 +134,7 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener } private fun setChips(date: String = eventDate, type: String = eventType) { - if (rootView.chipGroup.childCount>0) { + if (rootView.chipGroup.childCount> 0) { rootView.chipGroup.removeAllViews() } when { diff --git a/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerViewModel.kt index c644d21c6..2b8aac088 100644 --- a/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerViewModel.kt @@ -31,7 +31,7 @@ class SpeakerViewModel( } compositeDisposable += speakerService.fetchSpeaker(id) .withDefaultSchedulers() - .subscribe ({ + .subscribe({ mutableSpeaker.value = it }, { Timber.e(it, "Error fetching speaker for id %d", id) diff --git a/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorService.kt b/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorService.kt index c47e48cab..b5df7c86b 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorService.kt @@ -3,7 +3,7 @@ package org.fossasia.openevent.general.sponsor import androidx.lifecycle.LiveData import io.reactivex.Single -class SponsorService ( +class SponsorService( private val sponsorApi: SponsorApi, private val sponsorDao: SponsorDao, private val sponsorWithEventDao: SponsorWithEventDao diff --git a/app/src/main/java/org/fossasia/openevent/general/utils/extensions/RxExtensions.kt b/app/src/main/java/org/fossasia/openevent/general/utils/extensions/RxExtensions.kt index 53d3d1c8f..cff6378ca 100644 --- a/app/src/main/java/org/fossasia/openevent/general/utils/extensions/RxExtensions.kt +++ b/app/src/main/java/org/fossasia/openevent/general/utils/extensions/RxExtensions.kt @@ -6,11 +6,11 @@ import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -fun Single .withDefaultSchedulers(): +fun Single.withDefaultSchedulers(): Single = subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) -fun Flowable .withDefaultSchedulers(): +fun Flowable.withDefaultSchedulers(): Flowable = subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) -fun Completable .withDefaultSchedulers(): +fun Completable.withDefaultSchedulers(): Completable = subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt b/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt index e7ddce7f0..16b3fe02f 100644 --- a/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt +++ b/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt @@ -52,7 +52,7 @@ class SmartAuthViewModel : ViewModel() { }) } - fun saveCredential (id: String, password: String, credentialsClient: CredentialsClient) { + fun saveCredential(id: String, password: String, credentialsClient: CredentialsClient) { val credential = Credential.Builder(id).setPassword(password).build() credentialsClient.save(credential).addOnCompleteListener( OnCompleteListener { task -> From cb3ee5a7337a26bb405ba14286b159cb04f0306f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 17 May 2019 17:31:39 +0530 Subject: [PATCH 26/35] chore(deps): bump koin_version from 1.0.2 to 2.0.0-GA5 (#1774) Bumps `koin_version` from 1.0.2 to 2.0.0-GA5. Updates `koin-android` from 1.0.2 to 2.0.0-GA5 Updates `koin-androidx-scope` from 1.0.2 to 2.0.0-GA5 Updates `koin-androidx-viewmodel` from 1.0.2 to 2.0.0-GA5 Updates `koin-test` from 1.0.2 to 2.0.0-GA5 Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 0979c570a..6f4d27ac2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -96,7 +96,7 @@ repositories { dependencies { def lifecycle_version = "2.2.0-alpha01" - def koin_version = "2.0.0-GA4" + def koin_version = "2.0.0-GA5" def roomVersion = "2.1.0-beta01" def ktx_version = "1.0.0" def ktx2_version = "2.0.0" From d6b7ead485662bfb503a848e8d0441ce3ec51849 Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Fri, 17 May 2019 17:31:56 +0530 Subject: [PATCH 27/35] fix: Show only upcoming or ongoing events (#1753) --- .../openevent/general/event/EventService.kt | 5 +- .../openevent/general/event/EventUtils.kt | 8 +++ .../general/search/SearchViewModel.kt | 52 ++++++++++++++++--- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt index e640ea547..dfe85f8a8 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt @@ -14,7 +14,7 @@ import org.fossasia.openevent.general.event.topic.EventTopicApi import org.fossasia.openevent.general.event.topic.EventTopicsDao import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.event.types.EventTypesApi -import java.util.Locale.filter +import java.util.Date class EventService( private val eventApi: EventApi, @@ -88,7 +88,8 @@ class EventService( } fun getEventsByLocation(locationName: String?): Single> { - val query = "[{\"name\":\"location-name\",\"op\":\"ilike\",\"val\":\"%$locationName%\"}]" + val query = "[{\"name\":\"location-name\",\"op\":\"ilike\",\"val\":\"%$locationName%\"}," + + "{\"name\":\"ends-at\",\"op\":\"ge\",\"val\":\"%${EventUtils.getTimeInISO8601(Date())}%\"}]" return eventApi.searchEvents("name", query).flatMap { apiList -> val eventIds = apiList.map { it.id }.toList() eventTopicsDao.insertEventTopics(getEventTopicList(apiList)) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt index e2f51d1a9..ac53d675a 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventUtils.kt @@ -22,6 +22,7 @@ import java.io.FileOutputStream import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import java.util.TimeZone object EventUtils { @@ -269,4 +270,11 @@ object EventUtils { bmpUri } } + + fun getTimeInISO8601(date: Date): String { + val tz = TimeZone.getTimeZone(TimeZone.getDefault().id) + val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm", Locale.getDefault()) + df.timeZone = tz + return df.format(date) + } } diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt index e092bdeab..36aba5fe6 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchViewModel.kt @@ -14,6 +14,7 @@ import org.fossasia.openevent.general.data.Preference import org.fossasia.openevent.general.data.Resource import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.EventService +import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.utils.DateTimeUtils.getNextDate import org.fossasia.openevent.general.utils.DateTimeUtils.getNextMonth @@ -22,6 +23,7 @@ import org.fossasia.openevent.general.utils.DateTimeUtils.getNextToNextMonth import org.fossasia.openevent.general.utils.DateTimeUtils.getNextToWeekendDate import org.fossasia.openevent.general.utils.DateTimeUtils.getWeekendDate import timber.log.Timber +import java.util.Date class SearchViewModel( private val eventService: EventService, @@ -97,7 +99,11 @@ class SearchViewModel( | 'op':'eq', | 'val':'0' | } - |} + |}, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | } """.trimIndent() else "" val query: String = when { @@ -105,7 +111,11 @@ class SearchViewModel( | 'name':'name', | 'op':'ilike', | 'val':'%$searchEvent%' - |}]""".trimMargin().replace("'", "'") + |}, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | }]""".trimMargin().replace("'", "'") time == "Anytime" && type == "Anything" -> """[{ | 'and':[{ | 'name':'location-name', @@ -115,6 +125,10 @@ class SearchViewModel( | 'name':'name', | 'op':'ilike', | 'val':'%$searchEvent%' + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") time == "Anytime" -> """[{ @@ -134,6 +148,10 @@ class SearchViewModel( | 'op':'eq', | 'val':'$type' | } + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") time == "Today" -> """[{ @@ -161,7 +179,11 @@ class SearchViewModel( | 'op':'eq', | 'val':'$type' | } - | }$freeStuffFilter] + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") time == "Tomorrow" -> """[{ | 'and':[{ @@ -188,7 +210,11 @@ class SearchViewModel( | 'op':'eq', | 'val':'$type' | } - | }$freeStuffFilter] + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") time == "This weekend" -> """[{ | 'and':[{ @@ -215,7 +241,11 @@ class SearchViewModel( | 'op':'eq', | 'val':'$type' | } - | }$freeStuffFilter] + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") time == "In the next month" -> """[{ | 'and':[{ @@ -242,7 +272,11 @@ class SearchViewModel( | 'op':'eq', | 'val':'$type' | } - | }$freeStuffFilter] + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") else -> """[{ @@ -270,7 +304,11 @@ class SearchViewModel( | 'op':'eq', | 'val':'$type' | } - | }$freeStuffFilter] + | }, { + | 'name':'ends-at', + | 'op':'ge', + | 'val':'%${EventUtils.getTimeInISO8601(Date())}%' + | }$freeStuffFilter] |}]""".trimMargin().replace("'", "\"") } compositeDisposable += eventService.getSearchEvents(query, sortBy) From c2a7f11eb4dfb1c0edfb47d267c14d524fadd516 Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Fri, 17 May 2019 19:23:58 +0200 Subject: [PATCH 28/35] fix: Refractor EventDetailsFragment (#1740) Detail: - Remove nested fragment inside EventDetailsFragment (similar event section and social link section) - Set up connection live to dynamicly fetching event details whenever the internet connection is on. - Stop fetching sponsors, speakers, sessions,... again on screen rotation - Hide sections without information inside EventDetailsFragment Fixes: #1689 --- .../fossasia/openevent/general/di/Modules.kt | 6 +- .../general/event/EventDetailsFragment.kt | 230 +++++++++++------- .../general/event/EventDetailsViewModel.kt | 86 ++++++- .../openevent/general/event/EventService.kt | 2 +- .../event/topic/SimilarEventsFragment.kt | 158 ------------ .../event/topic/SimilarEventsViewModel.kt | 80 ------ .../general/social/SocialLinksFragment.kt | 107 -------- .../social/SocialLinksRecyclerAdapter.kt | 2 +- .../general/social/SocialLinksViewHolder.kt | 22 +- .../general/social/SocialLinksViewModel.kt | 60 ----- .../general/sponsor/SponsorRecyclerAdapter.kt | 1 + app/src/main/res/layout/content_event.xml | 64 ++++- .../res/layout/fragment_similar_events.xml | 58 ----- .../main/res/layout/fragment_social_links.xml | 59 ----- .../main/res/navigation/navigation_graph.xml | 22 -- app/src/main/res/values-vi/strings.xml | 2 - app/src/main/res/values/strings.xml | 4 +- 17 files changed, 290 insertions(+), 673 deletions(-) delete mode 100644 app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt delete mode 100644 app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsViewModel.kt delete mode 100644 app/src/main/java/org/fossasia/openevent/general/social/SocialLinksFragment.kt delete mode 100644 app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewModel.kt delete mode 100644 app/src/main/res/layout/fragment_similar_events.xml delete mode 100644 app/src/main/res/layout/fragment_social_links.xml diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index 2c3354756..eefd48820 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -49,7 +49,6 @@ import org.fossasia.openevent.general.event.topic.EventTopic import org.fossasia.openevent.general.event.topic.EventTopicApi import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.event.types.EventTypesApi -import org.fossasia.openevent.general.event.topic.SimilarEventsViewModel import org.fossasia.openevent.general.favorite.FavoriteEventsViewModel import org.fossasia.openevent.general.notification.Notification import org.fossasia.openevent.general.notification.NotificationApi @@ -85,7 +84,6 @@ import org.fossasia.openevent.general.settings.SettingsViewModel import org.fossasia.openevent.general.social.SocialLink import org.fossasia.openevent.general.social.SocialLinkApi import org.fossasia.openevent.general.social.SocialLinksService -import org.fossasia.openevent.general.social.SocialLinksViewModel import org.fossasia.openevent.general.speakers.Speaker import org.fossasia.openevent.general.speakers.SpeakerApi import org.fossasia.openevent.general.speakers.SpeakerService @@ -202,7 +200,7 @@ val viewModelModule = module { viewModel { EventsViewModel(get(), get(), get(), get()) } viewModel { ProfileViewModel(get(), get()) } viewModel { SignUpViewModel(get(), get(), get()) } - viewModel { EventDetailsViewModel(get(), get(), get(), get(), get(), get()) } + viewModel { EventDetailsViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { SessionViewModel(get(), get()) } viewModel { SearchViewModel(get(), get(), get(), get()) } viewModel { AttendeeViewModel(get(), get(), get(), get(), get(), get(), get()) } @@ -212,10 +210,8 @@ val viewModelModule = module { viewModel { TicketsViewModel(get(), get(), get(), get(), get()) } viewModel { AboutEventViewModel(get(), get()) } viewModel { EventFAQViewModel(get(), get()) } - viewModel { SocialLinksViewModel(get(), get(), get()) } viewModel { FavoriteEventsViewModel(get(), get()) } viewModel { SettingsViewModel(get()) } - viewModel { SimilarEventsViewModel(get(), get()) } viewModel { OrderCompletedViewModel(get(), get()) } viewModel { OrdersUnderUserViewModel(get(), get(), get(), get()) } viewModel { OrderDetailsViewModel(get(), get(), get()) } diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt index a63743b95..e70864461 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt @@ -23,7 +23,6 @@ import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.squareup.picasso.Picasso -import kotlinx.android.synthetic.main.content_event.similarEventsContainer import kotlinx.android.synthetic.main.content_event.view.eventDateDetailsFirst import kotlinx.android.synthetic.main.content_event.view.eventDateDetailsSecond import kotlinx.android.synthetic.main.content_event.view.eventDescription @@ -45,6 +44,10 @@ import kotlinx.android.synthetic.main.content_event.view.speakerRv import kotlinx.android.synthetic.main.content_event.view.speakersContainer import kotlinx.android.synthetic.main.content_event.view.sponsorsRecyclerView import kotlinx.android.synthetic.main.content_event.view.sponsorsSummaryContainer +import kotlinx.android.synthetic.main.content_event.view.socialLinksRecycler +import kotlinx.android.synthetic.main.content_event.view.socialLinkContainer +import kotlinx.android.synthetic.main.content_event.view.similarEventsRecycler +import kotlinx.android.synthetic.main.content_event.view.similarEventsContainer import kotlinx.android.synthetic.main.fragment_event.view.buttonTickets import kotlinx.android.synthetic.main.fragment_event.view.eventErrorCard import kotlinx.android.synthetic.main.fragment_event.view.container @@ -52,16 +55,17 @@ import kotlinx.android.synthetic.main.content_fetching_event_error.view.retry import kotlinx.android.synthetic.main.dialog_feedback.view.feedback import kotlinx.android.synthetic.main.dialog_feedback.view.feedbackTextInputLayout import kotlinx.android.synthetic.main.dialog_feedback.view.feedbackrating -import kotlinx.android.synthetic.main.fragment_event.eventCoordinatorLayout import org.fossasia.openevent.general.R import org.fossasia.openevent.general.common.SessionClickListener import org.fossasia.openevent.general.common.SpeakerClickListener +import org.fossasia.openevent.general.common.EventClickListener +import org.fossasia.openevent.general.common.FavoriteFabClickListener +import org.fossasia.openevent.general.common.EventsDiffCallback import org.fossasia.openevent.general.databinding.FragmentEventBinding import org.fossasia.openevent.general.event.EventUtils.loadMapUrl import org.fossasia.openevent.general.event.feedback.FeedbackRecyclerAdapter -import org.fossasia.openevent.general.event.topic.SimilarEventsFragment import org.fossasia.openevent.general.sessions.SessionRecyclerAdapter -import org.fossasia.openevent.general.social.SocialLinksFragment +import org.fossasia.openevent.general.social.SocialLinksRecyclerAdapter import org.fossasia.openevent.general.speakers.SpeakerRecyclerAdapter import org.fossasia.openevent.general.sponsor.SponsorClickListener import org.fossasia.openevent.general.sponsor.SponsorRecyclerAdapter @@ -76,10 +80,6 @@ import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.longSnackbar import org.jetbrains.anko.design.snackbar -const val EVENT_ID = "eventId" -const val EVENT_TOPIC_ID = "eventTopicId" -const val EVENT_LOCATION = "eventLocation" - class EventDetailsFragment : Fragment() { private val eventViewModel by viewModel() private val safeArgs: EventDetailsFragmentArgs by navArgs() @@ -87,18 +87,14 @@ class EventDetailsFragment : Fragment() { private val speakersAdapter = SpeakerRecyclerAdapter() private val sponsorsAdapter = SponsorRecyclerAdapter() private val sessionsAdapter = SessionRecyclerAdapter() + private val socialLinkAdapter = SocialLinksRecyclerAdapter() + private val similarEventsAdapter = EventsListAdapter(EventLayoutType.SIMILAR_EVENTS, EventsDiffCallback()) private lateinit var rootView: View private lateinit var binding: FragmentEventBinding - private var eventTopicId: Long? = null - private var eventLocation: String? = null - private lateinit var eventShare: Event - private var currency: String? = null private val LINE_COUNT: Int = 3 private val LINE_COUNT_ORGANIZER: Int = 2 private var menuActionBar: Menu? = null - private var title: String = "" - private var runOnce: Boolean = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -106,32 +102,19 @@ class EventDetailsFragment : Fragment() { .nonNull() .observe(this, Observer { loadEvent(it) - eventShare = it - title = eventShare.name + if (eventViewModel.similarEvents.value == null) { + val eventTopicId = it.eventTopic?.id ?: 0 + val eventLocation = it.searchableLocationName ?: it.locationName + eventViewModel.fetchSimilarEvents(safeArgs.eventId, eventTopicId, eventLocation) + } // Update favorite icon and external event url menu option activity?.invalidateOptionsMenu() - if (runOnce) { - loadSocialLinksFragment() - loadSimilarEventsFragment() - } - runOnce = false - Timber.d("Fetched events of id ${safeArgs.eventId}") showEventErrorScreen(false) setHasOptionsMenu(true) }) - eventViewModel.loadEventFeedback(safeArgs.eventId) - eventViewModel.fetchEventSpeakers(safeArgs.eventId) - val allSessions = eventViewModel.eventSessions.value - if (allSessions == null) { - eventViewModel.fetchEventSessions(safeArgs.eventId) - } else { - sessionsAdapter.addAll(allSessions) - rootView.sessionContainer.isVisible = allSessions.isNotEmpty() - } - eventViewModel.fetchEventSponsors(safeArgs.eventId) } override fun onCreateView( @@ -181,7 +164,16 @@ class EventDetailsFragment : Fragment() { checkForAuthentication() } - eventViewModel.loadEvent(safeArgs.eventId) + val socialLinkLinearLayoutManager = LinearLayoutManager(context) + socialLinkLinearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL + rootView.socialLinksRecycler.layoutManager = socialLinkLinearLayoutManager + rootView.socialLinksRecycler.adapter = socialLinkAdapter + + eventViewModel.socialLinks.observe(viewLifecycleOwner, Observer { + socialLinkAdapter.addAll(it) + rootView.socialLinkContainer.visibility = if (it.isEmpty()) View.GONE else View.VISIBLE + }) + rootView.retry.setOnClickListener { eventViewModel.loadEvent(safeArgs.eventId) } @@ -226,7 +218,7 @@ class EventDetailsFragment : Fragment() { if (scrollY > rootView.eventName.height + rootView.eventImage.height) /*Toolbar title set to name of Event if scrolled more than combined height of eventImage and eventName views*/ - setToolbar(activity, title) + setToolbar(activity, eventViewModel.event.value?.name ?: "") else // Toolbar title set to an empty string setToolbar(activity) @@ -238,13 +230,9 @@ class EventDetailsFragment : Fragment() { rootView.speakerRv.layoutManager = linearLayoutManagerSpeakers rootView.speakerRv.adapter = speakersAdapter - eventViewModel.loadEventSpeakers(safeArgs.eventId).observe(viewLifecycleOwner, Observer { + eventViewModel.eventSpeakers.observe(viewLifecycleOwner, Observer { speakersAdapter.addAll(it) - if (it.isEmpty()) { - rootView.speakersContainer.visibility = View.GONE - } else { - rootView.speakersContainer.visibility = View.VISIBLE - } + rootView.speakersContainer.visibility = if (it.isEmpty()) View.GONE else View.VISIBLE }) val sessionClickListener: SessionClickListener = object : SessionClickListener { @@ -272,10 +260,19 @@ class EventDetailsFragment : Fragment() { } }) - eventViewModel.loadEventSponsors(safeArgs.eventId).observe(viewLifecycleOwner, Observer { sponsors -> + eventViewModel.eventSponsors.observe(viewLifecycleOwner, Observer { sponsors -> sponsorsAdapter.addAll(sponsors) rootView.sponsorsSummaryContainer.visibility = if (sponsors.isEmpty()) View.GONE else View.VISIBLE - sponsorsAdapter.notifyDataSetChanged() + }) + + val similarLinearLayoutManager = LinearLayoutManager(context) + similarLinearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL + rootView.similarEventsRecycler.layoutManager = similarLinearLayoutManager + rootView.similarEventsRecycler.adapter = similarEventsAdapter + + eventViewModel.similarEvents.observe(viewLifecycleOwner, Observer { similarEvents -> + similarEventsAdapter.submitList(similarEvents) + rootView.similarEventsContainer.visibility = if (similarEvents.isNotEmpty()) View.VISIBLE else View.GONE }) return rootView @@ -309,7 +306,6 @@ class EventDetailsFragment : Fragment() { } } } - currency = Currency.getInstance(event.paymentCurrency ?: "USD").symbol // About event on-click val aboutEventOnClickListener = View.OnClickListener { findNavController(rootView).navigate(EventDetailsFragmentDirections @@ -349,21 +345,103 @@ class EventDetailsFragment : Fragment() { rootView.eventDateDetailsFirst.text = EventUtils.getFormattedEventDateTimeRange(startsAt, endsAt) rootView.eventDateDetailsSecond.text = EventUtils.getFormattedEventDateTimeRangeSecond(startsAt, endsAt) - // Similar Events Section - if (event.eventTopic != null || !event.locationName.isNullOrBlank() || - !event.searchableLocationName.isNullOrBlank()) { - similarEventsContainer.isVisible = true - eventTopicId = event.eventTopic?.id ?: 0 - eventLocation = - if (event.searchableLocationName.isNullOrBlank()) event.locationName - else event.searchableLocationName - } - // Add event to Calendar val dateClickListener = View.OnClickListener { startCalendar(event) } rootView.eventTimingLinearLayout.setOnClickListener(dateClickListener) } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + eventViewModel.connection + .nonNull() + .observe(this, Observer { isConnected -> + if (isConnected) { + showEventErrorScreen(false) + val currentEvent = eventViewModel.event.value + if (currentEvent == null) + eventViewModel.loadEvent(safeArgs.eventId) + else + loadEvent(currentEvent) + + val currentFeedback = eventViewModel.eventFeedback.value + if (currentFeedback == null) { + eventViewModel.fetchEventFeedback(safeArgs.eventId) + } else { + feedbackAdapter.addAll(currentFeedback) + if (currentFeedback.isEmpty()) { + rootView.feedbackRv.isVisible = false + rootView.noFeedBackTv.isVisible = true + } else { + rootView.feedbackRv.isVisible = true + rootView.noFeedBackTv.isVisible = false + } + } + + val currentSpeakers = eventViewModel.eventSpeakers.value + if (currentSpeakers == null) { + eventViewModel.fetchEventSpeakers(safeArgs.eventId) + } else { + speakersAdapter.addAll(currentSpeakers) + rootView.speakersContainer.visibility = + if (currentSpeakers.isEmpty()) View.GONE else View.VISIBLE + } + + val currentSessions = eventViewModel.eventSessions.value + if (currentSessions == null) { + eventViewModel.fetchEventSessions(safeArgs.eventId) + } else { + sessionsAdapter.addAll(currentSessions) + rootView.sessionContainer.visibility = + if (currentSessions.isEmpty()) View.GONE else View.VISIBLE + } + + val currentSponsors = eventViewModel.eventSponsors.value + if (currentSponsors == null) { + eventViewModel.fetchEventSponsors(safeArgs.eventId) + } else { + sponsorsAdapter.addAll(currentSponsors) + rootView.sponsorsSummaryContainer.visibility = + if (currentSponsors.isEmpty()) View.GONE else View.VISIBLE + } + + val currentSocialLinks = eventViewModel.socialLinks.value + if (currentSocialLinks == null) { + eventViewModel.fetchSocialLink(safeArgs.eventId) + } else { + socialLinkAdapter.addAll(currentSocialLinks) + rootView.socialLinkContainer.visibility = + if (currentSocialLinks.isEmpty()) View.GONE else View.VISIBLE + } + } else { + val currentEvent = eventViewModel.event.value + if (currentEvent == null) + showEventErrorScreen(true) + else + loadEvent(currentEvent) + } + }) + + val eventClickListener: EventClickListener = object : EventClickListener { + override fun onClick(eventID: Long) { + findNavController(view) + .navigate(EventDetailsFragmentDirections.actionSimilarEventsToEventDetails(eventID)) + } + } + + val favFabClickListener: FavoriteFabClickListener = object : FavoriteFabClickListener { + override fun onClick(event: Event, itemPosition: Int) { + eventViewModel.setFavorite(event.id, !event.favorite) + event.favorite = !event.favorite + similarEventsAdapter.notifyItemChanged(itemPosition) + } + } + + similarEventsAdapter.apply { + onEventClick = eventClickListener + onFavFabClick = favFabClickListener + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { @@ -372,23 +450,23 @@ class EventDetailsFragment : Fragment() { } R.id.add_to_calendar -> { // Add event to Calendar - startCalendar(eventShare) + eventViewModel.event.value?.let { startCalendar(it) } true } R.id.report_event -> { - reportEvent(eventShare) + eventViewModel.event.value?.let { reportEvent(it) } true } R.id.open_external_event_url -> { - eventShare.externalEventUrl?.let { Utils.openUrl(requireContext(), it) } + eventViewModel.event.value?.externalEventUrl?.let { Utils.openUrl(requireContext(), it) } true } R.id.favorite_event -> { - eventViewModel.setFavorite(safeArgs.eventId, !(eventShare.favorite)) + eventViewModel.event.value?.let { eventViewModel.setFavorite(safeArgs.eventId, !it.favorite) } true } R.id.event_share -> { - EventUtils.share(eventShare, rootView.eventImage) + eventViewModel.event.value?.let { EventUtils.share(it, rootView.eventImage) } return true } R.id.open_faqs -> { @@ -431,48 +509,20 @@ class EventDetailsFragment : Fragment() { } override fun onPrepareOptionsMenu(menu: Menu) { - if (::eventShare.isInitialized) { - if (eventShare.externalEventUrl == null) { + eventViewModel.event.value?.let { currentEvent -> + if (currentEvent.externalEventUrl == null) menu.findItem(R.id.open_external_event_url).isVisible = false - } - setFavoriteIconFilled(eventShare.favorite) + setFavoriteIconFilled(currentEvent.favorite) } super.onPrepareOptionsMenu(menu) } private fun loadTicketFragment() { + val currency = Currency.getInstance(eventViewModel.event.value?.paymentCurrency ?: "USD").symbol findNavController(rootView).navigate(EventDetailsFragmentDirections .actionEventDetailsToTickets(safeArgs.eventId, currency)) } - private fun loadSocialLinksFragment() { - // Initialise SocialLinks Fragment - val socialLinksFragemnt = SocialLinksFragment() - val bundle = Bundle() - bundle.putLong(EVENT_ID, safeArgs.eventId) - socialLinksFragemnt.arguments = bundle - socialLinksFragemnt.setErrorSnack { - eventCoordinatorLayout.longSnackbar(it) - } - val transaction = childFragmentManager.beginTransaction() - transaction.add(R.id.frameContainerSocial, socialLinksFragemnt).commit() - } - - private fun loadSimilarEventsFragment() { - // Initialise SimilarEvents Fragment - val similarEventsFragment = SimilarEventsFragment() - val bundle = Bundle() - bundle.putLong(EVENT_ID, safeArgs.eventId) - eventTopicId?.let { bundle.putLong(EVENT_TOPIC_ID, it) } - eventLocation?.let { bundle.putString(EVENT_LOCATION, it) } - similarEventsFragment.arguments = bundle - similarEventsFragment.setErrorSnack { - eventCoordinatorLayout.longSnackbar(it) - } - childFragmentManager.beginTransaction() - .replace(R.id.frameContainerSimilarEvents, similarEventsFragment).commit() - } - private fun startMap(event: Event) { // start map intent val mapUrl = loadMapUrl(event) diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt index fd69b32c1..9ce8dbdc6 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsViewModel.kt @@ -12,10 +12,13 @@ import org.fossasia.openevent.general.auth.AuthHolder import org.fossasia.openevent.general.auth.User import org.fossasia.openevent.general.auth.UserId import org.fossasia.openevent.general.common.SingleLiveEvent +import org.fossasia.openevent.general.connectivity.MutableConnectionLiveData import org.fossasia.openevent.general.data.Resource import org.fossasia.openevent.general.event.feedback.Feedback import org.fossasia.openevent.general.sessions.Session import org.fossasia.openevent.general.sessions.SessionService +import org.fossasia.openevent.general.social.SocialLinksService +import org.fossasia.openevent.general.social.SocialLink import org.fossasia.openevent.general.speakers.Speaker import org.fossasia.openevent.general.speakers.SpeakerService import org.fossasia.openevent.general.sponsor.Sponsor @@ -29,11 +32,14 @@ class EventDetailsViewModel( private val speakerService: SpeakerService, private val sponsorService: SponsorService, private val sessionService: SessionService, - private val resource: Resource + private val socialLinksService: SocialLinksService, + private val resource: Resource, + private val mutableConnectionLiveData: MutableConnectionLiveData ) : ViewModel() { private val compositeDisposable = CompositeDisposable() + val connection: LiveData = mutableConnectionLiveData private val mutableProgress = MutableLiveData() val progress: LiveData = mutableProgress private val mutableUser = MutableLiveData() @@ -48,13 +54,22 @@ class EventDetailsViewModel( val submittedFeedback: LiveData = mutableSubmittedFeedback private val mutableEventSessions = MutableLiveData>() val eventSessions: LiveData> = mutableEventSessions - private var eventSpeakers: LiveData> = MutableLiveData() + private val mutableEventSpeakers = MutableLiveData>() + val eventSpeakers: LiveData> = mutableEventSpeakers + private val mutableEventSponsors = MutableLiveData>() + val eventSponsors: LiveData> = mutableEventSponsors + private val mutableSocialLinks = MutableLiveData>() + val socialLinks: LiveData> = mutableSocialLinks + private val mutableSimilarEvents = MutableLiveData>() + val similarEvents: LiveData> = mutableSimilarEvents fun isLoggedIn() = authHolder.isLoggedIn() fun getId() = authHolder.getId() - fun loadEventFeedback(id: Long) { + fun fetchEventFeedback(id: Long) { + if (id == -1L) return + compositeDisposable += eventService.getEventFeedback(id) .withDefaultSchedulers() .subscribe({ @@ -79,10 +94,12 @@ class EventDetailsViewModel( }) } fun fetchEventSpeakers(id: Long) { - speakerService.fetchSpeakersForEvent(id) + if (id == -1L) return + + compositeDisposable += speakerService.fetchSpeakersForEvent(id) .withDefaultSchedulers() .subscribe({ - // Do Nothing + mutableEventSpeakers.value = it }, { Timber.e(it, "Error fetching speaker for event id %d", id) mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, @@ -90,16 +107,61 @@ class EventDetailsViewModel( }) } - fun loadEventSpeakers(id: Long): LiveData> { - eventSpeakers = speakerService.fetchSpeakersFromDb(id) - return eventSpeakers + fun fetchSocialLink(id: Long) { + if (id == -1L) return + + compositeDisposable += socialLinksService.getSocialLinks(id) + .withDefaultSchedulers() + .subscribe({ + mutableSocialLinks.value = it + }, { + Timber.e(it, "Error fetching social link for event id $id") + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.social_links)) + }) + } + + fun fetchSimilarEvents(eventId: Long, topicId: Long, location: String?) { + if (eventId == -1L) return + + if (topicId != -1L) { + compositeDisposable += eventService.getSimilarEvents(topicId) + .withDefaultSchedulers() + .subscribe({ + val similarEventList = mutableListOf() + mutableSimilarEvents.value?.let { currentEvents -> similarEventList.addAll(currentEvents) } + val list = it.filter { it.id != eventId } + similarEventList.addAll(list) + mutableSimilarEvents.value = similarEventList + }, { + Timber.e(it, "Error fetching similar events") + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.similar_events)) + }) + } + + compositeDisposable += eventService.getEventsByLocation(location) + .withDefaultSchedulers() + .subscribe({ + val similarEventList = mutableListOf() + mutableSimilarEvents.value?.let { currentEvents -> similarEventList.addAll(currentEvents) } + val list = it.filter { it.id != eventId } + similarEventList.addAll(list) + mutableSimilarEvents.value = similarEventList + }, { + Timber.e(it, "Error fetching similar events") + mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.similar_events)) + }) } fun fetchEventSponsors(id: Long) { - sponsorService.fetchSponsorsWithEvent(id) + if (id == -1L) return + + compositeDisposable += sponsorService.fetchSponsorsWithEvent(id) .withDefaultSchedulers() .subscribe({ - // Do Nothing + mutableEventSponsors.value = it }, { Timber.e(it, "Error fetching sponsor for event id %d", id) mutablePopMessage.value = resource.getString(R.string.error_fetching_event_section_message, @@ -107,8 +169,6 @@ class EventDetailsViewModel( }) } - fun loadEventSponsors(id: Long): LiveData> = sponsorService.fetchSponsorsFromDb(id) - fun loadEvent(id: Long) { if (id == -1L) { mutablePopMessage.value = resource.getString(R.string.error_fetching_event_message) @@ -129,6 +189,8 @@ class EventDetailsViewModel( } fun fetchEventSessions(id: Long) { + if (id == -1L) return + compositeDisposable += sessionService.fetchSessionForEvent(id) .withDefaultSchedulers() .subscribe({ diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt index dfe85f8a8..cbf658a09 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt @@ -76,7 +76,7 @@ class EventService( } fun getSearchEvents(eventName: String, sortBy: String): Single> { return eventApi.searchEvents(sortBy, eventName).flatMap { apiList -> - var eventIds = apiList.map { it.id }.toList() + val eventIds = apiList.map { it.id }.toList() eventDao.getFavoriteEventWithinIds(eventIds).flatMap { favIds -> updateFavorites(apiList, favIds) } diff --git a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt deleted file mode 100644 index 62e1c03fa..000000000 --- a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt +++ /dev/null @@ -1,158 +0,0 @@ -package org.fossasia.openevent.general.event.topic - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.navigation.Navigation.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.navigation.fragment.navArgs -import kotlinx.android.synthetic.main.fragment_similar_events.moreLikeThis -import kotlinx.android.synthetic.main.fragment_similar_events.progressBar -import kotlinx.android.synthetic.main.fragment_similar_events.similarEventsDivider -import kotlinx.android.synthetic.main.fragment_similar_events.similarEventsRecycler -import kotlinx.android.synthetic.main.fragment_similar_events.view.similarEventsRecycler -import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.event.Event -import org.fossasia.openevent.general.common.EventClickListener -import org.fossasia.openevent.general.common.EventsDiffCallback -import org.fossasia.openevent.general.event.EventsListAdapter -import org.fossasia.openevent.general.common.FavoriteFabClickListener -import org.fossasia.openevent.general.event.EventDetailsFragmentDirections -import org.fossasia.openevent.general.event.EventLayoutType -import org.fossasia.openevent.general.utils.extensions.nonNull -import org.koin.android.ext.android.get -import org.koin.androidx.viewmodel.ext.android.viewModel -import timber.log.Timber - -class SimilarEventsFragment : Fragment() { - - private val similarEventsViewModel by viewModel() - private val safeArgs: SimilarEventsFragmentArgs by navArgs() - private lateinit var rootView: View - private lateinit var linearLayoutManager: LinearLayoutManager - private lateinit var similarEventsListAdapter: EventsListAdapter - private var similarIdEvents: MutableList = mutableListOf() - private var similarLocationEvents: MutableList = mutableListOf() - private var similarEvents: MutableList = mutableListOf() - private var showErrorSnack: ((String) -> Unit)? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - similarEventsViewModel.eventId = safeArgs.eventId - similarEventsViewModel.loadSimilarIdEvents(safeArgs.eventTopicId) - similarEventsViewModel.loadSimilarLocationEvents(safeArgs.eventLocation.toString()) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - rootView = inflater.inflate(R.layout.fragment_similar_events, container, false) - similarEventsViewModel.similarLocationEvents - .nonNull() - .observe(viewLifecycleOwner, Observer { - similarLocationEvents.clear() - similarLocationEvents = it.toMutableList() - Timber.d("Fetched similar location events of size %s", it.size) - setUpAdapter() - }) - - similarEventsViewModel.similarIdEvents - .nonNull() - .observe(viewLifecycleOwner, Observer { - similarIdEvents.clear() - similarIdEvents = it.toMutableList() - Timber.d("Fetched similar id events of size %s", it.size) - setUpAdapter() - }) - - similarEventsViewModel.error - .nonNull() - .observe(viewLifecycleOwner, Observer { - showErrorSnack?.invoke(it) - }) - - similarEventsViewModel.progress - .nonNull() - .observe(viewLifecycleOwner, Observer { - progressBar.isVisible = it - }) - - return rootView - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - similarEventsListAdapter = EventsListAdapter(EventLayoutType.SIMILAR_EVENTS, EventsDiffCallback()) - - val eventClickListener: EventClickListener = object : EventClickListener { - override fun onClick(eventID: Long) { - findNavController(view).navigate(EventDetailsFragmentDirections - .actionSimilarEventsToEventDetails(eventID)) - } - } - - val favFabClickListener: FavoriteFabClickListener = object : FavoriteFabClickListener { - override fun onClick(event: Event, itemPosition: Int) { - similarEventsViewModel.setFavorite(event.id, !event.favorite) - event.favorite = !event.favorite - similarEventsListAdapter.notifyItemChanged(itemPosition) - } - } - - similarEventsListAdapter.apply { - onEventClick = eventClickListener - onFavFabClick = favFabClickListener - } - - linearLayoutManager = LinearLayoutManager(requireContext()) - linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL - view.similarEventsRecycler.layoutManager = linearLayoutManager - - view.similarEventsRecycler.adapter = similarEventsListAdapter - view.similarEventsRecycler.isNestedScrollingEnabled = false - } - - private fun handleVisibility(similarEvents: List) { - similarEventsDivider.isVisible = !similarEvents.isEmpty() - moreLikeThis.isVisible = !similarEvents.isEmpty() - similarEventsRecycler.isVisible = !similarEvents.isEmpty() - } - - /* - function to set errorSnackMessage CallBack, to be invoked , - to be invoked when snack error is generated - */ - fun setErrorSnack(errorSnack: (String) -> Unit) { - showErrorSnack = errorSnack - } - - private fun setUpAdapter() { - similarEvents.clear() - var id: Long - - when { - similarIdEvents.size != 0 && similarLocationEvents.size != 0 -> { - similarIdEvents.forEach { - id = it.id - if (similarLocationEvents.find { id == it.id } == null) similarEvents.add(it) - } - similarEvents.addAll(similarLocationEvents) - } - similarIdEvents.size == 0 -> similarEvents.addAll(similarLocationEvents) - similarLocationEvents.size == 0 -> similarEvents.addAll(similarIdEvents) - } - - handleVisibility(similarEvents) - Timber.d("Fetched Similar events of size %s", similarEvents.size) - if (similarEventsListAdapter.currentList.size != similarEvents.size) similarEvents.shuffle() - similarEventsListAdapter.submitList(similarEvents) - similarEventsListAdapter.notifyDataSetChanged() - } -} diff --git a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsViewModel.kt deleted file mode 100644 index 432e5708c..000000000 --- a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsViewModel.kt +++ /dev/null @@ -1,80 +0,0 @@ -package org.fossasia.openevent.general.event.topic - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign -import org.fossasia.openevent.general.utils.extensions.withDefaultSchedulers -import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.common.SingleLiveEvent -import org.fossasia.openevent.general.data.Resource -import org.fossasia.openevent.general.event.Event -import org.fossasia.openevent.general.event.EventService -import timber.log.Timber - -class SimilarEventsViewModel(private val eventService: EventService, private val resource: Resource) : ViewModel() { - - private val compositeDisposable = CompositeDisposable() - - private val mutableProgress = MutableLiveData() - val progress: LiveData = mutableProgress - private val mutableSimilarLocationEvents = MutableLiveData>() - val similarLocationEvents: LiveData> = mutableSimilarLocationEvents - private val mutableSimilarIdEvents = MutableLiveData>() - val similarIdEvents: LiveData> = mutableSimilarIdEvents - private val mutableError = SingleLiveEvent() - val error: LiveData = mutableError - - var eventId: Long = -1 - - fun loadSimilarIdEvents(id: Long) { - if (id == -1L) { - return - } - compositeDisposable += eventService.getSimilarEvents(id) - .withDefaultSchedulers() - .doOnSubscribe { - mutableProgress.value = true - }.subscribe({ - mutableProgress.value = false - mutableSimilarIdEvents.value = it.filter { it.id != eventId } - }, { - Timber.e(it, "Error fetching similar events") - mutableError.value = "Error fetching similar events" - }) - } - - fun loadSimilarLocationEvents(location: String) { - - compositeDisposable += eventService.getEventsByLocation(location) - .withDefaultSchedulers() - .doOnSubscribe { - mutableProgress.value = true - } - .doFinally { - mutableProgress.value = false - }.subscribe({ - mutableSimilarLocationEvents.value = it.filter { it.id != eventId } - }, { - Timber.e(it, "Error fetching similar events") - mutableError.value = resource.getString(R.string.fetch_similar_events_error_message) - }) - } - - fun setFavorite(eventId: Long, favorite: Boolean) { - compositeDisposable += eventService.setFavorite(eventId, favorite) - .withDefaultSchedulers() - .subscribe({ - Timber.d("Success") - }, { - Timber.e(it, "Error") - mutableError.value = resource.getString(R.string.error) - }) - } - - override fun onCleared() { - super.onCleared() - compositeDisposable.clear() - } -} diff --git a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksFragment.kt b/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksFragment.kt deleted file mode 100644 index 9c728e797..000000000 --- a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksFragment.kt +++ /dev/null @@ -1,107 +0,0 @@ -package org.fossasia.openevent.general.social - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isGone -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.android.synthetic.main.fragment_social_links.eventHostDetails -import kotlinx.android.synthetic.main.fragment_social_links.socialLinksRecycler -import kotlinx.android.synthetic.main.fragment_social_links.view.progressBarSocial -import kotlinx.android.synthetic.main.fragment_social_links.view.socialLinkReload -import kotlinx.android.synthetic.main.fragment_social_links.view.socialLinksRecycler -import kotlinx.android.synthetic.main.fragment_social_links.view.socialNoInternet -import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.event.EVENT_ID -import org.fossasia.openevent.general.utils.extensions.nonNull -import org.koin.androidx.viewmodel.ext.android.viewModel -import timber.log.Timber - -class SocialLinksFragment : Fragment() { - private val socialLinksRecyclerAdapter: SocialLinksRecyclerAdapter = SocialLinksRecyclerAdapter() - private val socialLinksViewModel by viewModel() - private lateinit var rootView: View - private var id: Long = -1 - private lateinit var linearLayoutManager: LinearLayoutManager - private var showErrorSnack: ((String) -> Unit)? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val bundle = this.arguments - if (bundle != null) { - id = bundle.getLong(EVENT_ID, -1) - } - loadSocialLink() - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - rootView = inflater.inflate(R.layout.fragment_social_links, container, false) - - rootView.progressBarSocial.isIndeterminate = true - - linearLayoutManager = LinearLayoutManager(requireContext()) - linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL - rootView.socialLinksRecycler.layoutManager = linearLayoutManager - - rootView.socialLinksRecycler.adapter = socialLinksRecyclerAdapter - rootView.socialLinksRecycler.isNestedScrollingEnabled = false - - rootView.socialLinkReload.setOnClickListener { - loadSocialLink() - } - - socialLinksViewModel.progress - .nonNull() - .observe(viewLifecycleOwner, Observer { - rootView.progressBarSocial.isVisible = it - }) - - socialLinksViewModel.socialLinks - .nonNull() - .observe(viewLifecycleOwner, Observer { - socialLinksRecyclerAdapter.addAll(it) - handleVisibility(it) - socialLinksRecyclerAdapter.notifyDataSetChanged() - Timber.d("Fetched social-links of size %s", socialLinksRecyclerAdapter.itemCount) - }) - - socialLinksViewModel.error - .nonNull() - .observe(viewLifecycleOwner, Observer { - showErrorSnack?.invoke(it) - }) - - socialLinksViewModel.internetError - .nonNull() - .observe(viewLifecycleOwner, Observer { - rootView.socialNoInternet.isVisible = it - }) - - return rootView - } - - /* - function to set errorSnackMessage CallBack, to be invoked , - to be invoked when snack error is generated - */ - fun setErrorSnack(errorSnack: (String) -> Unit) { - showErrorSnack = errorSnack - } - - private fun handleVisibility(socialLinks: List) { - eventHostDetails.isGone = socialLinks.isEmpty() - socialLinksRecycler.isGone = socialLinks.isEmpty() - } - - private fun loadSocialLink() { - socialLinksViewModel.checkAndLoadSocialLinks(id) - } -} diff --git a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksRecyclerAdapter.kt index 33cc155fc..be8dadca4 100644 --- a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksRecyclerAdapter.kt @@ -14,6 +14,7 @@ class SocialLinksRecyclerAdapter : RecyclerView.Adapter() if (socialLinkList.isNotEmpty()) this.socialLinks.clear() this.socialLinks.addAll(socialLinkList) + notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SocialLinksViewHolder { @@ -23,7 +24,6 @@ class SocialLinksRecyclerAdapter : RecyclerView.Adapter() override fun onBindViewHolder(holder: SocialLinksViewHolder, position: Int) { val socialLink = socialLinks[position] - holder.bind(socialLink) } diff --git a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewHolder.kt index 0239b2a14..23cc5cebc 100644 --- a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewHolder.kt @@ -26,15 +26,17 @@ class SocialLinksViewHolder(itemView: View, private var context: Context) : Recy } private fun getSocialLinkDrawableId(name: String): Int { - if (name.contains("github")) return R.drawable.ic_github - else if (name.contains("twitter")) return R.drawable.ic_twitter - else if (name.contains("facebook")) return R.drawable.ic_facebook - else if (name.contains("linkedin")) return R.drawable.ic_linkedin - else if (name.contains("youtube")) return R.drawable.ic_youtube - else if (name.contains("google")) return R.drawable.ic_google_plus - else if (name.contains("wiki")) return R.drawable.ic_wikipedia - else if (name.contains("flickr")) return R.drawable.ic_flickr - else if (name.contains("blog")) return R.drawable.ic_blogger - else return R.drawable.ic_link_black + return when { + name.contains("github") -> R.drawable.ic_github + name.contains("twitter") -> R.drawable.ic_twitter + name.contains("facebook") -> R.drawable.ic_facebook + name.contains("linkedin") -> R.drawable.ic_linkedin + name.contains("youtube") -> R.drawable.ic_youtube + name.contains("google") -> R.drawable.ic_google_plus + name.contains("wiki") -> R.drawable.ic_wikipedia + name.contains("flickr") -> R.drawable.ic_flickr + name.contains("blog") -> R.drawable.ic_blogger + else -> R.drawable.ic_link_black + } } } diff --git a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewModel.kt deleted file mode 100644 index dd9857325..000000000 --- a/app/src/main/java/org/fossasia/openevent/general/social/SocialLinksViewModel.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.fossasia.openevent.general.social - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import io.reactivex.disposables.CompositeDisposable -import org.fossasia.openevent.general.R -import io.reactivex.rxkotlin.plusAssign -import org.fossasia.openevent.general.utils.extensions.withDefaultSchedulers -import org.fossasia.openevent.general.common.SingleLiveEvent -import org.fossasia.openevent.general.data.Network -import org.fossasia.openevent.general.data.Resource -import timber.log.Timber - -class SocialLinksViewModel( - private val socialLinksService: SocialLinksService, - private val resource: Resource, - private val network: Network -) : ViewModel() { - - private val compositeDisposable = CompositeDisposable() - - private val mutableProgress = MutableLiveData() - val progress: LiveData = mutableProgress - private val mutableSocialLinks = MutableLiveData>() - val socialLinks: LiveData> = mutableSocialLinks - private val mutableError = SingleLiveEvent() - val error: LiveData = mutableError - private val mutableInternetError = MutableLiveData() - val internetError: LiveData = mutableInternetError - - private fun loadSocialLinks(id: Long) { - compositeDisposable += socialLinksService.getSocialLinks(id) - .withDefaultSchedulers() - .doOnSubscribe { - mutableProgress.value = true - }.subscribe({ - mutableSocialLinks.value = it - mutableProgress.value = false - }, { - Timber.e(it, resource.getString(R.string.error_fetching_social_links_message)) - mutableError.value = resource.getString(R.string.error_fetching_social_links_message) - mutableProgress.value = false - }) - } - - override fun onCleared() { - super.onCleared() - compositeDisposable.clear() - } - - fun checkAndLoadSocialLinks(id: Long) { - if (network.isNetworkConnected()) { - loadSocialLinks(id) - mutableInternetError.value = false - } else { - mutableInternetError.value = true - } - } -} diff --git a/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorRecyclerAdapter.kt index 9edcd2168..9429a8c57 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sponsor/SponsorRecyclerAdapter.kt @@ -14,6 +14,7 @@ class SponsorRecyclerAdapter : RecyclerView.Adapter() { fun addAll(newSponsors: List) { if (sponsorList.isNotEmpty()) sponsorList.clear() sponsorList.addAll(SponsorUtil.sortSponsorByLevel(newSponsors)) + notifyDataSetChanged() } override fun onBindViewHolder(holder: SponsorViewHolder, position: Int) { diff --git a/app/src/main/res/layout/content_event.xml b/app/src/main/res/layout/content_event.xml index 35e1c7788..5ea887146 100644 --- a/app/src/main/res/layout/content_event.xml +++ b/app/src/main/res/layout/content_event.xml @@ -386,13 +386,34 @@ - + android:paddingRight="@dimen/padding_large" + android:descendantFocusability="blocksDescendants" + android:animateLayoutChanges="true" + android:visibility="gone" + tools:visibility="visible"> + + + + - + + + android:layout_marginLeft="@dimen/layout_margin_large" + android:layout_marginRight="@dimen/layout_margin_large" + android:layout_marginBottom="@dimen/layout_margin_large" + android:text="@string/more_like_this" + android:textColor="@color/black" + android:textSize="@dimen/event_details_headers" /> + + diff --git a/app/src/main/res/layout/fragment_similar_events.xml b/app/src/main/res/layout/fragment_similar_events.xml deleted file mode 100644 index b957689b5..000000000 --- a/app/src/main/res/layout/fragment_similar_events.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_social_links.xml b/app/src/main/res/layout/fragment_social_links.xml deleted file mode 100644 index 775c6a79a..000000000 --- a/app/src/main/res/layout/fragment_social_links.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/navigation/navigation_graph.xml b/app/src/main/res/navigation/navigation_graph.xml index e8c58a4fb..8f78aea87 100644 --- a/app/src/main/res/navigation/navigation_graph.xml +++ b/app/src/main/res/navigation/navigation_graph.xml @@ -53,28 +53,6 @@ app:exitAnim="@anim/slide_out_left"/> - - - - - - - - Không thể tải thông tin người tham dự Không thể tải sự kiện Không thể tải sự kiện - Không thể tải sự kiện tương tự Lỗi Không thể tải các sự kiện được yêu thích Không thể tải vé của người dùng Không thể tải sự kiện của người dùng Lỗi thêm vào yêu thích - Không thể tải liên kết xã hội Không thể tải vé Lựa chọn hình ảnh Thay đổi của bạn chưa được lưu lại diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 32693adf8..d7a93938f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,13 +284,11 @@ Error fetching event Error fetching events - Error fetching similar events Error Error fetching favorite events Failed to list Orders under a user Failed to list events under a user Error adding to favorites - Error fetching Social Links Error fetching tickets @@ -313,6 +311,8 @@ Information is not loaded! Please check your internet connection Popular Locations + Social Link + Similar Events Anything And I\'m up for From 67ca358807752eaa79df2319f9ce8ff94cbd39fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 18 May 2019 03:10:21 +0530 Subject: [PATCH 29/35] chore(deps): bump koin_version from 2.0.0-GA5 to 2.0.0-GA6 (#1781) chore(deps): bump koin_version from 2.0.0-GA5 to 2.0.0-GA6 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6f4d27ac2..4b60798d3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -96,7 +96,7 @@ repositories { dependencies { def lifecycle_version = "2.2.0-alpha01" - def koin_version = "2.0.0-GA5" + def koin_version = "2.0.0-GA6" def roomVersion = "2.1.0-beta01" def ktx_version = "1.0.0" def ktx2_version = "2.0.0" From 71f98cf2b1c565e7bc3941e5ea768d3f6247e7ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 18 May 2019 11:28:30 +0530 Subject: [PATCH 30/35] chore(deps): bump stripe-android from 8.7.0 to 9.0.1 (#1782) Bumps [stripe-android](https://github.com/stripe/stripe-android) from 8.7.0 to 9.0.1. - [Release notes](https://github.com/stripe/stripe-android/releases) - [Changelog](https://github.com/stripe/stripe-android/blob/master/CHANGELOG.md) - [Commits](https://github.com/stripe/stripe-android/compare/v8.7.0...v9.0.1) Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4b60798d3..62ae242f1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -162,7 +162,7 @@ dependencies { implementation 'com.squareup.picasso:picasso:2.71828' // Stripe - implementation 'com.stripe:stripe-android:8.7.0' + implementation 'com.stripe:stripe-android:9.0.1' // QR Code implementation 'com.journeyapps:zxing-android-embedded:3.6.0' From 57da2193e11d54a69d0847c0d4e4246bb14c8c7b Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Sat, 18 May 2019 16:44:11 +0200 Subject: [PATCH 31/35] Summary Change: Retain ticket locally when logged in (#1761) Detail: - Retain the attendee list, order and event in the database after fetching. Current flow of going into OrdersUnderUserFragment is to fetch orders if there is internet, otherwise fetch saved orders from the database. Fixes: #1610 --- .../openevent/general/OpenEventDatabase.kt | 2 +- .../openevent/general/attendees/Attendee.kt | 1 + .../general/attendees/AttendeeDao.kt | 5 ++- .../attendees/ListAttendeeIdConverter.kt | 3 +- .../openevent/general/auth/AuthService.kt | 8 +++- .../fossasia/openevent/general/di/Modules.kt | 2 +- .../openevent/general/event/EventDao.kt | 3 ++ .../openevent/general/event/EventService.kt | 16 +++++++- .../fossasia/openevent/general/order/Order.kt | 9 +---- .../openevent/general/order/OrderDao.kt | 11 ++++++ .../general/order/OrderDetailsFragment.kt | 6 +-- .../order/OrderDetailsRecyclerAdapter.kt | 2 + .../general/order/OrderDetailsViewModel.kt | 29 ++++++++++---- .../openevent/general/order/OrderService.kt | 30 ++++++++++---- .../general/order/OrdersRecyclerAdapter.kt | 21 +++------- .../general/order/OrdersUnderUserFragment.kt | 14 ++----- .../general/order/OrdersUnderUserViewModel.kt | 39 ++++++++----------- .../general/order/OrdersViewHolder.kt | 15 +++---- .../general/ticket/TicketIdConverter.kt | 10 +++-- .../main/res/navigation/navigation_graph.xml | 9 ++++- 20 files changed, 139 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt b/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt index acdd1bd97..340e8fcf2 100644 --- a/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt +++ b/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt @@ -40,7 +40,7 @@ import org.fossasia.openevent.general.ticket.TicketIdConverter @Database(entities = [Event::class, User::class, SocialLink::class, Ticket::class, Attendee::class, EventTopic::class, Order::class, CustomForm::class, Speaker::class, SpeakerWithEvent::class, Sponsor::class, - SponsorWithEvent::class, Session::class], version = 5) + SponsorWithEvent::class, Session::class], version = 6) @TypeConverters(EventIdConverter::class, EventTopicConverter::class, EventTypeConverter::class, EventSubTopicConverter::class, TicketIdConverter::class, MicroLocationConverter::class, AttendeeIdConverter::class, ListAttendeeIdConverter::class, SessionTypeConverter::class) diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/Attendee.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/Attendee.kt index 941448e68..976bd5b57 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/Attendee.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/Attendee.kt @@ -39,6 +39,7 @@ data class Attendee( @ColumnInfo(index = true) @Relationship("event") var event: EventId? = null, + @ColumnInfo(index = true) @Relationship("ticket") var ticket: TicketId? = null ) diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeDao.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeDao.kt index ae8ffb36d..715869978 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeDao.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeDao.kt @@ -19,11 +19,14 @@ interface AttendeeDao { fun insertCustomForms(customForms: List) @Query("DELETE FROM Attendee") - fun deleteAll() + fun deleteAllAttendees() @Query("SELECT * from Attendee WHERE id in (:ids)") fun getAttendeesWithIds(ids: List): Single> @Query("SELECT * from CustomForm WHERE event = :eventId") fun getCustomFormsForId(eventId: Long): Single> + + @Query("SELECT * FROM Attendee") + fun getAllAttendees(): Single> } diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/ListAttendeeIdConverter.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/ListAttendeeIdConverter.kt index adcb0787e..0f1bbcfaa 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/ListAttendeeIdConverter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/ListAttendeeIdConverter.kt @@ -3,6 +3,7 @@ package org.fossasia.openevent.general.attendees import androidx.room.TypeConverter import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper class ListAttendeeIdConverter { @@ -14,7 +15,7 @@ class ListAttendeeIdConverter { @TypeConverter fun toListAttendeeId(attendeeList: String): List { - val objectMapper = ObjectMapper() + val objectMapper = jacksonObjectMapper() val mapType = object : TypeReference>() {} return objectMapper.readValue(attendeeList, mapType) } diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt b/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt index c249341f6..f628bc3c4 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt @@ -2,18 +2,22 @@ package org.fossasia.openevent.general.auth import io.reactivex.Completable import io.reactivex.Single +import org.fossasia.openevent.general.attendees.AttendeeDao import org.fossasia.openevent.general.auth.change.ChangeRequestToken import org.fossasia.openevent.general.auth.change.ChangeRequestTokenResponse import org.fossasia.openevent.general.auth.change.Password import org.fossasia.openevent.general.auth.forgot.Email import org.fossasia.openevent.general.auth.forgot.RequestToken import org.fossasia.openevent.general.auth.forgot.RequestTokenResponse +import org.fossasia.openevent.general.order.OrderDao import timber.log.Timber class AuthService( private val authApi: AuthApi, private val authHolder: AuthHolder, - private val userDao: UserDao + private val userDao: UserDao, + private val orderDao: OrderDao, + private val attendeeDao: AttendeeDao ) { fun login(username: String, password: String): Single { if (username.isEmpty() || password.isEmpty()) @@ -55,6 +59,8 @@ class AuthService( return Completable.fromAction { authHolder.token = null userDao.deleteUser(authHolder.getId()) + orderDao.deleteAllOrders() + attendeeDao.deleteAllAttendees() } } diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index eefd48820..24bce8364 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -182,7 +182,7 @@ val apiModule = module { } factory { AuthHolder(get()) } - factory { AuthService(get(), get(), get()) } + factory { AuthService(get(), get(), get(), get(), get()) } factory { EventService(get(), get(), get(), get(), get(), get(), get(), get()) } factory { SpeakerService(get(), get(), get()) } diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDao.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDao.kt index c65685a9b..66c623483 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDao.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDao.kt @@ -24,6 +24,9 @@ interface EventDao { @Query("SELECT * from Event WHERE id = :id") fun getEvent(id: Long): Flowable + @Query("SELECT * FROM event WHERE id = :eventId") + fun getEventById(eventId: Long): Single + @Query("SELECT * from Event WHERE id in (:ids)") fun getEventWithIds(ids: List): Single> diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt index cbf658a09..8ced85a90 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventService.kt @@ -110,13 +110,25 @@ class EventService( return eventDao.getEvent(id) } - fun getEventFromApi(id: Long): Single { - return eventApi.getEventFromApi(id) + fun getEventById(eventId: Long): Single { + return eventDao.getEventById(eventId) + .onErrorResumeNext { + eventApi.getEventFromApi(eventId).map { + eventDao.insertEvent(it) + it + } + } } fun getEventsUnderUser(eventIds: List): Single> { val query = buildQuery(eventIds) return eventApi.eventsUnderUser(query) + .map { + eventDao.insertEvents(it) + it + }.onErrorResumeNext { + eventDao.getEventWithIds(eventIds).map { it } + } } fun setFavorite(eventId: Long, favorite: Boolean): Completable { diff --git a/app/src/main/java/org/fossasia/openevent/general/order/Order.kt b/app/src/main/java/org/fossasia/openevent/general/order/Order.kt index 6ed5b477c..5c19a8a66 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/Order.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/Order.kt @@ -2,7 +2,6 @@ package org.fossasia.openevent.general.order import androidx.room.ColumnInfo import androidx.room.Entity -import androidx.room.ForeignKey import androidx.room.PrimaryKey import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.annotation.JsonNaming @@ -11,18 +10,12 @@ import com.github.jasminb.jsonapi.annotations.Id import com.github.jasminb.jsonapi.annotations.Relationship import com.github.jasminb.jsonapi.annotations.Type import io.reactivex.annotations.NonNull -import org.fossasia.openevent.general.attendees.Attendee import org.fossasia.openevent.general.attendees.AttendeeId -import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.EventId @Type("order") @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) -@Entity(foreignKeys = [ - (ForeignKey(entity = Event::class, parentColumns = ["id"], - childColumns = ["event"], onDelete = ForeignKey.CASCADE)), - (ForeignKey(entity = Attendee::class, parentColumns = ["id"], - childColumns = ["attendees"], onDelete = ForeignKey.CASCADE))]) +@Entity data class Order( @Id(IntegerIdHandler::class) @PrimaryKey diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDao.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDao.kt index 35b2a67c5..221437814 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDao.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDao.kt @@ -3,6 +3,8 @@ package org.fossasia.openevent.general.order import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.reactivex.Single @Dao interface OrderDao { @@ -11,4 +13,13 @@ interface OrderDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrder(order: Order) + + @Query("SELECT * FROM `order`") + fun getAllOrders(): Single> + + @Query("DELETE FROM `order`") + fun deleteAllOrders() + + @Query("SELECT * FROM `order` WHERE id = :orderId") + fun getOrderById(orderId: Long): Single } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt index 463d48c33..4627a63ca 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt @@ -48,20 +48,18 @@ class OrderDetailsFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - ordersRecyclerAdapter.setOrderIdentifier(safeArgs.orders) + ordersRecyclerAdapter.setOrderIdentifier(safeArgs.orderIdentifier) orderDetailsViewModel.event .nonNull() .observe(this, Observer { ordersRecyclerAdapter.setEvent(it) - ordersRecyclerAdapter.notifyDataSetChanged() }) orderDetailsViewModel.attendees .nonNull() .observe(this, Observer { ordersRecyclerAdapter.addAll(it) - ordersRecyclerAdapter.notifyDataSetChanged() Timber.d("Fetched attendees of size %s", ordersRecyclerAdapter.itemCount) }) } @@ -127,7 +125,7 @@ class OrderDetailsFragment : Fragment() { }) orderDetailsViewModel.loadEvent(safeArgs.eventId) - orderDetailsViewModel.loadAttendeeDetails(safeArgs.orders) + orderDetailsViewModel.loadAttendeeDetails(safeArgs.orderId) return rootView } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt index f2c1aeac5..50d89d8b8 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsRecyclerAdapter.kt @@ -21,10 +21,12 @@ class OrderDetailsRecyclerAdapter : RecyclerView.Adapter if (attendees.isNotEmpty()) this.attendees.clear() this.attendees.addAll(attendeeList) + notifyDataSetChanged() } fun setEvent(event: Event?) { this.event = event + notifyDataSetChanged() } fun setSeeEventListener(listener: EventDetailsListener) { diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt index dc64365f6..0921f049c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewModel.kt @@ -36,7 +36,7 @@ class OrderDetailsViewModel( throw IllegalStateException("ID should never be -1") } - compositeDisposable += eventService.getEventFromApi(id) + compositeDisposable += eventService.getEventById(id) .withDefaultSchedulers() .subscribe({ mutableEvent.value = it @@ -46,21 +46,34 @@ class OrderDetailsViewModel( }) } - fun loadAttendeeDetails(id: String) { - if (id.equals(-1)) { - throw IllegalStateException("ID should never be -1") - } + fun loadAttendeeDetails(orderId: Long) { + if (orderId == -1L) return - compositeDisposable += orderService.attendeesUnderOrder(id) + compositeDisposable += orderService.getOrderById(orderId) .withDefaultSchedulers() .doOnSubscribe { mutableProgress.value = true - }.doFinally { - mutableProgress.value = false }.subscribe({ + loadAttendeeUnderOrder(it) + }, { + Timber.e(it, "Error fetching attendee details") + mutableProgress.value = false + message.value = resource.getString(R.string.error_fetching_attendee_details_message) + }) + } + + private fun loadAttendeeUnderOrder(order: Order) { + val orderIdentifier = order.identifier ?: return + + compositeDisposable += orderService + .getAttendeesUnderOrder(orderIdentifier, order.attendees.map { it.id }) + .withDefaultSchedulers() + .subscribe({ mutableAttendees.value = it + mutableProgress.value = false }, { Timber.e(it, "Error fetching attendee details") + mutableProgress.value = false message.value = resource.getString(R.string.error_fetching_attendee_details_message) }) } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderService.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderService.kt index 99daff01b..415e06690 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderService.kt @@ -12,12 +12,10 @@ class OrderService( fun placeOrder(order: Order): Single { return orderApi.placeOrder(order) .map { order -> - val attendeeIds = order.attendees?.map { order.id } - if (attendeeIds != null) { - attendeeDao.getAttendeesWithIds(attendeeIds).map { - if (it.size == attendeeIds.size) { - orderDao.insertOrder(order) - } + val attendeeIds = order.attendees.map { order.id } + attendeeDao.getAttendeesWithIds(attendeeIds).map { + if (it.size == attendeeIds.size) { + orderDao.insertOrder(order) } } order @@ -32,11 +30,27 @@ class OrderService( return orderApi.confirmOrder(identifier, order) } - fun orderUser(userId: Long): Single> { + fun getOrdersOfUser(userId: Long): Single> { return orderApi.ordersUnderUser(userId) + .map { + orderDao.insertOrders(it) + it + }.onErrorResumeNext { + orderDao.getAllOrders().map { it } + } } - fun attendeesUnderOrder(orderIdentifier: String): Single> { + fun getOrderById(orderId: Long): Single { + return orderDao.getOrderById(orderId) + } + + fun getAttendeesUnderOrder(orderIdentifier: String, attendeesIds: List): Single> { return orderApi.attendeesUnderOrder(orderIdentifier) + .map { + attendeeDao.insertAttendees(it) + it + }.onErrorResumeNext { + attendeeDao.getAttendeesWithIds(attendeesIds) + } } } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersRecyclerAdapter.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersRecyclerAdapter.kt index ab7fe3b53..55117b93c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersRecyclerAdapter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersRecyclerAdapter.kt @@ -8,19 +8,19 @@ import org.fossasia.openevent.general.event.Event class OrdersRecyclerAdapter : RecyclerView.Adapter() { - private val eventAndOrderIdentifier = ArrayList>() - private val showExpired = false + private val eventAndOrderIdentifier = ArrayList>() + private var showExpired = false private var clickListener: OrderClickListener? = null - var attendeesNumber = listOf() fun setListener(listener: OrderClickListener) { clickListener = listener } - fun addAllPairs(list: List>, showExpired: Boolean) { + fun addAllPairs(list: List>, showExpired: Boolean) { if (eventAndOrderIdentifier.isNotEmpty()) this.eventAndOrderIdentifier.clear() eventAndOrderIdentifier.addAll(list) + this.showExpired = showExpired } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrdersViewHolder { @@ -29,23 +29,14 @@ class OrdersRecyclerAdapter : RecyclerView.Adapter() { } override fun onBindViewHolder(holder: OrdersViewHolder, position: Int) { - attendeesNumber[position]?.let { - holder.bind(eventAndOrderIdentifier[position].first, - clickListener, - eventAndOrderIdentifier[position].second, - it, showExpired) - } + holder.bind(eventAndOrderIdentifier[position], showExpired, clickListener) } override fun getItemCount(): Int { return eventAndOrderIdentifier.size } - fun setAttendeeNumber(number: List) { - attendeesNumber = number - } - interface OrderClickListener { - fun onClick(eventID: Long, orderIdentifier: String) + fun onClick(eventID: Long, orderIdentifier: String, orderId: Long) } } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt index 5bee56e3a..d51c62a20 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt @@ -77,9 +77,9 @@ class OrdersUnderUserFragment : Fragment(), ScrollToTop { if (safeArgs.showExpired) rootView.expireFilter.isVisible = false val recyclerViewClickListener = object : OrdersRecyclerAdapter.OrderClickListener { - override fun onClick(eventID: Long, orderIdentifier: String) { - findNavController(rootView).navigate(OrdersUnderUserFragmentDirections - .actionOrderUserToOrderDetails(eventID, orderIdentifier)) + override fun onClick(eventID: Long, orderIdentifier: String, orderId: Long) { + findNavController(rootView).navigate(OrdersUnderUserFragmentDirections + .actionOrderUserToOrderDetails(eventID, orderIdentifier, orderId)) } } @@ -109,13 +109,7 @@ class OrdersUnderUserFragment : Fragment(), ScrollToTop { showNoTicketsScreen(it) }) - ordersUnderUserVM.attendeesNumber - .nonNull() - .observe(viewLifecycleOwner, Observer { - ordersRecyclerAdapter.setAttendeeNumber(it) - }) - - ordersUnderUserVM.eventAndOrderIdentifier + ordersUnderUserVM.eventAndOrder .nonNull() .observe(viewLifecycleOwner, Observer { val list = it.sortedByDescending { diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserViewModel.kt index 47815b5f0..363be8961 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserViewModel.kt @@ -24,17 +24,14 @@ class OrdersUnderUserViewModel( private val compositeDisposable = CompositeDisposable() private lateinit var order: List - private val mutableAttendeesNumber = MutableLiveData>() - val attendeesNumber: LiveData> = mutableAttendeesNumber private var eventIdMap = mutableMapOf() private val eventIdAndTimes = mutableMapOf() private val mutableMessage = SingleLiveEvent() val message: LiveData = mutableMessage - private val mutableEventAndOrderIdentifier = MutableLiveData>>() - val eventAndOrderIdentifier: LiveData>> = - mutableEventAndOrderIdentifier - private val mutableshowShimmerResults = MutableLiveData() - val showShimmerResults: LiveData = mutableshowShimmerResults + private val mutableEventAndOrder = MutableLiveData>>() + val eventAndOrder: LiveData>> = mutableEventAndOrder + private val mutableShowShimmerResults = MutableLiveData() + val showShimmerResults: LiveData = mutableShowShimmerResults private val mutableNoTickets = MutableLiveData() val noTickets: LiveData = mutableNoTickets @@ -43,24 +40,22 @@ class OrdersUnderUserViewModel( fun isLoggedIn() = authHolder.isLoggedIn() fun ordersUnderUser(showExpired: Boolean) { - compositeDisposable += orderService.orderUser(getId()) + compositeDisposable += orderService.getOrdersOfUser(getId()) .withDefaultSchedulers() .doOnSubscribe { - mutableshowShimmerResults.value = true + mutableShowShimmerResults.value = true mutableNoTickets.value = false }.subscribe({ order = it - mutableAttendeesNumber.value = it.map { it.attendees.size } - val eventIds = it.mapNotNull { order -> order.event?.id } if (eventIds.isNotEmpty()) { eventsUnderUser(eventIds, showExpired) } else { - mutableshowShimmerResults.value = false + mutableShowShimmerResults.value = false mutableNoTickets.value = true } }, { - mutableshowShimmerResults.value = false + mutableShowShimmerResults.value = false mutableNoTickets.value = true mutableMessage.value = resource.getString(R.string.list_orders_fail_message) Timber.d(it, "Failed to list Orders under a user ") @@ -71,7 +66,7 @@ class OrdersUnderUserViewModel( compositeDisposable += eventService.getEventsUnderUser(eventIds) .withDefaultSchedulers() .doFinally { - mutableshowShimmerResults.value = false + mutableShowShimmerResults.value = false }.subscribe({ val events = ArrayList() it.map { @@ -83,22 +78,20 @@ class OrdersUnderUserViewModel( } eventIdMap[it.id] = it } - var eventAndIdentifier = ArrayList>() - var finalList: List> + val eventAndIdentifier = ArrayList>() order.forEach { val event = eventIdMap[it.event?.id] - if (event != null && it.identifier != null) - eventAndIdentifier.add(Pair(event, it.identifier)) + if (event != null) + eventAndIdentifier.add(Pair(event, it)) } - finalList = eventAndIdentifier - when (showExpired) { - false -> finalList = finalList.filter { + val finalList = when (showExpired) { + false -> eventAndIdentifier.filter { EventUtils.getTimeInMilliSeconds(it.first.endsAt, null) > System.currentTimeMillis() } - true -> finalList = finalList.filter { + true -> eventAndIdentifier.filter { EventUtils.getTimeInMilliSeconds(it.first.endsAt, null) < System.currentTimeMillis() } } if (finalList.isEmpty()) mutableNoTickets.value = true - mutableEventAndOrderIdentifier.value = finalList + mutableEventAndOrder.value = finalList }, { mutableMessage.value = resource.getString(R.string.list_events_fail_message) Timber.d(it, "Failed to list events under a user ") diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersViewHolder.kt index 3571f7b9c..4128b623d 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersViewHolder.kt @@ -13,12 +13,12 @@ import org.fossasia.openevent.general.event.EventUtils class OrdersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind( - event: Event, - clickListener: OrdersRecyclerAdapter.OrderClickListener?, - orderIdentifier: String?, - attendeesNumber: Int, - showExpired: Boolean + eventAndOrder: Pair, + showExpired: Boolean, + listener: OrdersRecyclerAdapter.OrderClickListener? ) { + val event = eventAndOrder.first + val order = eventAndOrder.second val formattedDateTime = EventUtils.getEventDateTime(event.startsAt, event.timezone) val formattedTime = EventUtils.getFormattedTime(formattedDateTime) val timezone = EventUtils.getFormattedTimeZone(formattedDateTime) @@ -26,9 +26,10 @@ class OrdersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { itemView.eventName.text = event.name itemView.time.text = "Starts at $formattedTime $timezone" itemView.setOnClickListener { - orderIdentifier?.let { it1 -> clickListener?.onClick(event.id, it1) } + listener?.onClick(event.id, order.identifier ?: "", order.id) } + val attendeesNumber = order.attendees.size if (attendeesNumber == 1) { itemView.ticketsNumber.text = "See $attendeesNumber Ticket" } else { @@ -44,7 +45,7 @@ class OrdersViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { .placeholder(R.drawable.header) .into(itemView.eventImage) } - if (!showExpired) { + if (showExpired) { val matrix = ColorMatrix() matrix.setSaturation(0F) itemView.eventImage.colorFilter = ColorMatrixColorFilter(matrix) diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketIdConverter.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketIdConverter.kt index 6e0028fb9..f2825ee50 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketIdConverter.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketIdConverter.kt @@ -5,12 +5,14 @@ import androidx.room.TypeConverter class TicketIdConverter { @TypeConverter - fun fromTicketId(ticketId: TicketId): Long { - return ticketId.id + fun fromTicketId(ticketId: TicketId?): Long? { + return ticketId?.id } @TypeConverter - fun toTicketId(id: Long): TicketId { - return TicketId(id) + fun toTicketId(id: Long?): TicketId? { + return id?.let { + TicketId(id) + } } } diff --git a/app/src/main/res/navigation/navigation_graph.xml b/app/src/main/res/navigation/navigation_graph.xml index 8f78aea87..349f8cb16 100644 --- a/app/src/main/res/navigation/navigation_graph.xml +++ b/app/src/main/res/navigation/navigation_graph.xml @@ -350,9 +350,14 @@ android:defaultValue="-1L"/> + android:defaultValue=""/> + + Date: Sat, 18 May 2019 23:05:00 +0200 Subject: [PATCH 32/35] feat: Add track for event (#1783) Detail: - Delete Scope - Add track to session item view and session fragment - Remove wildcard imports Fixes: #1694 --- .../openevent/general/OpenEventDatabase.kt | 3 +- .../fossasia/openevent/general/di/Modules.kt | 3 +- .../fossasia/openevent/general/di/Scopes.kt | 13 ----- .../openevent/general/sessions/Session.kt | 6 ++- .../openevent/general/sessions/SessionApi.kt | 2 +- .../general/sessions/SessionFragment.kt | 47 ++++++++++++++++++- .../general/sessions/SessionViewHolder.kt | 13 +++++ .../general/sessions/SessionViewModel.kt | 7 ++- .../openevent/general/sessions/track/Track.kt | 22 +++++++++ .../general/sessions/track/TrackConverter.kt | 15 ++++++ .../fossasia/openevent/general/utils/Utils.kt | 17 +++++++ app/src/main/res/drawable/ic_track.xml | 8 ++++ app/src/main/res/layout/fragment_session.xml | 36 +++++++++++++- app/src/main/res/layout/item_session.xml | 25 +++++++++- app/src/main/res/values/strings.xml | 3 ++ 15 files changed, 198 insertions(+), 22 deletions(-) delete mode 100644 app/src/main/java/org/fossasia/openevent/general/di/Scopes.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/track/Track.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/sessions/track/TrackConverter.kt create mode 100644 app/src/main/res/drawable/ic_track.xml diff --git a/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt b/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt index 340e8fcf2..f2b365afa 100644 --- a/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt +++ b/app/src/main/java/org/fossasia/openevent/general/OpenEventDatabase.kt @@ -24,6 +24,7 @@ import org.fossasia.openevent.general.sessions.Session import org.fossasia.openevent.general.sessions.SessionDao import org.fossasia.openevent.general.sessions.microlocation.MicroLocationConverter import org.fossasia.openevent.general.sessions.sessiontype.SessionTypeConverter +import org.fossasia.openevent.general.sessions.track.TrackConverter import org.fossasia.openevent.general.social.SocialLink import org.fossasia.openevent.general.social.SocialLinksDao import org.fossasia.openevent.general.speakers.Speaker @@ -43,7 +44,7 @@ import org.fossasia.openevent.general.ticket.TicketIdConverter SponsorWithEvent::class, Session::class], version = 6) @TypeConverters(EventIdConverter::class, EventTopicConverter::class, EventTypeConverter::class, EventSubTopicConverter::class, TicketIdConverter::class, MicroLocationConverter::class, - AttendeeIdConverter::class, ListAttendeeIdConverter::class, SessionTypeConverter::class) + AttendeeIdConverter::class, ListAttendeeIdConverter::class, SessionTypeConverter::class, TrackConverter::class) abstract class OpenEventDatabase : RoomDatabase() { abstract fun eventDao(): EventDao diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index 24bce8364..8d448b418 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -80,6 +80,7 @@ import org.fossasia.openevent.general.event.faq.EventFAQViewModel import org.fossasia.openevent.general.sessions.SessionViewModel import org.fossasia.openevent.general.sessions.microlocation.MicroLocation import org.fossasia.openevent.general.sessions.sessiontype.SessionType +import org.fossasia.openevent.general.sessions.track.Track import org.fossasia.openevent.general.settings.SettingsViewModel import org.fossasia.openevent.general.social.SocialLink import org.fossasia.openevent.general.social.SocialLinkApi @@ -262,7 +263,7 @@ val networkModule = module { CustomForm::class.java, EventLocation::class.java, EventType::class.java, EventSubTopic::class.java, Feedback::class.java, Speaker::class.java, Session::class.java, SessionType::class.java, MicroLocation::class.java, - Sponsor::class.java, EventFAQ::class.java, Notification::class.java) + Sponsor::class.java, EventFAQ::class.java, Notification::class.java, Track::class.java) Retrofit.Builder() .client(get()) diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Scopes.kt b/app/src/main/java/org/fossasia/openevent/general/di/Scopes.kt deleted file mode 100644 index 7f7078281..000000000 --- a/app/src/main/java/org/fossasia/openevent/general/di/Scopes.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.fossasia.openevent.general.di - -/** - * Enum class to collect all possible Fragment scopes for Koin DI - * in one place. This list is expected to grow as Scopes are used in more - * fragments. - */ -enum class Scopes { - EVENTS_FRAGMENT, - SIMILAR_EVENTS_FRAGMENT, - FAVORITE_FRAGMENT, - SEARCH_RESULTS_FRAGMENT -} diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt index 1a8f20ea1..a373b5154 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/Session.kt @@ -12,6 +12,7 @@ import com.github.jasminb.jsonapi.annotations.Relationship import com.github.jasminb.jsonapi.annotations.Type import org.fossasia.openevent.general.sessions.microlocation.MicroLocation import org.fossasia.openevent.general.sessions.sessiontype.SessionType +import org.fossasia.openevent.general.sessions.track.Track @Type("session") @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) @@ -45,5 +46,8 @@ data class Session( var sessionType: SessionType? = null, @ColumnInfo(index = true) @Relationship("microlocation", resolve = true) - var microlocation: MicroLocation? = null + var microlocation: MicroLocation? = null, + @ColumnInfo(index = true) + @Relationship("track", resolve = true) + var track: Track? = null ) diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionApi.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionApi.kt index 70088e26e..9aa14f07e 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionApi.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionApi.kt @@ -7,7 +7,7 @@ import retrofit2.http.Query interface SessionApi { - @GET("events/{eventId}/sessions?include=session-type,microlocation") + @GET("events/{eventId}/sessions?include=session-type,microlocation,track") fun getSessionsForEvent( @Path("eventId") eventId: Long, @Query("sort") sort: String = "created-at", diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt index c0d530902..2613bf240 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt @@ -1,6 +1,7 @@ package org.fossasia.openevent.general.sessions import android.content.Intent +import android.graphics.Color import android.net.Uri import android.os.Bundle import android.provider.CalendarContract @@ -12,7 +13,27 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.navigation.fragment.navArgs import com.squareup.picasso.Picasso -import kotlinx.android.synthetic.main.fragment_session.view.* +import kotlinx.android.synthetic.main.fragment_session.view.progressBar +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTrack +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailAbstract +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailLanguage +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailLanguageContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailLocationInfoContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailLocation +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTimeContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailLocationImageMap +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailType +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailName +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailEndTime +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailStartTime +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailLocationContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailInfoLocation +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailAbstractContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailAbstractSeeMore +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTrackContainer +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailSignUpButton +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTrackIcon import org.fossasia.openevent.general.R import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.utils.Utils @@ -46,6 +67,13 @@ class SessionFragment : Fragment() { makeSessionView(it) }) + sessionViewModel.progress + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.progressBar.visibility = if (it) View.VISIBLE else View.GONE + rootView.sessionDetailContainer.visibility = if (it) View.GONE else View.VISIBLE + }) + sessionViewModel.loadSession(safeArgs.sessionId) return rootView @@ -61,6 +89,12 @@ class SessionFragment : Fragment() { } } + override fun onDestroy() { + super.onDestroy() + Utils.setNewHeaderColor(activity, resources.getColor(R.color.colorPrimaryDark), + resources.getColor(R.color.colorPrimary)) + } + private fun makeSessionView(session: Session) { when (session.title.isNullOrBlank()) { true -> rootView.sessionDetailName.visibility = View.GONE @@ -158,6 +192,17 @@ class SessionFragment : Fragment() { } } + val track = session.track + when (track == null) { + true -> rootView.sessionDetailTrackContainer.visibility = View.GONE + false -> { + rootView.sessionDetailTrack.text = track.name + val trackColor = Color.parseColor(track.color) + rootView.sessionDetailTrackIcon.setColorFilter(trackColor) + Utils.setNewHeaderColor(activity, trackColor) + } + } + when (session.signupUrl.isNullOrBlank()) { true -> rootView.sessionDetailSignUpButton.visibility = View.GONE false -> rootView.sessionDetailSignUpButton.setOnClickListener { diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt index fbc8dbc2d..d01c3763a 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewHolder.kt @@ -1,5 +1,6 @@ package org.fossasia.openevent.general.sessions +import android.graphics.Color import android.view.View import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView @@ -8,6 +9,9 @@ import kotlinx.android.synthetic.main.item_session.view.sessionType import kotlinx.android.synthetic.main.item_session.view.sessiontime import kotlinx.android.synthetic.main.item_session.view.shortAbstract import kotlinx.android.synthetic.main.item_session.view.title +import kotlinx.android.synthetic.main.item_session.view.trackDetail +import kotlinx.android.synthetic.main.item_session.view.trackText +import kotlinx.android.synthetic.main.item_session.view.trackIcon import org.fossasia.openevent.general.common.SessionClickListener import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.utils.nullToEmpty @@ -25,6 +29,15 @@ class SessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { session.microlocation.let { itemView.mircolocation.text = it?.name } + + session.track.let { + if (it == null) + itemView.trackDetail.visibility = View.GONE + else { + itemView.trackText.text = it.name + itemView.trackIcon.setColorFilter(Color.parseColor(it.color)) + } + } when (session.startsAt.isNullOrBlank()) { true -> itemView.sessiontime.isVisible = false false -> { diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt index b186d464f..c476b1b65 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt @@ -20,6 +20,8 @@ class SessionViewModel( private val mutableSession = MutableLiveData() val session: LiveData = mutableSession + private val mutableProgress = MutableLiveData(true) + val progress: LiveData = mutableProgress private val mutableError = SingleLiveEvent() val error: LiveData = mutableError @@ -32,11 +34,14 @@ class SessionViewModel( compositeDisposable.add(sessionService.fetchSession(id) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) + .doOnSubscribe { mutableProgress.value = true } .subscribe({ mutableSession.value = it + mutableProgress.value = false }, { Timber.e(it, "Error fetching session id $id") - mutableError.value = resource.getString(R.string.error_fetching_event_message) + mutableError.value = resource.getString(R.string.error_fetching_event_section_message, + resource.getString(R.string.session)) }) ) } diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/track/Track.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/track/Track.kt new file mode 100644 index 000000000..1ece28962 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/track/Track.kt @@ -0,0 +1,22 @@ +package org.fossasia.openevent.general.sessions.track + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import com.github.jasminb.jsonapi.LongIdHandler +import com.github.jasminb.jsonapi.annotations.Id +import com.github.jasminb.jsonapi.annotations.Type + +@Type("track") +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy::class) +@Entity +data class Track( + @Id(LongIdHandler::class) + @PrimaryKey + val id: Long, + val name: String, + val description: String?, + val color: String, + val fontColor: String? +) diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/track/TrackConverter.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/track/TrackConverter.kt new file mode 100644 index 000000000..39f996e39 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/track/TrackConverter.kt @@ -0,0 +1,15 @@ +package org.fossasia.openevent.general.sessions.track + +import androidx.room.TypeConverter +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.databind.ObjectMapper + +class TrackConverter { + + @TypeConverter + fun toTrack(json: String): Track? = + jacksonObjectMapper().readerFor(Track::class.java).readValue(json) + + @TypeConverter + fun toJson(track: Track?) = ObjectMapper().writeValueAsString(track) +} diff --git a/app/src/main/java/org/fossasia/openevent/general/utils/Utils.kt b/app/src/main/java/org/fossasia/openevent/general/utils/Utils.kt index 50c263d53..42ff9b0b4 100644 --- a/app/src/main/java/org/fossasia/openevent/general/utils/Utils.kt +++ b/app/src/main/java/org/fossasia/openevent/general/utils/Utils.kt @@ -5,6 +5,8 @@ import android.app.AlertDialog import android.app.ProgressDialog import android.content.Context import android.graphics.BitmapFactory +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.net.ConnectivityManager import android.net.Uri import android.view.View @@ -16,6 +18,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.navigation.NavOptions @@ -94,6 +97,20 @@ object Utils { } } + fun setNewHeaderColor(activity: Activity?, color: Int) { + if (activity is AppCompatActivity) { + activity.supportActionBar?.setBackgroundDrawable(ColorDrawable(color)) + activity.window.statusBarColor = ColorUtils.blendARGB(color, Color.BLACK, 0.2f) + } + } + + fun setNewHeaderColor(activity: Activity?, statusColor: Int, actionBarColor: Int) { + if (activity is AppCompatActivity) { + activity.supportActionBar?.setBackgroundDrawable(ColorDrawable(actionBarColor)) + activity.window.statusBarColor = statusColor + } + } + fun checkAndLoadFragment( fragmentManager: FragmentManager, fragment: Fragment, diff --git a/app/src/main/res/drawable/ic_track.xml b/app/src/main/res/drawable/ic_track.xml new file mode 100644 index 000000000..edb2307df --- /dev/null +++ b/app/src/main/res/drawable/ic_track.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/app/src/main/res/layout/fragment_session.xml b/app/src/main/res/layout/fragment_session.xml index 70d03b1bb..897f041c3 100644 --- a/app/src/main/res/layout/fragment_session.xml +++ b/app/src/main/res/layout/fragment_session.xml @@ -4,6 +4,15 @@ android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"> + + - + + + + + + + diff --git a/app/src/main/res/layout/item_session.xml b/app/src/main/res/layout/item_session.xml index 6edc27d9e..9dc7f0296 100644 --- a/app/src/main/res/layout/item_session.xml +++ b/app/src/main/res/layout/item_session.xml @@ -57,6 +57,26 @@ android:textColor="@color/dark_grey" android:textSize="@dimen/text_size_large" tools:text="Tuesday June 5" /> + + + + + android:textSize="@dimen/text_size_large" + tools:text="@string/description_preview"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7a93938f..75531c67d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Event host details Organizer Location + Track What\'s good in where? We\'re having difficulty connecting to the server. Check your connection or try again later. @@ -333,6 +334,7 @@ Speakers Sponsors Sessions + Session Some description about this sponsor: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. name starts-at @@ -343,5 +345,6 @@ Failed to load notifications Notifications You don\'t have any notification.. + Color Code: From 4bea92c5be0fab4330d7764e47c3298f5efa5419 Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Sun, 19 May 2019 11:40:27 +0530 Subject: [PATCH 33/35] feat: Change authentication process and remove navigation (#1749) (#1763) --- .../general/auth/SmartAuthViewModel.kt | 2 + .../openevent/general/MainActivity.kt | 10 +- .../openevent/general/auth/AuthApi.kt | 4 + .../openevent/general/auth/AuthFragment.kt | 116 ++++++++++++++++++ .../openevent/general/auth/AuthService.kt | 4 + .../openevent/general/auth/AuthViewModel.kt | 47 +++++++ .../general/auth/CheckEmailResponse.kt | 5 + .../openevent/general/auth/LoginFragment.kt | 38 +++--- .../openevent/general/auth/ProfileFragment.kt | 6 +- .../openevent/general/auth/SignUpFragment.kt | 21 +++- .../fossasia/openevent/general/di/Modules.kt | 2 + .../general/event/EventDetailsFragment.kt | 7 +- .../notification/NotificationFragment.kt | 9 +- .../general/order/OrdersUnderUserFragment.kt | 4 +- .../general/ticket/TicketsFragment.kt | 6 +- app/src/main/res/layout/activity_main.xml | 11 -- app/src/main/res/layout/fragment_auth.xml | 61 +++++++++ .../main/res/navigation/navigation_graph.xml | 79 +++++++++--- app/src/main/res/values/strings.xml | 5 + .../general/auth/SmartAuthViewModel.kt | 5 + 20 files changed, 379 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/org/fossasia/openevent/general/auth/AuthFragment.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/auth/AuthViewModel.kt create mode 100644 app/src/main/java/org/fossasia/openevent/general/auth/CheckEmailResponse.kt create mode 100644 app/src/main/res/layout/fragment_auth.xml diff --git a/app/src/fdroid/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt b/app/src/fdroid/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt index 45d1a097c..a449689b1 100644 --- a/app/src/fdroid/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt +++ b/app/src/fdroid/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt @@ -15,6 +15,8 @@ class SmartAuthViewModel : ViewModel() { val progress: LiveData = mutableProgress private val mutableApiExceptionRequestCodePair = MutableLiveData>() val apiExceptionCodePair: LiveData> = mutableApiExceptionRequestCodePair + private val mutableStatus = MutableLiveData() + val isCredentialStored: LiveData = mutableStatus fun requestCredentials(any: Any) { return diff --git a/app/src/main/java/org/fossasia/openevent/general/MainActivity.kt b/app/src/main/java/org/fossasia/openevent/general/MainActivity.kt index 3ca99dfc8..3a13a2685 100644 --- a/app/src/main/java/org/fossasia/openevent/general/MainActivity.kt +++ b/app/src/main/java/org/fossasia/openevent/general/MainActivity.kt @@ -8,7 +8,6 @@ import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI.setupWithNavController import kotlinx.android.synthetic.main.activity_main.navigation -import kotlinx.android.synthetic.main.activity_main.navigationAuth import kotlinx.android.synthetic.main.activity_main.mainFragmentCoordinatorLayout import org.fossasia.openevent.general.auth.EditProfileFragment import org.fossasia.openevent.general.auth.RC_CREDENTIALS_READ @@ -44,7 +43,6 @@ class MainActivity : AppCompatActivity() { private fun setupBottomNavigationMenu(navController: NavController) { setupWithNavController(navigation, navController) - setupWithNavController(navigationAuth, navController) navigation.setOnNavigationItemReselectedListener { val hostFragment = supportFragmentManager.findFragmentById(R.id.frameContainer) @@ -64,17 +62,11 @@ class MainActivity : AppCompatActivity() { R.id.favoriteFragment -> navAnimVisible(navigation, this@MainActivity) else -> navAnimGone(navigation, this@MainActivity) } - when (id) { - R.id.loginFragment, - R.id.signUpFragment -> navAnimVisible(navigationAuth, this@MainActivity) - else -> navAnimGone(navigationAuth, this@MainActivity) - } } override fun onBackPressed() { when (currentFragmentId) { - R.id.loginFragment, - R.id.signUpFragment -> { + R.id.authFragment -> { navController.popBackStack(R.id.eventsFragment, false) mainFragmentCoordinatorLayout.snackbar(R.string.sign_in_canceled) } diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/AuthApi.kt b/app/src/main/java/org/fossasia/openevent/general/auth/AuthApi.kt index 4f06d1dd3..2e86794e5 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/AuthApi.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/AuthApi.kt @@ -3,6 +3,7 @@ package org.fossasia.openevent.general.auth import io.reactivex.Single import org.fossasia.openevent.general.auth.change.ChangeRequestToken import org.fossasia.openevent.general.auth.change.ChangeRequestTokenResponse +import org.fossasia.openevent.general.auth.forgot.Email import org.fossasia.openevent.general.auth.forgot.RequestToken import org.fossasia.openevent.general.auth.forgot.RequestTokenResponse import retrofit2.http.Body @@ -33,4 +34,7 @@ interface AuthApi { @POST("upload/image") fun uploadImage(@Body uploadImage: UploadImage): Single + + @POST("users/checkEmail") + fun checkEmail(@Body email: Email): Single } diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/AuthFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/AuthFragment.kt new file mode 100644 index 000000000..715194c95 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/auth/AuthFragment.kt @@ -0,0 +1,116 @@ +package org.fossasia.openevent.general.auth + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.Observer +import androidx.navigation.Navigation +import androidx.navigation.fragment.navArgs +import kotlinx.android.synthetic.main.fragment_auth.view.getStartedButton +import kotlinx.android.synthetic.main.fragment_auth.view.email +import kotlinx.android.synthetic.main.fragment_auth.view.rootLayout +import org.fossasia.openevent.general.BuildConfig +import org.fossasia.openevent.general.PLAY_STORE_BUILD_FLAVOR +import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.utils.Utils +import org.fossasia.openevent.general.utils.Utils.hideSoftKeyboard +import org.fossasia.openevent.general.utils.Utils.show +import org.fossasia.openevent.general.utils.Utils.progressDialog +import org.fossasia.openevent.general.utils.extensions.nonNull +import org.jetbrains.anko.design.longSnackbar +import org.jetbrains.anko.design.snackbar +import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.viewModel + +class AuthFragment : Fragment() { + private lateinit var rootView: View + private val authViewModel by viewModel() + private val safeArgs: AuthFragmentArgs by navArgs() + private val smartAuthViewModel by sharedViewModel() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (BuildConfig.FLAVOR == PLAY_STORE_BUILD_FLAVOR) { + smartAuthViewModel.requestCredentials(SmartAuthUtil.getCredentialsClient(requireActivity())) + smartAuthViewModel.isCredentialStored + .nonNull() + .observe(this, Observer { + if (it) redirectToLogin() + }) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + rootView = inflater.inflate(R.layout.fragment_auth, container, false) + + Utils.setToolbar(activity, "", true) + setHasOptionsMenu(true) + + val progressDialog = progressDialog(context) + + val snackbarMessage = safeArgs.snackbarMessage + if (!snackbarMessage.isNullOrEmpty()) rootView.snackbar(snackbarMessage) + + rootView.getStartedButton.setOnClickListener { + hideSoftKeyboard(context, rootView) + authViewModel.checkUser(rootView.email.text.toString()) + } + + authViewModel.isUserExists + .nonNull() + .observe(viewLifecycleOwner, Observer { + if (it) + redirectToLogin(rootView.email.text.toString()) + else + redirectToSignUp() + authViewModel.mutableStatus.postValue(null) + }) + + authViewModel.progress + .nonNull() + .observe(viewLifecycleOwner, Observer { + progressDialog.show(it) + }) + + smartAuthViewModel.progress + .nonNull() + .observe(viewLifecycleOwner, Observer { + progressDialog.show(it) + }) + + authViewModel.error + .nonNull() + .observe(viewLifecycleOwner, Observer { + rootView.rootLayout.longSnackbar(it) + }) + + return rootView + } + + private fun redirectToLogin(email: String = "") { + Navigation.findNavController(rootView) + .navigate(AuthFragmentDirections + .actionAuthToLogIn(email, safeArgs.redirectedFrom) + ) + } + + private fun redirectToSignUp() { + Navigation.findNavController(rootView) + .navigate(AuthFragmentDirections + .actionAuthToSignUp(rootView.email.text.toString(), safeArgs.redirectedFrom) + ) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + activity?.onBackPressed() + true + } + else -> super.onOptionsItemSelected(item) + } + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt b/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt index f628bc3c4..8db9a02d4 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/AuthService.kt @@ -85,4 +85,8 @@ class AuthService( val changeRequestToken = ChangeRequestToken(Password(oldPassword, newPassword)) return authApi.changeRequestToken(changeRequestToken) } + + fun checkEmail(email: String): Single { + return authApi.checkEmail(Email(email)) + } } diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/AuthViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/auth/AuthViewModel.kt new file mode 100644 index 000000000..3a3d0b50b --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/auth/AuthViewModel.kt @@ -0,0 +1,47 @@ +package org.fossasia.openevent.general.auth + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import org.fossasia.openevent.general.R +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import org.fossasia.openevent.general.data.Network +import org.fossasia.openevent.general.data.Resource +import org.fossasia.openevent.general.utils.extensions.withDefaultSchedulers +import timber.log.Timber + +class AuthViewModel( + private val authService: AuthService, + private val network: Network, + private val resource: Resource +) : ViewModel() { + + private val compositeDisposable = CompositeDisposable() + private val mutableProgress = MutableLiveData() + val progress: LiveData = mutableProgress + val mutableStatus = MutableLiveData() + val isUserExists: LiveData = mutableStatus + private val mutableError = MutableLiveData() + val error: LiveData = mutableError + + fun checkUser(email: String) { + if (!network.isNetworkConnected()) { + mutableError.value = resource.getString(R.string.no_internet_message) + return + } + compositeDisposable += authService.checkEmail(email) + .withDefaultSchedulers() + .doOnSubscribe { + mutableProgress.value = true + }.doFinally { + mutableProgress.value = false + }.subscribe({ + mutableStatus.value = !it.result + Timber.d("Success!") + }, { + mutableError.value = resource.getString(R.string.error) + Timber.d(it, "Failed") + }) + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/CheckEmailResponse.kt b/app/src/main/java/org/fossasia/openevent/general/auth/CheckEmailResponse.kt new file mode 100644 index 000000000..5b5bd601c --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/auth/CheckEmailResponse.kt @@ -0,0 +1,5 @@ +package org.fossasia.openevent.general.auth + +class CheckEmailResponse( + val result: Boolean +) diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/LoginFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/LoginFragment.kt index cf547918b..32e811a05 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/LoginFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/LoginFragment.kt @@ -13,7 +13,6 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs -import kotlinx.android.synthetic.main.activity_main.navigationAuth import kotlinx.android.synthetic.main.fragment_login.email import kotlinx.android.synthetic.main.fragment_login.password import kotlinx.android.synthetic.main.fragment_login.loginButton @@ -28,6 +27,10 @@ import kotlinx.android.synthetic.main.fragment_login.view.tick import org.fossasia.openevent.general.BuildConfig import org.fossasia.openevent.general.PLAY_STORE_BUILD_FLAVOR import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.event.EVENT_DETAIL_FRAGMENT +import org.fossasia.openevent.general.notification.NOTIFICATION_FRAGMENT +import org.fossasia.openevent.general.order.ORDERS_FRAGMENT +import org.fossasia.openevent.general.ticket.TICKETS_FRAGMNET import org.fossasia.openevent.general.utils.Utils import org.fossasia.openevent.general.utils.Utils.show import org.fossasia.openevent.general.utils.Utils.hideSoftKeyboard @@ -55,7 +58,6 @@ class LoginFragment : Fragment() { val progressDialog = progressDialog(context) Utils.setToolbar(activity, getString(R.string.login)) setHasOptionsMenu(true) - showSnackbar() if (loginViewModel.isLoggedIn()) popBackStack() @@ -65,7 +67,9 @@ class LoginFragment : Fragment() { hideSoftKeyboard(context, rootView) } - if (BuildConfig.FLAVOR == PLAY_STORE_BUILD_FLAVOR) { + if (safeArgs.email.isNotEmpty()) { + rootView.email.text = SpannableStringBuilder(safeArgs.email) + } else if (BuildConfig.FLAVOR == PLAY_STORE_BUILD_FLAVOR) { smartAuthViewModel.requestCredentials(SmartAuthUtil.getCredentialsClient(requireActivity())) @@ -82,8 +86,8 @@ class LoginFragment : Fragment() { }) smartAuthViewModel.apiExceptionCodePair.nonNull().observe(viewLifecycleOwner, Observer { - SmartAuthUtil.handleResolvableApiException( - it.first, requireActivity(), it.second) + SmartAuthUtil.handleResolvableApiException( + it.first, requireActivity(), it.second) }) smartAuthViewModel.progress @@ -92,6 +96,7 @@ class LoginFragment : Fragment() { progressDialog.show(it) }) } + loginViewModel.progress .nonNull() .observe(viewLifecycleOwner, Observer { @@ -143,10 +148,8 @@ class LoginFragment : Fragment() { rootView.sentEmailLayout.visibility = View.VISIBLE rootView.loginLayout.visibility = View.GONE Utils.setToolbar(activity, show = false) - Utils.navAnimGone(activity?.navigationAuth, requireContext()) } else { Utils.setToolbar(activity, getString(R.string.login)) - Utils.navAnimVisible(activity?.navigationAuth, requireContext()) } }) @@ -165,7 +168,6 @@ class LoginFragment : Fragment() { rootView.tick.setOnClickListener { rootView.sentEmailLayout.visibility = View.GONE Utils.setToolbar(activity, getString(R.string.login)) - Utils.navAnimVisible(activity?.navigationAuth, requireContext()) rootView.loginLayout.visibility = View.VISIBLE } @@ -189,7 +191,16 @@ class LoginFragment : Fragment() { } private fun popBackStack() { - findNavController(rootView).popBackStack() + val destinationId = + when (safeArgs.redirectedFrom) { + PROFILE_FRAGMENT -> R.id.profileFragment + EVENT_DETAIL_FRAGMENT -> R.id.eventDetailsFragment + ORDERS_FRAGMENT -> R.id.orderUnderUserFragment + TICKETS_FRAGMNET -> R.id.ticketsFragment + NOTIFICATION_FRAGMENT -> R.id.notificationFragment + else -> R.id.eventsFragment + } + findNavController(rootView).popBackStack(destinationId, false) rootView.snackbar(R.string.welcome_back) } @@ -200,17 +211,10 @@ class LoginFragment : Fragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { - findNavController(rootView).popBackStack(R.id.eventsFragment, false) - rootView.snackbar(R.string.sign_in_canceled) + activity?.onBackPressed() true } else -> super.onOptionsItemSelected(item) } } - - private fun showSnackbar() { - safeArgs.snackbarMessage?.let { textSnackbar -> - rootView.loginCoordinatorLayout.snackbar(textSnackbar) - } - } } diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt index 26f252bcd..193d056e6 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt @@ -34,6 +34,8 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.snackbar +const val PROFILE_FRAGMENT = "profileFragment" + class ProfileFragment : Fragment() { private val profileViewModel by viewModel() @@ -41,9 +43,7 @@ class ProfileFragment : Fragment() { private var emailSettings: String? = null private fun redirectToLogin() { - findNavController(rootView).navigate(ProfileFragmentDirections - .actionProfileToLogin() - ) + findNavController(rootView).navigate(ProfileFragmentDirections.actionProfileToAuth(null, PROFILE_FRAGMENT)) } private fun redirectToEventsFragment() { diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/SignUpFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/SignUpFragment.kt index d7b3425ea..eded359d4 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/SignUpFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/SignUpFragment.kt @@ -39,12 +39,18 @@ import android.text.SpannableStringBuilder import android.text.TextPaint import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan +import androidx.navigation.fragment.navArgs +import org.fossasia.openevent.general.event.EVENT_DETAIL_FRAGMENT +import org.fossasia.openevent.general.notification.NOTIFICATION_FRAGMENT +import org.fossasia.openevent.general.order.ORDERS_FRAGMENT +import org.fossasia.openevent.general.ticket.TICKETS_FRAGMNET import org.jetbrains.anko.design.longSnackbar import org.jetbrains.anko.design.snackbar class SignUpFragment : Fragment() { private val signUpViewModel by viewModel() + private val safeArgs: SignUpFragmentArgs by navArgs() private lateinit var rootView: View override fun onCreateView( @@ -98,6 +104,7 @@ class SignUpFragment : Fragment() { rootView.signUpText.text = paragraph rootView.signUpText.movementMethod = LinkMovementMethod.getInstance() + rootView.usernameSignUp.text = SpannableStringBuilder(safeArgs.email) lateinit var confirmPassword: String val signUp = SignUp() @@ -247,7 +254,16 @@ class SignUpFragment : Fragment() { } private fun redirectToMain() { - findNavController(rootView).popBackStack() + val destinationId = + when (safeArgs.redirectedFrom) { + PROFILE_FRAGMENT -> R.id.profileFragment + EVENT_DETAIL_FRAGMENT -> R.id.eventDetailsFragment + ORDERS_FRAGMENT -> R.id.orderUnderUserFragment + TICKETS_FRAGMNET -> R.id.ticketsFragment + NOTIFICATION_FRAGMENT -> R.id.notificationFragment + else -> R.id.eventsFragment + } + findNavController(rootView).popBackStack(destinationId, false) rootView.snackbar(R.string.logged_in_automatically) } @@ -277,8 +293,7 @@ class SignUpFragment : Fragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { - findNavController(rootView).popBackStack(R.id.eventsFragment, false) - rootView.snackbar(R.string.sign_in_canceled) + activity?.onBackPressed() true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index 8d448b418..ade8bd4ee 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -29,6 +29,7 @@ import org.fossasia.openevent.general.auth.RequestAuthenticator import org.fossasia.openevent.general.auth.SignUp import org.fossasia.openevent.general.auth.SignUpViewModel import org.fossasia.openevent.general.auth.User +import org.fossasia.openevent.general.auth.AuthViewModel import org.fossasia.openevent.general.data.Network import org.fossasia.openevent.general.data.Preference import org.fossasia.openevent.general.event.Event @@ -222,6 +223,7 @@ val viewModelModule = module { viewModel { SpeakerViewModel(get(), get()) } viewModel { SponsorsViewModel(get(), get()) } viewModel { NotificationViewModel(get(), get(), get(), get()) } + viewModel { AuthViewModel(get(), get(), get()) } } val networkModule = module { diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt index e70864461..d71bedc2c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt @@ -80,6 +80,11 @@ import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.longSnackbar import org.jetbrains.anko.design.snackbar +const val EVENT_ID = "eventId" +const val EVENT_TOPIC_ID = "eventTopicId" +const val EVENT_LOCATION = "eventLocation" +const val EVENT_DETAIL_FRAGMENT = "eventDetailFragment;" + class EventDetailsFragment : Fragment() { private val eventViewModel by viewModel() private val safeArgs: EventDetailsFragmentArgs by navArgs() @@ -561,7 +566,7 @@ class EventDetailsFragment : Fragment() { private fun redirectToLogin() { findNavController(rootView).navigate(EventDetailsFragmentDirections - .actionEventDetailsToLogin(getString(R.string.log_in_first))) + .actionEventDetailsToAuth(getString(R.string.log_in_first), EVENT_DETAIL_FRAGMENT)) } private fun writeFeedback() { diff --git a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt index b56694182..543bf61f8 100644 --- a/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/notification/NotificationFragment.kt @@ -19,12 +19,13 @@ import kotlinx.android.synthetic.main.fragment_notification.view.notificationCoo import kotlinx.android.synthetic.main.fragment_notification.view.noNotification import org.fossasia.openevent.general.R import org.fossasia.openevent.general.auth.LoginFragmentArgs -import org.fossasia.openevent.general.utils.Utils import org.fossasia.openevent.general.utils.Utils.setToolbar import org.fossasia.openevent.general.utils.extensions.nonNull import org.jetbrains.anko.design.snackbar import org.koin.androidx.viewmodel.ext.android.viewModel +const val NOTIFICATION_FRAGMENT = "notificationFragment" + class NotificationFragment : Fragment() { private val notificationViewModel by viewModel() private val recyclerAdapter = NotificationsRecyclerAdapter() @@ -135,7 +136,11 @@ class NotificationFragment : Fragment() { LoginFragmentArgs(getString(R.string.log_in_first)) .toBundle() .also { - Navigation.findNavController(rootView).navigate(R.id.loginFragment, it, Utils.getAnimFade()) + Navigation.findNavController(rootView).navigate( + NotificationFragmentDirections.actionNotificationToAuth( + getString(R.string.log_in_first), + NOTIFICATION_FRAGMENT) + ) } } } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt index d51c62a20..c7dd5ccaa 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt @@ -32,6 +32,8 @@ import timber.log.Timber import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.longSnackbar +const val ORDERS_FRAGMENT = "ordersFragment" + class OrdersUnderUserFragment : Fragment(), ScrollToTop { private lateinit var rootView: View @@ -138,7 +140,7 @@ class OrdersUnderUserFragment : Fragment(), ScrollToTop { private fun redirectToLogin() { findNavController(rootView).navigate(OrdersUnderUserFragmentDirections - .actionOrderUserToLogin(getString(R.string.log_in_first))) + .actionOrderUserToAuth(getString(R.string.log_in_first), ORDERS_FRAGMENT)) } override fun scrollToTop() = rootView.ordersNestedScrollView.smoothScrollTo(0, 0) diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt index 0e7706026..cfed35062 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt @@ -34,6 +34,8 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.longSnackbar +const val TICKETS_FRAGMNET = "ticketsFragment" + class TicketsFragment : Fragment() { private val ticketsRecyclerAdapter: TicketsRecyclerAdapter = TicketsRecyclerAdapter() private val ticketsViewModel by viewModel() @@ -149,8 +151,8 @@ class TicketsFragment : Fragment() { } private fun redirectToLogin() { - findNavController(rootView).navigate(TicketsFragmentDirections.actionTicketsToLogin( - getString(R.string.log_in_first) + findNavController(rootView).navigate(TicketsFragmentDirections.actionTicketsToAuth( + getString(R.string.log_in_first), TICKETS_FRAGMNET )) } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d1d739721..7dc645ee2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -40,16 +40,5 @@ app:labelVisibilityMode="labeled" app:menu="@menu/navigation" /> - - diff --git a/app/src/main/res/layout/fragment_auth.xml b/app/src/main/res/layout/fragment_auth.xml new file mode 100644 index 000000000..b5429dda1 --- /dev/null +++ b/app/src/main/res/layout/fragment_auth.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/navigation_graph.xml b/app/src/main/res/navigation/navigation_graph.xml index 349f8cb16..00ee6368a 100644 --- a/app/src/main/res/navigation/navigation_graph.xml +++ b/app/src/main/res/navigation/navigation_graph.xml @@ -318,8 +318,8 @@ app:exitAnim="@anim/slide_out_left"/> - + app:nullable="false" + android:defaultValue="''"/> + + tools:layout="@layout/fragment_signup"> + + + + tools:layout="@layout/fragment_notification"> + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 75531c67d..c0fe2ca23 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -266,6 +266,11 @@ We\'ll show you what\'s good near you Pick a city + + Let\'s get started + Sign up or log in to see what\'s happening near you + Get Started + Your email address is invalid! Your password and confirmation password do not match! diff --git a/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt b/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt index 16b3fe02f..1f6832fc7 100644 --- a/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt +++ b/app/src/playStore/java/org/fossasia/openevent/general/auth/SmartAuthViewModel.kt @@ -25,6 +25,8 @@ class SmartAuthViewModel : ViewModel() { val progress: LiveData = mutableProgress private val mutableApiExceptionRequestCodePair = MutableLiveData>() val apiExceptionCodePair: LiveData> = mutableApiExceptionRequestCodePair + private val mutableStatus = MutableLiveData() + val isCredentialStored: LiveData = mutableStatus fun requestCredentials(credentialsClient: CredentialsClient) { if (requestedCredentialsEarlier) return @@ -41,7 +43,10 @@ class SmartAuthViewModel : ViewModel() { if (task.isSuccessful) { mutableId.value = task.result?.credential?.id mutablePassword.value = task.result?.credential?.password + mutableStatus.value = true return@OnCompleteListener + } else { + mutableStatus.value = false } val e = task.exception if (e is ResolvableApiException) { From a12b674a5f01347e1bcb1c15df6327cd3dd9f94d Mon Sep 17 00:00:00 2001 From: Duc Le Tran <36192582+anhanh11001@users.noreply.github.com> Date: Sun, 19 May 2019 12:50:28 +0200 Subject: [PATCH 34/35] feat: Add speakers under session (#1788) Detail: - Set up recycler/adapter to fetch speakers under session Fixes: #1786 --- .../fossasia/openevent/general/di/Modules.kt | 2 +- .../general/event/EventDetailsFragment.kt | 3 -- .../general/sessions/SessionFragment.kt | 49 +++++++++++++++++++ .../general/sessions/SessionViewModel.kt | 31 +++++++++--- .../openevent/general/speakers/SpeakerApi.kt | 3 ++ .../general/speakers/SpeakerService.kt | 6 +++ app/src/main/res/layout/fragment_session.xml | 30 ++++++++++++ .../main/res/navigation/navigation_graph.xml | 7 +++ app/src/main/res/values/strings.xml | 1 + 9 files changed, 122 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt index ade8bd4ee..441e559ac 100644 --- a/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt +++ b/app/src/main/java/org/fossasia/openevent/general/di/Modules.kt @@ -203,7 +203,7 @@ val viewModelModule = module { viewModel { ProfileViewModel(get(), get()) } viewModel { SignUpViewModel(get(), get(), get()) } viewModel { EventDetailsViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } - viewModel { SessionViewModel(get(), get()) } + viewModel { SessionViewModel(get(), get(), get()) } viewModel { SearchViewModel(get(), get(), get(), get()) } viewModel { AttendeeViewModel(get(), get(), get(), get(), get(), get(), get()) } viewModel { SearchLocationViewModel(get(), get()) } diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt index d71bedc2c..3d63b504d 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt @@ -80,9 +80,6 @@ import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.longSnackbar import org.jetbrains.anko.design.snackbar -const val EVENT_ID = "eventId" -const val EVENT_TOPIC_ID = "eventTopicId" -const val EVENT_LOCATION = "eventLocation" const val EVENT_DETAIL_FRAGMENT = "eventDetailFragment;" class EventDetailsFragment : Fragment() { diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt index 2613bf240..6de3b57e1 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionFragment.kt @@ -13,6 +13,9 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.navigation.fragment.navArgs import com.squareup.picasso.Picasso +import androidx.navigation.Navigation.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL import kotlinx.android.synthetic.main.fragment_session.view.progressBar import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTrack import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailAbstract @@ -34,7 +37,12 @@ import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailAbstrac import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTrackContainer import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailSignUpButton import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailTrackIcon +import kotlinx.android.synthetic.main.fragment_session.view.speakersUnderSessionRecycler +import kotlinx.android.synthetic.main.fragment_session.view.speakersProgressBar +import kotlinx.android.synthetic.main.fragment_session.view.sessionDetailSpeakersContainer import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.common.SpeakerClickListener +import org.fossasia.openevent.general.speakers.SpeakerRecyclerAdapter import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.utils.Utils import org.fossasia.openevent.general.utils.Utils.setToolbar @@ -47,6 +55,7 @@ const val LINE_COUNT_ABSTRACT = 3 class SessionFragment : Fragment() { private lateinit var rootView: View private val sessionViewModel by viewModel() + private val speakersAdapter = SpeakerRecyclerAdapter() private val safeArgs: SessionFragmentArgs by navArgs() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -59,6 +68,9 @@ class SessionFragment : Fragment() { .nonNull() .observe(viewLifecycleOwner, Observer { rootView.snackbar(it) + if (it == getString(R.string.error_fetching_speakers_for_session)) { + rootView.sessionDetailSpeakersContainer.visibility = View.GONE + } }) sessionViewModel.session @@ -74,11 +86,48 @@ class SessionFragment : Fragment() { rootView.sessionDetailContainer.visibility = if (it) View.GONE else View.VISIBLE }) + sessionViewModel.speakersUnderSession + .nonNull() + .observe(viewLifecycleOwner, Observer { + speakersAdapter.addAll(it) + if (it.isEmpty()) + rootView.sessionDetailSpeakersContainer.visibility = View.GONE + else + rootView.speakersProgressBar.visibility = View.GONE + }) + sessionViewModel.loadSession(safeArgs.sessionId) + val currentSpeakers = sessionViewModel.speakersUnderSession.value + if (currentSpeakers == null) + sessionViewModel.loadSpeakersUnderSession(safeArgs.sessionId) + else { + speakersAdapter.addAll(currentSpeakers) + if (currentSpeakers.isEmpty()) + rootView.sessionDetailSpeakersContainer.visibility = View.GONE + else + rootView.speakersProgressBar.visibility = View.GONE + } + + val layoutManager = LinearLayoutManager(context) + layoutManager.orientation = HORIZONTAL + rootView.speakersUnderSessionRecycler.layoutManager = layoutManager + rootView.speakersUnderSessionRecycler.adapter = speakersAdapter return rootView } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val speakerClickListener = object : SpeakerClickListener { + override fun onClick(speakerId: Long) { + findNavController(rootView).navigate(SessionFragmentDirections.actionSessionToSpeaker(speakerId)) + } + } + speakersAdapter.apply { + onSpeakerClick = speakerClickListener + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { diff --git a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt index c476b1b65..e98a73098 100644 --- a/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/sessions/SessionViewModel.kt @@ -3,17 +3,20 @@ package org.fossasia.openevent.general.sessions import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import io.reactivex.android.schedulers.AndroidSchedulers import org.fossasia.openevent.general.BuildConfig.MAPBOX_KEY import io.reactivex.disposables.CompositeDisposable -import io.reactivex.schedulers.Schedulers +import io.reactivex.rxkotlin.plusAssign import org.fossasia.openevent.general.R import org.fossasia.openevent.general.common.SingleLiveEvent import org.fossasia.openevent.general.data.Resource +import org.fossasia.openevent.general.speakers.Speaker +import org.fossasia.openevent.general.speakers.SpeakerService +import org.fossasia.openevent.general.utils.extensions.withDefaultSchedulers import timber.log.Timber class SessionViewModel( private val sessionService: SessionService, + private val speakerService: SpeakerService, private val resource: Resource ) : ViewModel() { private val compositeDisposable = CompositeDisposable() @@ -24,6 +27,8 @@ class SessionViewModel( val progress: LiveData = mutableProgress private val mutableError = SingleLiveEvent() val error: LiveData = mutableError + private val mutableSpeakers = MutableLiveData>() + val speakersUnderSession: LiveData> = mutableSpeakers fun loadSession(id: Long) { if (id == -1L) { @@ -31,9 +36,8 @@ class SessionViewModel( return } - compositeDisposable.add(sessionService.fetchSession(id) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) + compositeDisposable += sessionService.fetchSession(id) + .withDefaultSchedulers() .doOnSubscribe { mutableProgress.value = true } .subscribe({ mutableSession.value = it @@ -43,7 +47,22 @@ class SessionViewModel( mutableError.value = resource.getString(R.string.error_fetching_event_section_message, resource.getString(R.string.session)) }) - ) + } + + fun loadSpeakersUnderSession(id: Long) { + if (id == -1L) { + mutableError.value = resource.getString(R.string.error_fetching_speakers_for_session) + return + } + + compositeDisposable += speakerService.fetchSpeakerForSession(id) + .withDefaultSchedulers() + .subscribe({ + mutableSpeakers.value = it + }, { + Timber.e(it, "Error fetching speakers for session $id") + mutableError.value = resource.getString(R.string.error_fetching_speakers_for_session) + }) } fun loadMap(latitude: String, longitude: String): String { diff --git a/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerApi.kt b/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerApi.kt index 72d16c5f8..fc4ac2a89 100644 --- a/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerApi.kt +++ b/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerApi.kt @@ -9,6 +9,9 @@ interface SpeakerApi { @GET("events/{id}/speakers") fun getSpeakerForEvent(@Path("id") id: Long): Single> + @GET("sessions/{sessionId}/speakers") + fun getSpeakersForSession(@Path("sessionId") id: Long): Single> + @GET("speakers/{speaker_id}") fun getSpeakerWithId(@Path("speaker_id") id: Long): Single } diff --git a/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerService.kt b/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerService.kt index b63a97c8a..b7aab6f37 100644 --- a/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerService.kt +++ b/app/src/main/java/org/fossasia/openevent/general/speakers/SpeakerService.kt @@ -24,6 +24,12 @@ class SpeakerService( return speakerWithEventDao.getSpeakerWithEventId(id) } + fun fetchSpeakerForSession(sessionId: Long): Single> = + speakerApi.getSpeakersForSession(sessionId) + .doOnSuccess { + speakerDao.insertSpeakers(it) + } + fun fetchSpeaker(id: Long): Flowable { return speakerDao.getSpeaker(id) } diff --git a/app/src/main/res/layout/fragment_session.xml b/app/src/main/res/layout/fragment_session.xml index 897f041c3..d745bed64 100644 --- a/app/src/main/res/layout/fragment_session.xml +++ b/app/src/main/res/layout/fragment_session.xml @@ -220,6 +220,36 @@ android:background="@color/grey" /> + + + + + + + + Error fetching event Error fetching events + Error fetching speakers under this session Error Error fetching favorite events Failed to list Orders under a user From f18e2a4710b8931a6226fe97714878f74bf5a5cb Mon Sep 17 00:00:00 2001 From: Harshit Khandelwal Date: Sun, 19 May 2019 20:05:25 +0530 Subject: [PATCH 35/35] chore: Bump version code and name for release v0.3.0 (#1790) --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 62ae242f1..d3c95e952 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ android { applicationId "com.eventyay.attendee" minSdkVersion 21 targetSdkVersion 28 - versionCode 8 - versionName "0.2.1" + versionCode 9 + versionName "0.3.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled true