mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-07-31 19:20:34 +00:00
Refactor party fragmetns
This commit is contained in:
parent
3da46b9c99
commit
ed05b34706
12 changed files with 79 additions and 111 deletions
|
|
@ -77,7 +77,7 @@
|
|||
style="@style/Base.Widget.AppCompat.Button.Borderless"
|
||||
android:text="@string/expand_notes"
|
||||
android:padding="0dp"
|
||||
android:textColor="?colorAccent"/>
|
||||
android:textColor="?colorAccent" />
|
||||
<TextView
|
||||
android:id="@+id/approvalRequiredTextField"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -45,6 +45,14 @@
|
|||
android:name="tabToOpen"
|
||||
app:argType="integer"
|
||||
android:defaultValue="0"/>
|
||||
<argument
|
||||
android:name="partyID"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/noPartyFragment"
|
||||
android:name="com.habitrpg.android.habitica.ui.fragments.social.NoPartyFragmentFragment"
|
||||
android:label="@string/sidebar_party">
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/questDetailFragment"
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewF
|
|||
import com.habitrpg.android.habitica.ui.fragments.skills.SkillsFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.NoPartyFragmentFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GuildDetailFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GuildFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GuildsOverviewFragment;
|
||||
|
|
@ -185,7 +185,7 @@ public interface UserComponent {
|
|||
|
||||
void inject(ChatListFragment chatListFragment);
|
||||
|
||||
void inject(GroupInformationFragment groupInformationFragment);
|
||||
void inject(NoPartyFragmentFragment noPartyFragmentFragment);
|
||||
|
||||
void inject(GuildFragment guildFragment);
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
|
|||
} else if (user?.isValid == true && user?.profile != null) {
|
||||
toolbarTitleTextView.text = destination.label
|
||||
}
|
||||
drawerFragment?.setSelection(destination.id, false)
|
||||
drawerFragment?.setSelection(destination.id, null, false)
|
||||
}
|
||||
MainNavigationController.setup(navigationController)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private val itemSelectedEvents = PublishSubject.create<Int>()
|
||||
private val itemSelectedEvents = PublishSubject.create<HabiticaDrawerItem>()
|
||||
|
||||
|
||||
fun getItemSelectionEvents(): Flowable<Int> = itemSelectedEvents.toFlowable(BackpressureStrategy.DROP)
|
||||
fun getItemSelectionEvents(): Flowable<HabiticaDrawerItem> = itemSelectedEvents.toFlowable(BackpressureStrategy.DROP)
|
||||
|
||||
fun getItemWithTransitionId(transitionId: Int): HabiticaDrawerItem? =
|
||||
items.find { it.transitionId == transitionId }
|
||||
|
|
@ -73,7 +73,7 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
|
|||
itemHolder?.tintColor = tintColor
|
||||
itemHolder?.backgroundTintColor = backgroundTintColor
|
||||
itemHolder?.bind(drawerItem, drawerItem.transitionId == selectedItem)
|
||||
itemHolder?.itemView?.setOnClickListener { itemSelectedEvents.onNext(drawerItem.transitionId) }
|
||||
itemHolder?.itemView?.setOnClickListener { itemSelectedEvents.onNext(drawerItem) }
|
||||
} else if (getItemViewType(position) == 1) {
|
||||
(holder as? SectionHeaderViewHolder)?.backgroundTintColor = backgroundTintColor
|
||||
(holder as? SectionHeaderViewHolder)?.bind(drawerItem)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
|
|
@ -20,6 +21,7 @@ import com.habitrpg.android.habitica.extensions.getRemainingString
|
|||
import com.habitrpg.android.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestContent
|
||||
|
|
@ -167,7 +169,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
initializeMenuItems()
|
||||
|
||||
subscriptions?.add(adapter.getItemSelectionEvents().subscribe(Consumer {
|
||||
setSelection(it, true)
|
||||
setSelection(it.transitionId, it.bundle, true)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
subscriptions?.add(socialRepository.getGroup(Group.TAVERN_ID)
|
||||
|
|
@ -240,7 +242,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
if (user.isSubscribed && user.purchased?.plan?.dateTerminated != null) {
|
||||
val terminatedCalendar = Calendar.getInstance()
|
||||
terminatedCalendar.time = user.purchased?.plan?.dateTerminated ?: Date()
|
||||
val msDiff = Calendar.getInstance().timeInMillis - terminatedCalendar.timeInMillis
|
||||
val msDiff = terminatedCalendar.timeInMillis - Calendar.getInstance().timeInMillis
|
||||
val daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff)
|
||||
if (daysDiff <= 30) {
|
||||
context?.let {
|
||||
|
|
@ -269,6 +271,14 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
it.showBubble = user.flags?.newStuff ?: false
|
||||
}
|
||||
|
||||
val partyMenuItem = getItemWithIdentifier(SIDEBAR_PARTY)
|
||||
if (user.hasParty() && partyMenuItem?.bundle == null) {
|
||||
partyMenuItem?.transitionId = R.id.partyFragment
|
||||
partyMenuItem?.bundle = bundleOf(Pair("partyID", user.party?.id))
|
||||
} else if (!user.hasParty()) {
|
||||
partyMenuItem?.transitionId = R.id.noPartyFragment
|
||||
partyMenuItem?.bundle = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
@ -312,7 +322,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
adapter.updateItems(items)
|
||||
}
|
||||
|
||||
fun setSelection(transitionId: Int?, openSelection: Boolean = true) {
|
||||
fun setSelection(transitionId: Int?, bundle: Bundle? = null, openSelection: Boolean = true) {
|
||||
adapter.selectedItem = transitionId
|
||||
closeDrawer()
|
||||
|
||||
|
|
@ -320,10 +330,11 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
return
|
||||
}
|
||||
|
||||
val activity = activity as? MainActivity
|
||||
if (activity != null) {
|
||||
if (transitionId != null) {
|
||||
activity.navigate(transitionId)
|
||||
if (transitionId != null) {
|
||||
if (bundle != null) {
|
||||
MainNavigationController.navigate(transitionId, bundle)
|
||||
} else {
|
||||
MainNavigationController.navigate(transitionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import com.google.firebase.analytics.FirebaseAnalytics
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.SocialRepository
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
|
|
@ -28,25 +27,23 @@ import com.habitrpg.android.habitica.models.social.Group
|
|||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
|
||||
import com.habitrpg.android.habitica.ui.helpers.setMarkdown
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.functions.Consumer
|
||||
import kotlinx.android.synthetic.main.fragment_group_info.*
|
||||
import kotlinx.android.synthetic.main.fragment_no_party.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
class GroupInformationFragment : BaseFragment() {
|
||||
class NoPartyFragmentFragment : BaseMainFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
|
||||
var group: Group? = null
|
||||
|
|
@ -54,26 +51,18 @@ class GroupInformationFragment : BaseFragment() {
|
|||
field = value
|
||||
updateGroup(value)
|
||||
}
|
||||
private var user: User? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
|
||||
inflater.inflate(R.layout.fragment_group_info, container, false)
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
hidesToolbar = true
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
return inflater.inflate(R.layout.fragment_no_party, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
||||
refreshLayout?.setOnRefreshListener { this.refresh() }
|
||||
|
||||
if (user != null) {
|
||||
setUser(user)
|
||||
} else {
|
||||
compositeSubscription.add(userRepository.getUser().subscribe(Consumer {
|
||||
user = it
|
||||
setUser(user)
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
updateGroup(group)
|
||||
|
||||
buttonPartyInviteAccept.setOnClickListener {
|
||||
|
|
@ -82,9 +71,11 @@ class GroupInformationFragment : BaseFragment() {
|
|||
socialRepository.joinGroup(userId)
|
||||
.doOnNext { setInvitation(null) }
|
||||
.flatMap { userRepository.retrieveUser(false) }
|
||||
.flatMap { socialRepository.retrieveGroup("party") }
|
||||
.flatMap<List<Member>> { group1 -> socialRepository.retrieveGroupMembers(group1.id, true) }
|
||||
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
|
||||
.subscribe(Consumer {
|
||||
fragmentManager?.popBackStack()
|
||||
MainNavigationController.navigate(R.id.partyFragment,
|
||||
bundleOf(Pair("partyID", user?.party?.id)))
|
||||
}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +134,13 @@ class GroupInformationFragment : BaseFragment() {
|
|||
MainNavigationController.navigate(R.id.guildFragment, bundleOf("groupID" to "f2db2a7f-13c5-454d-b3ee-ea1f5089e601"))
|
||||
}
|
||||
}
|
||||
|
||||
if (group == null && user?.invitations?.party?.id != null) {
|
||||
setInvitation(user?.invitations?.party)
|
||||
} else {
|
||||
setInvitation(null)
|
||||
}
|
||||
username_textview.text = user?.formattedUsername
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
|
|
@ -162,6 +160,9 @@ class GroupInformationFragment : BaseFragment() {
|
|||
userRepository.retrieveUser(false)
|
||||
}
|
||||
.subscribe(Consumer {
|
||||
fragmentManager?.popBackStack()
|
||||
MainNavigationController.navigate(R.id.partyFragment,
|
||||
bundleOf(Pair("partyID", user?.party?.id)))
|
||||
}, RxErrorHandler.handleEmptyError())
|
||||
} else {
|
||||
this.socialRepository.updateGroup(this.group,
|
||||
|
|
@ -189,15 +190,6 @@ class GroupInformationFragment : BaseFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setUser(user: User?) {
|
||||
if (group == null && user?.invitations?.party?.id != null) {
|
||||
setInvitation(user.invitations?.party)
|
||||
} else {
|
||||
setInvitation(null)
|
||||
}
|
||||
username_textview.text = user?.formattedUsername
|
||||
}
|
||||
|
||||
private fun setInvitation(invitation: PartyInvite?) {
|
||||
invitationWrapper.visibility = if (invitation == null) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
|
@ -233,10 +225,10 @@ class GroupInformationFragment : BaseFragment() {
|
|||
|
||||
companion object {
|
||||
|
||||
fun newInstance(group: Group?, user: User?): GroupInformationFragment {
|
||||
fun newInstance(group: Group?, user: User?): NoPartyFragmentFragment {
|
||||
val args = Bundle()
|
||||
|
||||
val fragment = GroupInformationFragment()
|
||||
val fragment = NoPartyFragmentFragment()
|
||||
fragment.arguments = args
|
||||
fragment.group = group
|
||||
fragment.user = user
|
||||
|
|
@ -290,10 +290,14 @@ class PartyDetailFragment : BaseFragment() {
|
|||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setMessage(R.string.leave_party_confirmation)
|
||||
alert.addButton(R.string.keep_challenges, true) { _, _ ->
|
||||
viewModel?.leaveGroup(true) { }
|
||||
viewModel?.leaveGroup(true) {
|
||||
fragmentManager?.popBackStack()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.leave_challenges, true) { _, _ ->
|
||||
viewModel?.leaveGroup(false) { }
|
||||
viewModel?.leaveGroup(false) {
|
||||
fragmentManager?.popBackStack()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.no, false)
|
||||
alert.show()
|
||||
|
|
|
|||
|
|
@ -11,25 +11,22 @@ import androidx.lifecycle.ViewModelProviders
|
|||
import androidx.viewpager.widget.ViewPager
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.GroupInviteActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.ChatFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.bindView
|
||||
import com.habitrpg.android.habitica.ui.helpers.resetViews
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewType
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.PartyViewModel
|
||||
import io.reactivex.functions.Consumer
|
||||
import java.util.*
|
||||
|
||||
|
||||
class PartyFragment : BaseMainFragment() {
|
||||
|
||||
private var detailFragment: PartyDetailFragment? = null
|
||||
private val viewPager: ViewPager? by bindView(R.id.viewPager)
|
||||
private var firstFragment: Fragment? = null
|
||||
private var chatFragment: ChatFragment? = null
|
||||
private var viewPagerAdapter: FragmentPagerAdapter? = null
|
||||
|
||||
|
|
@ -56,53 +53,25 @@ class PartyFragment : BaseMainFragment() {
|
|||
updateGroupUI(it)
|
||||
})
|
||||
|
||||
viewModel.loadPartyID()
|
||||
|
||||
compositeSubscription.add(viewModel.groupIDFlowable.map { it.value != null }
|
||||
.distinctUntilChanged()
|
||||
.subscribe(Consumer {
|
||||
val fragment = firstFragment
|
||||
if (fragment != null) {
|
||||
childFragmentManager.beginTransaction().remove(fragment).commit()
|
||||
}
|
||||
viewPager?.adapter?.notifyDataSetChanged()
|
||||
|
||||
if (it) {
|
||||
viewModel.retrieveGroup {}
|
||||
tabLayout?.visibility = View.VISIBLE
|
||||
} else {
|
||||
tabLayout?.visibility = View.GONE
|
||||
}
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
viewPager?.currentItem = 0
|
||||
|
||||
setViewPagerAdapter()
|
||||
setFragments()
|
||||
|
||||
arguments?.let {
|
||||
val args = PartyFragmentArgs.fromBundle(it)
|
||||
viewPager?.currentItem = args.tabToOpen
|
||||
if (args.partyID.isNotEmpty()) {
|
||||
viewModel.setGroupID(args.partyID)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.loadPartyID()
|
||||
|
||||
this.tutorialStepIdentifier = "party"
|
||||
this.tutorialText = getString(R.string.tutorial_party)
|
||||
}
|
||||
|
||||
private fun setFragments() {
|
||||
val fragments = childFragmentManager.fragments
|
||||
for (childFragment in fragments) {
|
||||
if (childFragment is ChatFragment) {
|
||||
chatFragment = childFragment
|
||||
chatFragment?.viewModel = viewModel
|
||||
}
|
||||
if (childFragment is PartyDetailFragment) {
|
||||
firstFragment = childFragment
|
||||
childFragment.viewModel = viewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectFragment(component: UserComponent) {
|
||||
component.inject(this)
|
||||
}
|
||||
|
|
@ -148,7 +117,7 @@ class PartyFragment : BaseMainFragment() {
|
|||
return true
|
||||
}
|
||||
R.id.menu_guild_leave -> {
|
||||
(firstFragment as? PartyDetailFragment)?.leaveParty()
|
||||
(detailFragment as? PartyDetailFragment)?.leaveParty()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -217,14 +186,9 @@ class PartyFragment : BaseMainFragment() {
|
|||
override fun getItem(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> {
|
||||
firstFragment = if (user?.hasParty() == true) {
|
||||
val detailFragment = PartyDetailFragment()
|
||||
detailFragment.viewModel = viewModel
|
||||
detailFragment
|
||||
} else {
|
||||
GroupInformationFragment.newInstance(null, user)
|
||||
}
|
||||
firstFragment
|
||||
detailFragment = PartyDetailFragment()
|
||||
detailFragment?.viewModel = viewModel
|
||||
detailFragment
|
||||
}
|
||||
1 -> {
|
||||
if (chatFragment == null) {
|
||||
|
|
@ -239,29 +203,16 @@ class PartyFragment : BaseMainFragment() {
|
|||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return if (user?.isValid != true || user?.hasParty() != true) {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return when (position) {
|
||||
0 -> context?.getString(R.string.party)
|
||||
1 -> context?.getString(R.string.chat)
|
||||
2 -> context?.getString(R.string.members)
|
||||
else -> ""
|
||||
} ?: ""
|
||||
}
|
||||
|
||||
override fun getItemPosition(fragment: Any): Int {
|
||||
return if ((fragment is GroupInformationFragment && user?.hasParty() == true) || (fragment is PartyDetailFragment && user?.hasParty() != true)) {
|
||||
POSITION_NONE
|
||||
} else {
|
||||
POSITION_UNCHANGED
|
||||
}
|
||||
}
|
||||
}
|
||||
this.viewPager?.adapter = viewPagerAdapter
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package com.habitrpg.android.habitica.ui.menu
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
|
||||
data class HabiticaDrawerItem(val transitionId: Int, val identifier: String, val text: String, val isHeader: Boolean = false, var additionalInfoAsPill: Boolean = true) {
|
||||
data class HabiticaDrawerItem(var transitionId: Int, val identifier: String, val text: String, val isHeader: Boolean = false, var additionalInfoAsPill: Boolean = true) {
|
||||
constructor(transitionId: Int, identifier: String) : this(transitionId, identifier, "")
|
||||
|
||||
var bundle: Bundle? = null
|
||||
var isPromo: Boolean = false
|
||||
var additionalInfo: String? = null
|
||||
var additionalInfoTextColor: Int? = null
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ open class GroupViewModel : BaseViewModel() {
|
|||
}
|
||||
|
||||
fun setGroupID(groupID: String) {
|
||||
if (groupID == groupIDSubject.value?.value) return
|
||||
groupIDSubject.onNext(groupID.asOptional())
|
||||
|
||||
disposable.add(notificationsManager.getNotifications().firstElement().map { it.filter { notification ->
|
||||
|
|
|
|||
Loading…
Reference in a new issue