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 0e51f48618..bd64ee6110 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 bd64ee6110..471879cd30 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 471879cd30..55e691f10f 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 55e691f10f..87f85005d3 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 87f85005d3..853a542499 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 853a542499..ed35935e1f 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 ed35935e1f..6effc3054c 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 7d31a77ce2..463d48c339 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 9e45822d8a..f2c1aeac5f 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 c81461800f..d2dfd6e172 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 f3dcb05b58..dc64365f62 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 4738deca70..34ef8b8cbe 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 d6f20b4678..7fac21ad3a 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 3d9a8bc930..52e5243d0e 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 d7d6cbbd69..90a06a24e3 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 d3217dc311..a36856a6a7 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 05d29cdb67..b77a42065c 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 b29cf65f18..9699853874 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 196c9e868f..5bee56e3ac 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 0000000000..93be57d0b1 --- /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 437f301d0e..71d533158b 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 3287b19c10..539c2384fc 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 51d7dd1a55..0000000000 --- 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 4373ff2b08..b7d65d73b8 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 198d50ba95..acdd1bd977 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 a32fd14651..bd76a35e32 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 633cc543d6..b742d04b3d 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 b0ec403329..ba004eeff6 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 b964527343..c9f05bb35f 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 6642044377..e640ea5475 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 dd918a5354..1a8f20ea1f 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 0000000000..1e68db8f5a --- /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 0000000000..c0d530902a --- /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 6e00e50141..17e6a41623 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 0000000000..4d4f24bb24 --- /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 e68515e953..fbc8dbc2d7 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 0000000000..b186d464f8 --- /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 e601ea7bbc..9673566556 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 0000000000..a6a1fdb953 --- /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 5d3a7a7b20..be89840698 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 0000000000..f485608d6f --- /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 0000000000..180a16a034 --- /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 0000000000..70d03b1bb6 --- /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 4db0652d6b..6edc27d9e6 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 d3a4b73f03..26f252bcd4 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 c41131f4d0..7c9e03d504 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 025a8a2248..5bfcc6fc06 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 5c0f2cdd18..e7de46236d 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 62e2a98d4f..13323c7fcd 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 6effc3054c..90857563c0 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 ba004eeff6..a63743b95d 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 c9f05bb35f..d969e96b4b 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 510a376c22..771d53aa85 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 e7de46236d..bee11caf06 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 4e7124255a..c868a57ded 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 bee11caf06..26ac36448e 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 be408efcba..4eb6f186a6 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 90a06a24e3..e092bdeab9 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 0ebf0d440d..ff58e60042 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 e6530c09ae..d525d19058 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 116265dfd4..6ed5b477c3 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 b742d04b3d..7127a0d1c4 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 40bcd47266..e2f51d1a99 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 a36856a6a7..95eb754a05 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 0000000000..e433e0632c --- /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 0000000000..1c159bede4 --- /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 0000000000..b566941820 --- /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 0000000000..5cd32b1c9e --- /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 0000000000..d8dcc81622 --- /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 0000000000..17de5f0363 --- /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 0000000000..227f62ba0d --- /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 0000000000..6b0bba0649 --- /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 0000000000..2ad953fe29 --- /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 0000000000..d03fb6cd55 --- /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 0000000000..d48ad7b0d7 --- /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 0000000000..c2352d5649 --- /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 0000000000..d2c0428a7f --- /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 63f50071ca..e8c58a4fbc 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 8f68b6e903..26281dfdd0 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 123a72034e..252665b423 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 2ad6f94df2..907874197e 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 26ac36448e..32693adf8b 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 7c9e03d504..125c3e1816 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 8bb8addd53..99bc42942c 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 90857563c0..c1629a4a51 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 5f4f5129fa..ca25da4352 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 e6b9c532bc..f87cb0e5b0 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 9d0195a4c0..3e4034c777 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 c1629a4a51..a12607890e 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 b7d65d73b8..d25d70b5dc 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 c460e1e3eb..463dc13f8e 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 3481f821c2..c5f128a361 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 e693708074..7cf66b7c06 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 7127a0d1c4..2c3354756e 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 95eb754a05..7937da2757 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 9b63a65165..62e1c03fab 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 81d8e4ba1d..fb6f7a9f58 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 52e5243d0e..6e7efd6606 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 ef77fdc8c9..c26a90790b 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 1ba83b2d73..c1d2ae383d 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 a12607890e..df09df897e 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 df09df897e..85a05a788e 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 85a05a788e..0979c570a7 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 c868a57ded..c88a8b30d3 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 d969e96b4b..fd69b32c1e 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 6e7efd6606..345b7391c0 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 c644d21c6f..2b8aac0885 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 c47e48cab8..b5df7c86b0 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 53d3d1c8f4..cff6378ca4 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 e7ddce7f05..16b3fe02f1 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 0979c570a7..6f4d27ac23 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 e640ea5475..dfe85f8a82 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 e2f51d1a99..ac53d675a7 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 e092bdeab9..36aba5fe65 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 2c3354756e..eefd488202 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 a63743b95d..e708644619 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 fd69b32c1e..9ce8dbdc6f 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 dfe85f8a82..cbf658a098 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 62e1c03fab..0000000000 --- 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 432e5708c4..0000000000 --- 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 9c728e797c..0000000000 --- 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 33cc155fcb..be8dadca46 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 0239b2a14a..23cc5cebca 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 dd9857325e..0000000000 --- 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 9edcd21687..9429a8c57d 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 35e1c77884..5ea8871464 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 b957689b5f..0000000000 --- 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 775c6a79ac..0000000000 --- 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 e8c58a4fbc..8f78aea877 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 32693adf8b..d7a93938f4 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 6f4d27ac23..4b60798d34 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 4b60798d34..62ae242f15 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 acdd1bd977..340e8fcf26 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 941448e685..976bd5b570 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 ae8ffb36d2..7158699785 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 adcb0787e9..0f1bbcfaa5 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 c249341f6e..f628bc3c49 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 eefd488202..24bce8364a 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 c65685a9b3..66c623483e 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 cbf658a098..8ced85a905 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 6ed5b477c3..5c19a8a665 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 35b2a67c53..2214378142 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 463d48c339..4627a63ca2 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 f2c1aeac5f..50d89d8b8c 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 dc64365f62..0921f049c2 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 99daff01b6..415e066901 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 ab7fe3b53c..55117b93c5 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 5bee56e3ac..d51c62a204 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 47815b5f01..363be89612 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 3571f7b9c0..4128b623d8 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 6e0028fb91..f2825ee50a 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 8f78aea877..349f8cb16a 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 340e8fcf26..f2b365afa9 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 24bce8364a..8d448b4186 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 7f70782812..0000000000 --- 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 1a8f20ea1f..a373b51541 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 70088e26e9..9aa14f07e9 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 c0d530902a..2613bf2401 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 fbc8dbc2d7..d01c3763a2 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 b186d464f8..c476b1b658 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 0000000000..1ece28962d --- /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 0000000000..39f996e391 --- /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 50c263d53d..42ff9b0b43 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 0000000000..edb2307df5 --- /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 70d03b1bb6..897f041c33 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 6edc27d9e6..9dc7f02960 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 d7a93938f4..75531c67d3 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 45d1a097c5..a449689b1e 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 3ca99dfc82..3a13a2685f 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 4f06d1dd31..2e86794e5b 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 0000000000..715194c95c --- /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 f628bc3c49..8db9a02d4c 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 0000000000..3a3d0b50bb --- /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 0000000000..5b5bd601ce --- /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 cf547918b7..32e811a057 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 26f252bcd4..193d056e66 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 d7b3425ea5..eded359d4a 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 8d448b4186..ade8bd4ee4 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 e708644619..d71bedc2c1 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 b566941820..543bf61f8d 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 d51c62a204..c7dd5ccaa5 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 0e77060269..cfed350625 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 d1d7397210..7dc645ee21 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 0000000000..b5429dda1e --- /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 349f8cb16a..00ee6368ab 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 75531c67d3..c0fe2ca236 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 16b3fe02f1..1f6832fc70 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 ade8bd4ee4..441e559ac8 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 d71bedc2c1..3d63b504d7 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 2613bf2401..6de3b57e19 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 c476b1b658..e98a730989 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 72d16c5f89..fc4ac2a893 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 b63a97c8a8..b7aab6f37b 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 897f041c33..d745bed64f 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 62ae242f15..d3c95e952d 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