Compare commits
4 Commits
v3.7.1
...
v3.7.1_gam
| Author | SHA1 | Date | |
|---|---|---|---|
| 045dea265d | |||
| 998cf70723 | |||
| eb112b358e | |||
| b860ab912a |
33
app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt
Normal file
33
app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import com.gh.gamecenter.entity.Display
|
||||
import com.gh.gamecenter.entity.SubjectRecommendEntity
|
||||
|
||||
object HomeBottomBarHelper {
|
||||
private const val GAME_BAR_KEY = "game_bar_key"
|
||||
|
||||
@JvmStatic
|
||||
fun getDefaultGameBarData(): SubjectRecommendEntity {
|
||||
try {
|
||||
val json = SPUtils.getString(GAME_BAR_KEY)
|
||||
if (json.isNotEmpty()) {
|
||||
return GsonUtils.fromJson(json, SubjectRecommendEntity::class.java)
|
||||
}
|
||||
} catch (ignore: Exception) {
|
||||
|
||||
}
|
||||
return SubjectRecommendEntity(link = "5de21b5d75e6fa054f784882",
|
||||
type = "block",
|
||||
text = "游戏库",
|
||||
name = "游戏库",
|
||||
iconSelect = "https://resource.ghzs.com/image/game/library/entrance/5de3a2ac2ab874001c5ff0e9.png",
|
||||
iconUnselect = "https://resource.ghzs.com/image/game/library/entrance/5de3a2b16b6b89001d48e779.png",
|
||||
default = false,
|
||||
display = Display())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun updateDefaultGameBarData(data: SubjectRecommendEntity) {
|
||||
SPUtils.setString(GAME_BAR_KEY, data.toJson())
|
||||
}
|
||||
}
|
||||
@ -25,39 +25,47 @@ abstract class DiffUtilAdapter<DataType>(context: Context) : BaseRecyclerAdapter
|
||||
return
|
||||
}
|
||||
object : AsyncTask<Void, Void, DiffUtil.DiffResult>() {
|
||||
override fun doInBackground(vararg voids: Void): DiffUtil.DiffResult {
|
||||
return DiffUtil.calculateDiff(object : DiffUtil.Callback() {
|
||||
override fun getOldListSize(): Int {
|
||||
return mDataList.size
|
||||
}
|
||||
override fun doInBackground(vararg voids: Void): DiffUtil.DiffResult? {
|
||||
try {
|
||||
return DiffUtil.calculateDiff(object : DiffUtil.Callback() {
|
||||
override fun getOldListSize(): Int {
|
||||
return mDataList.size
|
||||
}
|
||||
|
||||
override fun getNewListSize(): Int {
|
||||
return updateData.size
|
||||
}
|
||||
override fun getNewListSize(): Int {
|
||||
return updateData.size
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
if (oldItemPosition >= mDataList.size) return false
|
||||
if (newItemPosition >= updateData.size) return false
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
if (oldItemPosition >= mDataList.size) return false
|
||||
if (newItemPosition >= updateData.size) return false
|
||||
|
||||
val oldItem = mDataList[oldItemPosition]
|
||||
val newItem = updateData[newItemPosition]
|
||||
return areItemsTheSame(oldItem, newItem)
|
||||
}
|
||||
val oldItem = mDataList[oldItemPosition]
|
||||
val newItem = updateData[newItemPosition]
|
||||
return areItemsTheSame(oldItem, newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
if (oldItemPosition >= mDataList.size) return false
|
||||
if (newItemPosition >= updateData.size) return false
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
if (oldItemPosition >= mDataList.size) return false
|
||||
if (newItemPosition >= updateData.size) return false
|
||||
|
||||
val oldItem = mDataList[oldItemPosition]
|
||||
val newItem = updateData[newItemPosition]
|
||||
return areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
})
|
||||
val oldItem = mDataList[oldItemPosition]
|
||||
val newItem = updateData[newItemPosition]
|
||||
return areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
})
|
||||
} catch (ignore: Exception) {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(diffResult: DiffUtil.DiffResult) {
|
||||
override fun onPostExecute(diffResult: DiffUtil.DiffResult?) {
|
||||
mDataList = ArrayList(updateData)
|
||||
diffResult.dispatchUpdatesTo(this@DiffUtilAdapter)
|
||||
if (diffResult != null) {
|
||||
diffResult.dispatchUpdatesTo(this@DiffUtilAdapter)
|
||||
} else {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
package com.gh.gamecenter.fragment;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.base.OnDoubleTapListener;
|
||||
import com.gh.base.fragment.BaseFragment_ViewPager_Checkable;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.ImageUtils;
|
||||
import com.gh.common.util.HomeBottomBarHelper;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.db.GameTrendsDao;
|
||||
import com.gh.gamecenter.db.info.GameTrendsInfo;
|
||||
@ -31,6 +33,7 @@ import com.gh.gamecenter.manager.UserManager;
|
||||
import com.gh.gamecenter.personal.PersonalFragment;
|
||||
import com.gh.gamecenter.qa.CommunityFragment;
|
||||
import com.lightgame.view.NoScrollableViewPager;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
@ -57,7 +60,9 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable {
|
||||
@BindView(R.id.main_tab_community)
|
||||
View mTabCommunity;
|
||||
@BindView(R.id.main_tab_game_icon)
|
||||
SimpleDraweeView mTabGameIcon;
|
||||
ImageView mTabGameIcon;
|
||||
@BindView(R.id.main_tab_game_name)
|
||||
TextView mTabGameName;
|
||||
|
||||
@BindView(R.id.iv_discovery_hint_dot)
|
||||
protected View mDiscoveryHintIv;
|
||||
@ -115,23 +120,20 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable {
|
||||
super.onCreate(savedInstanceState);
|
||||
mGameTrendsDao = new GameTrendsDao(getContext());
|
||||
|
||||
SubjectRecommendEntity defaultGameBarData = HomeBottomBarHelper.getDefaultGameBarData();
|
||||
if (TextUtils.isEmpty(defaultGameBarData.getLink())) {
|
||||
mMainTab.setVisibility(View.GONE);
|
||||
} else {
|
||||
mMainTab.setVisibility(View.VISIBLE);
|
||||
mTabGameName.setText(defaultGameBarData.getName());
|
||||
Picasso.with(getContext())
|
||||
.load(Uri.parse(defaultGameBarData.getIconUnselect()))
|
||||
.placeholder(R.drawable.ic_game_unselect)
|
||||
.into(mTabGameIcon);
|
||||
}
|
||||
|
||||
mViewModel = ViewModelProviders.of(this).get(MainWrapperViewModel.class);
|
||||
|
||||
mViewModel.getNavBar().observe(this, navBarEntity -> {
|
||||
if (!isAdded()) return;
|
||||
FragmentManager fragmentManager = mGameWrapperFragment.getChildFragmentManager();
|
||||
List<Fragment> fragments = fragmentManager.getFragments();
|
||||
for (Fragment fragment : fragments) {
|
||||
if (fragment instanceof GameFragment) {
|
||||
((GameFragment) fragment).initPage(navBarEntity);
|
||||
mMainTab.setVisibility(View.VISIBLE);
|
||||
ImageUtils.display(mTabGameIcon, navBarEntity.getIconUnselect());
|
||||
if (navBarEntity.getDefault()) setCurrentItem(INDEX_GAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.getNavBar().observe(this, this::updateGameBarContent);
|
||||
mViewModel.getConcernData().observe(this, concernData -> {
|
||||
if (concernData != null && concernData.size() > 0) {
|
||||
ConcernEntity entity = concernData.get(0);
|
||||
@ -146,6 +148,24 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable {
|
||||
});
|
||||
}
|
||||
|
||||
private void updateGameBarContent(SubjectRecommendEntity navBarEntity) {
|
||||
if (navBarEntity != null) {
|
||||
Fragment contentFragment = mGameWrapperFragment.getContentFragment();
|
||||
if (contentFragment instanceof GameFragment) {
|
||||
((GameFragment) contentFragment).initPage(navBarEntity);
|
||||
mMainTab.setVisibility(View.VISIBLE);
|
||||
mTabGameName.setText(navBarEntity.getName());
|
||||
Picasso.with(getContext())
|
||||
.load(Uri.parse(navBarEntity.getIconUnselect()))
|
||||
.placeholder(R.drawable.ic_game_unselect)
|
||||
.into(mTabGameIcon);
|
||||
if (navBarEntity.getDefault()) setCurrentItem(INDEX_GAME);
|
||||
}
|
||||
} else {
|
||||
mMainTab.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@ -213,9 +233,15 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable {
|
||||
SubjectRecommendEntity navBarEntity = mViewModel.getNavBar().getValue();
|
||||
if (navBarEntity != null) {
|
||||
if (index == INDEX_GAME) {
|
||||
ImageUtils.display(mTabGameIcon, navBarEntity.getIconSelect());
|
||||
Picasso.with(getContext())
|
||||
.load(Uri.parse(navBarEntity.getIconSelect()))
|
||||
.placeholder(R.drawable.ic_game_unselect)
|
||||
.into(mTabGameIcon);
|
||||
} else {
|
||||
ImageUtils.display(mTabGameIcon, navBarEntity.getIconUnselect());
|
||||
Picasso.with(getContext())
|
||||
.load(Uri.parse(navBarEntity.getIconUnselect()))
|
||||
.placeholder(R.drawable.ic_game_unselect)
|
||||
.into(mTabGameIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.HomeBottomBarHelper
|
||||
import com.gh.gamecenter.entity.ConcernEntity
|
||||
import com.gh.gamecenter.entity.SubjectRecommendEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
@ -14,6 +15,7 @@ import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import retrofit2.HttpException
|
||||
|
||||
class MainWrapperViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val mApi = RetrofitManager.getInstance(getApplication()).api
|
||||
@ -37,6 +39,14 @@ class MainWrapperViewModel(application: Application) : AndroidViewModel(applicat
|
||||
.subscribe(object : BiResponse<SubjectRecommendEntity>() {
|
||||
override fun onSuccess(data: SubjectRecommendEntity) {
|
||||
navBar.postValue(data)
|
||||
HomeBottomBarHelper.updateDefaultGameBarData(data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
if (exception is HttpException && exception.code() == 404) {
|
||||
navBar.postValue(null)
|
||||
HomeBottomBarHelper.updateDefaultGameBarData(SubjectRecommendEntity())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -46,7 +56,7 @@ class MainWrapperViewModel(application: Application) : AndroidViewModel(applicat
|
||||
val currentTimeMills = System.currentTimeMillis()
|
||||
if (currentTimeMills - mLastRequestDiscoveryTime > mIntervalRequestDiscoveryData || immediately) {
|
||||
mLastRequestDiscoveryTime = currentTimeMills
|
||||
mApi
|
||||
mApi
|
||||
.getZiXunConcern(UserManager.getInstance().userId, 1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<List<ConcernEntity>>() {
|
||||
|
||||
@ -27,11 +27,24 @@ public class SearchToolWrapperFragment extends BaseFragment {
|
||||
public final static String WRAPPER_FRAGMENT_NAME = "WrapperFragmentName";
|
||||
|
||||
private SearchToolbarFragment mSearchToolbarFragment;
|
||||
private Fragment mContentFragment;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mSearchToolbarFragment = new SearchToolbarFragment();
|
||||
try {
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments != null) {
|
||||
String className = Objects.requireNonNull(arguments.getString(WRAPPER_FRAGMENT_NAME));
|
||||
mContentFragment = (Fragment) Class.forName(className).newInstance();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.wrapper_toolbar, mSearchToolbarFragment)
|
||||
.replace(R.id.wrapper_main_content, Objects.requireNonNull(mContentFragment)).commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -42,19 +55,6 @@ public class SearchToolWrapperFragment extends BaseFragment {
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
Fragment fragment = null;
|
||||
try {
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments != null) {
|
||||
String className = Objects.requireNonNull(arguments.getString(WRAPPER_FRAGMENT_NAME));
|
||||
fragment = (Fragment) Class.forName(className).newInstance();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.wrapper_toolbar, mSearchToolbarFragment)
|
||||
.replace(R.id.wrapper_main_content, Objects.requireNonNull(fragment)).commitAllowingStateLoss();
|
||||
setSearchHints();
|
||||
}
|
||||
|
||||
@ -73,6 +73,9 @@ public class SearchToolWrapperFragment extends BaseFragment {
|
||||
}
|
||||
}
|
||||
|
||||
public Fragment getContentFragment() {
|
||||
return mContentFragment;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import com.ethanhua.skeleton.Skeleton
|
||||
import com.ethanhua.skeleton.ViewSkeletonScreen
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.toJson
|
||||
import com.gh.common.view.FixLinearLayoutManager
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
@ -124,8 +125,10 @@ class GameFragment : NormalFragment() {
|
||||
}
|
||||
|
||||
fun initPage(blockData: SubjectRecommendEntity) {
|
||||
mViewModel.blockData = blockData
|
||||
mViewModel.initData()
|
||||
if (mViewModel.blockData?.toJson() != blockData.toJson()) {
|
||||
mViewModel.blockData = blockData
|
||||
mViewModel.initData()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
||||
@ -53,14 +53,21 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
|
||||
private var mSubjectGameIdList = hashSetOf<String>()
|
||||
|
||||
init {
|
||||
if (blockData != null) initData()
|
||||
if (blockData == null) {
|
||||
// 读取默认板块
|
||||
blockData = HomeBottomBarHelper.getDefaultGameBarData()
|
||||
}
|
||||
|
||||
if (!blockData?.link.isNullOrEmpty()) {
|
||||
initData()
|
||||
}
|
||||
}
|
||||
|
||||
fun initData() {
|
||||
mSlideList.clear()
|
||||
mSubjectList.clear()
|
||||
mSubjectGameIdList.clear()
|
||||
mSubjectDigestList.clear()
|
||||
mSlideList = arrayListOf()
|
||||
mSubjectList = arrayListOf()
|
||||
mSubjectGameIdList = hashSetOf()
|
||||
mSubjectDigestList = arrayListOf()
|
||||
mItemDataListCache.clear()
|
||||
|
||||
loadStatus.postValue(LoadStatus.INIT_LOADING)
|
||||
|
||||
@ -79,7 +79,7 @@ class HomeFragment : BaseFragment<Any>() {
|
||||
if (activity is MainActivity) mViewModel.requestOpeningData()
|
||||
mViewModel.openingDialog.observeNonNull(this, callback = {
|
||||
ImageUtils.display(context, it.icon, object : BaseBitmapDataSubscriber() {
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>?) {
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import android.app.Application;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Debug;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@ -23,10 +23,12 @@ import com.gh.common.im.ImReceiver;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DeviceUtils;
|
||||
import com.gh.common.util.GdtHelper;
|
||||
import com.gh.common.util.HomeBottomBarHelper;
|
||||
import com.gh.common.util.PackageHelper;
|
||||
import com.gh.common.util.TeaHelper;
|
||||
import com.gh.download.DownloadNotification;
|
||||
import com.gh.gamecenter.Injection;
|
||||
import com.gh.gamecenter.entity.SubjectRecommendEntity;
|
||||
import com.gh.gamecenter.receiver.ActivitySkipReceiver;
|
||||
import com.gh.gamecenter.receiver.DownloadReceiver;
|
||||
import com.gh.gamecenter.receiver.InstallAndUninstallReceiver;
|
||||
@ -38,6 +40,7 @@ import com.gh.gid.GidHelper;
|
||||
import com.leon.channel.helper.ChannelReaderUtil;
|
||||
import com.llew.huawei.verifier.LoadedApkHuaWei;
|
||||
import com.m7.imkfsdk.chat.ChatActivity;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -153,7 +156,17 @@ public class HaloApp extends TinkerAppLike {
|
||||
initPackageChangesReceiver();
|
||||
|
||||
// 耗时操作
|
||||
mMainExecutor.execute(() -> GdtHelper.INSTANCE.init(getApplication()));
|
||||
mMainExecutor.execute(() -> {
|
||||
GdtHelper.INSTANCE.init(getApplication());
|
||||
// 预加载游戏库图标
|
||||
SubjectRecommendEntity barData = HomeBottomBarHelper.getDefaultGameBarData();
|
||||
if (!TextUtils.isEmpty(barData.getIconSelect())) {
|
||||
Picasso.with(getApplication()).load(Uri.parse(barData.getIconSelect())).fetch();
|
||||
}
|
||||
if (!TextUtils.isEmpty(barData.getIconUnselect())) {
|
||||
Picasso.with(getApplication()).load(Uri.parse(barData.getIconUnselect())).fetch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3.5 开始将 targetSdk 升级至 26,原来写在 Manifest 的部分 receiver 由于系统限制需要换成在运行时注册
|
||||
|
||||
@ -62,9 +62,9 @@
|
||||
android:checked="true"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
android:visibility="visible">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
<com.lightgame.view.CheckableImageView
|
||||
android:id="@+id/main_tab_game_icon"
|
||||
android:layout_width="@dimen/tab_img_size"
|
||||
android:layout_height="@dimen/tab_img_size"
|
||||
@ -72,6 +72,7 @@
|
||||
android:src="@drawable/selector_ic_game" />
|
||||
|
||||
<CheckedTextView
|
||||
android:id="@+id/main_tab_game_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
|
||||
@ -45,7 +45,7 @@ ext {
|
||||
okHttp = "3.10.0"
|
||||
gson = "2.8.2"
|
||||
zxing = "3.2.1"
|
||||
fresco = "1.13.0"
|
||||
fresco = "2.0.0"
|
||||
ormlite = "5.0"
|
||||
systemBarTint = "1.0.3"
|
||||
switchButton = "1.4.5"
|
||||
|
||||
@ -54,8 +54,8 @@ DATA_HOST=https\://data.ghzs.com/
|
||||
|
||||
# 请不要手动改动下面的值,除非你明确需要以某个apk作为基准包,需要打包请以scripts/tinker*.sh为准
|
||||
TINKER_ENABLE=
|
||||
TINKER_ID=8f9d6ed
|
||||
TINKER_BASE_APK_DIR=app-1205-18-20-44_8f9d6ed
|
||||
TINKER_ID=998cf70
|
||||
TINKER_BASE_APK_DIR=app-1220-10-02-18_998cf70
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user