mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
Re-license and rename UIExplorer integration test app as RNTester
Reviewed By: yungsters Differential Revision: D4950085 fbshipit-source-id: 44574b5d0ef0d2ad5dfc714309b18dc69cbad9ff
This commit is contained in:
committed by
Facebook Github Bot
parent
82c4b9f0b7
commit
4a80dceac7
32
RNTester/android/app/BUCK
Normal file
32
RNTester/android/app/BUCK
Normal file
@@ -0,0 +1,32 @@
|
||||
include_defs("//ReactAndroid/DEFS")
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
deps = [
|
||||
":rntester-lib",
|
||||
],
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "rntester-lib",
|
||||
srcs = glob(["src/main/java/**/*.java"]),
|
||||
deps = [
|
||||
":res",
|
||||
react_native_dep("third-party/android/support/v4:lib-support-v4"),
|
||||
react_native_dep("third-party/java/jsr-305:jsr-305"),
|
||||
react_native_target("java/com/facebook/react:react"),
|
||||
react_native_target("java/com/facebook/react/modules/core:core"),
|
||||
react_native_target("java/com/facebook/react/shell:shell"),
|
||||
react_native_target("jni/prebuilt:android-jsc"),
|
||||
# .so files are prebuilt by Gradle with `./gradlew :ReactAndroid:packageReactNdkLibsForBuck`
|
||||
react_native_target("jni/prebuilt:reactnative-libs"),
|
||||
],
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "com.facebook.react.uiapp",
|
||||
res = "src/main/res",
|
||||
)
|
||||
143
RNTester/android/app/build.gradle
Normal file
143
RNTester/android/app/build.gradle
Normal file
@@ -0,0 +1,143 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property is in the format 'bundleIn${productFlavor}${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"]
|
||||
* ]
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
bundleAssetName: "RNTesterApp.android.bundle",
|
||||
entryFile: file("../../js/RNTesterApp.android.js"),
|
||||
root: "../../../../",
|
||||
inputExcludes: ["android/**", "./**"]
|
||||
]
|
||||
|
||||
apply from: "react.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create three separate APKs instead of one:
|
||||
* - A universal APK that works on all devices
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.facebook.react.uiapp"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 23
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(MYAPP_RELEASE_STORE_FILE)
|
||||
storePassword MYAPP_RELEASE_STORE_PASSWORD
|
||||
keyAlias MYAPP_RELEASE_KEY_ALIAS
|
||||
keyPassword MYAPP_RELEASE_KEY_PASSWORD
|
||||
}
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false
|
||||
reset()
|
||||
include "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
|
||||
def versionCodes = ["armeabi-v7a":1, "x86":2]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
||||
|
||||
// Build React Native from source
|
||||
compile project(':ReactAndroid')
|
||||
}
|
||||
5
RNTester/android/app/gradle.properties
Normal file
5
RNTester/android/app/gradle.properties
Normal file
@@ -0,0 +1,5 @@
|
||||
android.useDeprecatedNdk=true
|
||||
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
|
||||
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
|
||||
MYAPP_RELEASE_STORE_PASSWORD=*****
|
||||
MYAPP_RELEASE_KEY_PASSWORD=*****
|
||||
17
RNTester/android/app/proguard-rules.pro
vendored
Normal file
17
RNTester/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
96
RNTester/android/app/react.gradle
Normal file
96
RNTester/android/app/react.gradle
Normal file
@@ -0,0 +1,96 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
def config = project.hasProperty("react") ? project.react : [];
|
||||
|
||||
def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"
|
||||
def entryFile = config.entryFile ?: "index.android.js"
|
||||
|
||||
// because elvis operator
|
||||
def elvisFile(thing) {
|
||||
return thing ? file(thing) : null;
|
||||
}
|
||||
|
||||
def reactRoot = elvisFile(config.root) ?: file("../../")
|
||||
def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]
|
||||
|
||||
void runBefore(String dependentTaskName, Task task) {
|
||||
Task dependentTask = tasks.findByPath(dependentTaskName);
|
||||
if (dependentTask != null) {
|
||||
dependentTask.dependsOn task
|
||||
}
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
// Grab all build types and product flavors
|
||||
def buildTypes = android.buildTypes.collect { type -> type.name }
|
||||
def productFlavors = android.productFlavors.collect { flavor -> flavor.name }
|
||||
|
||||
// When no product flavors defined, use empty
|
||||
if (!productFlavors) productFlavors.add('')
|
||||
|
||||
productFlavors.each { productFlavorName ->
|
||||
buildTypes.each { buildTypeName ->
|
||||
// Create variant and source names
|
||||
def sourceName = "${buildTypeName}"
|
||||
def targetName = "${sourceName.capitalize()}"
|
||||
if (productFlavorName) {
|
||||
sourceName = "${productFlavorName}${targetName}"
|
||||
}
|
||||
|
||||
// React js bundle directories
|
||||
def jsBundleDirConfigName = "jsBundleDir${targetName}"
|
||||
def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?:
|
||||
file("$buildDir/intermediates/assets/${sourceName}")
|
||||
|
||||
def resourcesDirConfigName = "jsBundleDir${targetName}"
|
||||
def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?:
|
||||
file("$buildDir/intermediates/res/merged/${sourceName}")
|
||||
def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
|
||||
|
||||
// Bundle task name for variant
|
||||
def bundleJsAndAssetsTaskName = "bundle${targetName}JsAndAssets"
|
||||
|
||||
def currentBundleTask = tasks.create(
|
||||
name: bundleJsAndAssetsTaskName,
|
||||
type: Exec) {
|
||||
group = "react"
|
||||
description = "bundle JS and assets for ${targetName}."
|
||||
|
||||
// Create dirs if they are not there (e.g. the "clean" task just ran)
|
||||
doFirst {
|
||||
jsBundleDir.mkdirs()
|
||||
resourcesDir.mkdirs()
|
||||
}
|
||||
|
||||
// Set up inputs and outputs so gradle can cache the result
|
||||
inputs.files fileTree(dir: reactRoot, excludes: inputExcludes)
|
||||
outputs.dir jsBundleDir
|
||||
outputs.dir resourcesDir
|
||||
|
||||
// Set up the call to the react-native cli
|
||||
workingDir reactRoot
|
||||
|
||||
// Set up dev mode
|
||||
def devEnabled = !targetName.toLowerCase().contains("release")
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
commandLine "cmd", "/c", "node", "./local-cli/cli.js", "bundle", "--platform", "android", "--dev", "${devEnabled}",
|
||||
"--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir
|
||||
} else {
|
||||
commandLine "node", "./local-cli/cli.js", "bundle", "--platform", "android", "--dev", "${devEnabled}",
|
||||
"--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir
|
||||
}
|
||||
|
||||
enabled config."bundleIn${targetName}" ?: targetName.toLowerCase().contains("release")
|
||||
}
|
||||
|
||||
// Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process
|
||||
currentBundleTask.dependsOn("merge${targetName}Resources")
|
||||
currentBundleTask.dependsOn("merge${targetName}Assets")
|
||||
|
||||
runBefore("processArmeabi-v7a${targetName}Resources", currentBundleTask)
|
||||
runBefore("processX86${targetName}Resources", currentBundleTask)
|
||||
runBefore("processUniversal${targetName}Resources", currentBundleTask)
|
||||
runBefore("process${targetName}Resources", currentBundleTask)
|
||||
}
|
||||
}
|
||||
}
|
||||
49
RNTester/android/app/src/main/AndroidManifest.xml
Normal file
49
RNTester/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.react.uiapp"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!--Just to show permissions example-->
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR"/>
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<application
|
||||
android:name=".RNTesterApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/launcher_icon"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.ReactNative.AppCompat.Light" >
|
||||
<activity
|
||||
android:name=".RNTesterActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<!-- Accepts URIs that begin with "rntester://example” -->
|
||||
<data android:scheme="rntester" android:host="example" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
BIN
RNTester/android/app/src/main/assets/fonts/notoserif.ttf
Executable file
BIN
RNTester/android/app/src/main/assets/fonts/notoserif.ttf
Executable file
Binary file not shown.
BIN
RNTester/android/app/src/main/assets/fonts/notoserif_bold_italic.ttf
Executable file
BIN
RNTester/android/app/src/main/assets/fonts/notoserif_bold_italic.ttf
Executable file
Binary file not shown.
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.facebook.react.uiapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RNTesterActivity extends ReactActivity {
|
||||
public static class RNTesterActivityDelegate extends ReactActivityDelegate {
|
||||
private static final String PARAM_ROUTE = "route";
|
||||
private Bundle mInitialProps = null;
|
||||
private final @Nullable Activity mActivity;
|
||||
|
||||
public RNTesterActivityDelegate(Activity activity, String mainComponentName) {
|
||||
super(activity, mainComponentName);
|
||||
this.mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// Get remote param before calling super which uses it
|
||||
Bundle bundle = mActivity.getIntent().getExtras();
|
||||
if (bundle != null && bundle.containsKey(PARAM_ROUTE)) {
|
||||
String routeUri = new StringBuilder("rntester://example/")
|
||||
.append(bundle.getString(PARAM_ROUTE))
|
||||
.append("Example")
|
||||
.toString();
|
||||
mInitialProps = new Bundle();
|
||||
mInitialProps.putString("exampleFromAppetizeParams", routeUri);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle getLaunchOptions() {
|
||||
return mInitialProps;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new RNTesterActivityDelegate(this, getMainComponentName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "RNTesterApp";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.facebook.react.uiapp;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RNTesterApplication extends Application implements ReactApplication {
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
@Override
|
||||
public String getJSMainModuleName() {
|
||||
return "RNTester/js/RNTesterApp.android";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getBundleAssetName() {
|
||||
return "RNTesterApp.android.bundle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 406 B |
Binary file not shown.
|
After Width: | Height: | Size: 179 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
RNTester/android/app/src/main/res/drawable/launcher_icon.png
Normal file
BIN
RNTester/android/app/src/main/res/drawable/launcher_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
BIN
RNTester/android/app/src/main/res/drawable/legacy_image.png
Normal file
BIN
RNTester/android/app/src/main/res/drawable/legacy_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
12
RNTester/android/app/src/main/res/layout/activity_main.xml
Normal file
12
RNTester/android/app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".RNTesterApp">
|
||||
|
||||
<com.facebook.react.ReactRootView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/react_root_view"/>
|
||||
|
||||
</RelativeLayout>
|
||||
3
RNTester/android/app/src/main/res/values/strings.xml
Normal file
3
RNTester/android/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">RNTester App</string>
|
||||
</resources>
|
||||
8
RNTester/android/app/src/main/res/values/styles.xml
Normal file
8
RNTester/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user