mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-04 18:29:23 +02:00
more refactoring: String -> ZLNetworkException (code is still not compilable)
This commit is contained in:
parent
b2f140c612
commit
b5aea623a2
12 changed files with 120 additions and 101 deletions
|
@ -26,10 +26,10 @@ import android.widget.TextView;
|
|||
import android.content.DialogInterface;
|
||||
|
||||
import org.geometerplus.zlibrary.ui.android.R;
|
||||
|
||||
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
|
||||
|
||||
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
|
||||
|
||||
|
@ -91,17 +91,19 @@ class AuthenticationDialog extends NetworkDialog {
|
|||
mgr.UserNameOption.setValue(login);
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
String err = mgr.authorise(password);
|
||||
if (err != null) {
|
||||
try {
|
||||
mgr.authorise(password);
|
||||
} catch (ZLNetworkException e) {
|
||||
mgr.logOut();
|
||||
sendError(true, false, err);
|
||||
sendError(true, false, e.getMessage());
|
||||
return;
|
||||
}
|
||||
if (mgr.needsInitialization()) {
|
||||
err = mgr.initialize();
|
||||
if (err != null) {
|
||||
try {
|
||||
mgr.initialize();
|
||||
} catch (ZLNetworkException e) {
|
||||
mgr.logOut();
|
||||
sendError(true, false, err);
|
||||
sendError(true, false, e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,10 @@ import android.widget.TextView;
|
|||
import android.content.DialogInterface;
|
||||
|
||||
import org.geometerplus.zlibrary.core.resources.ZLResource;
|
||||
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.zlibrary.ui.android.R;
|
||||
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
|
||||
|
||||
import org.geometerplus.fbreader.network.*;
|
||||
import org.geometerplus.fbreader.network.opds.OPDSLinkReader;
|
||||
|
@ -183,9 +184,13 @@ class CustomCatalogDialog extends NetworkDialog {
|
|||
|
||||
final Runnable loadInfoRunnable = new Runnable() {
|
||||
public void run() {
|
||||
final ICustomNetworkLink link = (ICustomNetworkLink) myLink;
|
||||
final String err = link.reloadInfo();
|
||||
handler.sendMessage(handler.obtainMessage(0, err));
|
||||
String error = null;
|
||||
try {
|
||||
((ICustomNetworkLink)myLink).reloadInfo();
|
||||
} catch (ZLNetworkException e) {
|
||||
error = e.getMessage();
|
||||
}
|
||||
handler.sendMessage(handler.obtainMessage(0, error));
|
||||
}
|
||||
};
|
||||
((ZLAndroidDialogManager)ZLAndroidDialogManager.Instance()).wait("loadingCatalogInfo", loadInfoRunnable, myActivity);
|
||||
|
|
|
@ -82,19 +82,21 @@ abstract class ItemsLoadingRunnable implements Runnable {
|
|||
myUpdateInterval = updateIntervalMillis;
|
||||
}
|
||||
|
||||
public abstract String doBefore();
|
||||
public abstract void doBefore() throws ZLNetworkException;
|
||||
public abstract void doLoading(NetworkOperationData.OnNewItemListener doWithListener) throws ZLNetworkException;
|
||||
|
||||
public abstract String getResourceKey();
|
||||
|
||||
|
||||
public final void run() {
|
||||
String err = doBefore();
|
||||
if (err != null) {
|
||||
myHandler.sendFinish(err, false);
|
||||
try {
|
||||
doBefore();
|
||||
} catch (ZLNetworkException e) {
|
||||
myHandler.sendFinish(e.getMessage(), false);
|
||||
return;
|
||||
}
|
||||
err = doLoading(new NetworkOperationData.OnNewItemListener() {
|
||||
String error = null;
|
||||
try {
|
||||
doLoading(new NetworkOperationData.OnNewItemListener() {
|
||||
private long myUpdateTime;
|
||||
private int myItemsNumber;
|
||||
public void onNewItem(INetworkLink link, NetworkLibraryItem item) {
|
||||
|
@ -113,9 +115,13 @@ abstract class ItemsLoadingRunnable implements Runnable {
|
|||
myHandler.commitItems(link);
|
||||
}
|
||||
});
|
||||
} catch (ZLNetworkException e) {
|
||||
error = e.getMessage();
|
||||
}
|
||||
|
||||
myHandler.sendUpdateItems();
|
||||
myHandler.ensureItemsProcessed();
|
||||
myHandler.sendFinish(err, isLoadingInterrupted());
|
||||
myHandler.sendFinish(error, isLoadingInterrupted());
|
||||
myHandler.ensureFinishProcessed();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import android.view.ContextMenu;
|
|||
|
||||
import org.geometerplus.zlibrary.core.resources.ZLResource;
|
||||
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.zlibrary.ui.android.R;
|
||||
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
|
||||
|
@ -391,8 +392,13 @@ class NetworkBookActions extends NetworkTreeActions {
|
|||
}; // end Handler
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
String err = mgr.purchaseBook(book);
|
||||
handler.sendMessage(handler.obtainMessage(0, err));
|
||||
String error = null;
|
||||
try {
|
||||
mgr.purchaseBook(book);
|
||||
} catch (ZLNetworkException e) {
|
||||
error = e.getMessage();
|
||||
}
|
||||
handler.sendMessage(handler.obtainMessage(0, error));
|
||||
}
|
||||
}; // end Runnable
|
||||
((ZLAndroidDialogManager)ZLAndroidDialogManager.Instance()).wait("purchaseBook", runnable, activity);
|
||||
|
|
|
@ -359,25 +359,26 @@ class NetworkCatalogActions extends NetworkTreeActions {
|
|||
return "downloadingCatalogs";
|
||||
}
|
||||
|
||||
public String doBefore() {
|
||||
@Override
|
||||
public void doBefore() throws ZLNetworkException {
|
||||
/*if (!NetworkOperationRunnable::tryConnect()) {
|
||||
return;
|
||||
}*/
|
||||
final INetworkLink link = myTree.Item.Link;
|
||||
if (myCheckAuthentication && link.authenticationManager() != null) {
|
||||
NetworkAuthenticationManager mgr = link.authenticationManager();
|
||||
AuthenticationStatus auth = mgr.isAuthorised(true);
|
||||
final NetworkAuthenticationManager mgr = link.authenticationManager();
|
||||
final AuthenticationStatus auth = mgr.isAuthorised(true);
|
||||
if (auth.Message != null) {
|
||||
return auth.Message;
|
||||
throw new ZLNetworkException(true, auth.Message);
|
||||
}
|
||||
if (auth.Status == ZLBoolean3.B3_TRUE && mgr.needsInitialization()) {
|
||||
final String err = mgr.initialize();
|
||||
if (err != null) {
|
||||
try {
|
||||
mgr.initialize();
|
||||
} catch (ZLNetworkException e) {
|
||||
mgr.logOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,8 +29,9 @@ import android.view.*;
|
|||
import android.widget.BaseAdapter;
|
||||
|
||||
import org.geometerplus.zlibrary.core.resources.ZLResource;
|
||||
import org.geometerplus.zlibrary.ui.android.R;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.zlibrary.ui.android.R;
|
||||
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
|
||||
|
||||
import org.geometerplus.fbreader.network.NetworkTree;
|
||||
|
@ -109,7 +110,12 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
|
|||
private void runInitialization() {
|
||||
((ZLAndroidDialogManager)ZLAndroidDialogManager.Instance()).wait("loadingNetworkLibrary", new Runnable() {
|
||||
public void run() {
|
||||
final String error = NetworkView.Instance().initialize();
|
||||
String error = null;
|
||||
try {
|
||||
NetworkView.Instance().initialize();
|
||||
} catch (ZLNetworkException e) {
|
||||
error = e.getMessage();
|
||||
}
|
||||
Initializator.this.end(error);
|
||||
}
|
||||
}, myActivity);
|
||||
|
@ -272,8 +278,13 @@ public class NetworkLibraryActivity extends NetworkBaseActivity {
|
|||
|
||||
((ZLAndroidDialogManager)ZLAndroidDialogManager.Instance()).wait("updatingCatalogsList", new Runnable() {
|
||||
public void run() {
|
||||
final String result = view.runBackgroundUpdate(true);
|
||||
handler.sendMessage(handler.obtainMessage(0, result));
|
||||
String error = null;
|
||||
try {
|
||||
view.runBackgroundUpdate(true);
|
||||
} catch (ZLNetworkException e) {
|
||||
error = e.getMessage();
|
||||
}
|
||||
handler.sendMessage(handler.obtainMessage(0, error));
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
|
|
@ -125,7 +125,6 @@ public class NetworkSearchActivity extends Activity {
|
|||
}
|
||||
|
||||
private static class SearchRunnable extends ItemsLoadingRunnable {
|
||||
|
||||
private final String myPattern;
|
||||
|
||||
public SearchRunnable(ItemsLoadingHandler handler, String pattern) {
|
||||
|
@ -137,8 +136,7 @@ public class NetworkSearchActivity extends Activity {
|
|||
return "searchingNetwork";
|
||||
}
|
||||
|
||||
public String doBefore() {
|
||||
return null;
|
||||
public void doBefore() {
|
||||
}
|
||||
|
||||
public void doLoading(NetworkOperationData.OnNewItemListener doWithListener) throws ZLNetworkException {
|
||||
|
|
|
@ -34,8 +34,9 @@ import android.os.Message;
|
|||
import android.view.MenuItem;
|
||||
import android.view.Menu;
|
||||
|
||||
import org.geometerplus.fbreader.network.*;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.fbreader.network.*;
|
||||
|
||||
class NetworkView {
|
||||
private static NetworkView ourInstance;
|
||||
|
@ -57,15 +58,11 @@ class NetworkView {
|
|||
return myInitialized;
|
||||
}
|
||||
|
||||
public String initialize() {
|
||||
public void initialize() throws ZLNetworkException {
|
||||
new SQLiteNetworkDatabase();
|
||||
|
||||
final NetworkLibrary library = NetworkLibrary.Instance();
|
||||
final String error = library.initialize();
|
||||
if (error != null) {
|
||||
return error;
|
||||
}
|
||||
|
||||
library.initialize();
|
||||
library.synchronize();
|
||||
|
||||
myActions.add(new NetworkBookActions());
|
||||
|
@ -76,12 +73,10 @@ class NetworkView {
|
|||
myActions.trimToSize();
|
||||
|
||||
myInitialized = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// This method must be called from background thread
|
||||
public String runBackgroundUpdate(boolean clearCache) {
|
||||
return NetworkLibrary.Instance().runBackgroundUpdate(clearCache);
|
||||
public void runBackgroundUpdate(boolean clearCache) throws ZLNetworkException {
|
||||
NetworkLibrary.Instance().runBackgroundUpdate(clearCache);
|
||||
}
|
||||
|
||||
// This method MUST be called from main thread
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
|
|||
|
||||
import org.geometerplus.zlibrary.core.resources.ZLResource;
|
||||
import org.geometerplus.zlibrary.core.util.ZLBoolean3;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.fbreader.network.NetworkErrors;
|
||||
import org.geometerplus.fbreader.network.authentication.NetworkAuthenticationManager;
|
||||
|
@ -148,17 +149,19 @@ class RegisterUserDialog extends NetworkDialog {
|
|||
final NetworkAuthenticationManager mgr = myLink.authenticationManager();
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
String err = mgr.registerUser(myLogin, myPassword, myEmail);
|
||||
if (err != null) {
|
||||
try {
|
||||
mgr.registerUser(myLogin, myPassword, myEmail);
|
||||
} catch (ZLNetworkException e) {
|
||||
mgr.logOut();
|
||||
sendError(true, false, err);
|
||||
sendError(true, false, e.getMessage());
|
||||
return;
|
||||
}
|
||||
if (mgr.isAuthorised(true).Status != ZLBoolean3.B3_FALSE && mgr.needsInitialization()) {
|
||||
err = mgr.initialize();
|
||||
if (err != null) {
|
||||
try {
|
||||
mgr.initialize();
|
||||
} catch (ZLNetworkException e) {
|
||||
mgr.logOut();
|
||||
sendError(false, false, err);
|
||||
sendError(false, false, e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -488,8 +488,7 @@ public class NetworkLibrary {
|
|||
}
|
||||
|
||||
|
||||
// returns Error Message
|
||||
public String simpleSearch(String pattern, final NetworkOperationData.OnNewItemListener listener) {
|
||||
public void simpleSearch(String pattern, final NetworkOperationData.OnNewItemListener listener) throws ZLNetworkException {
|
||||
LinkedList<ZLNetworkRequest> requestList = new LinkedList<ZLNetworkRequest>();
|
||||
LinkedList<NetworkOperationData> dataList = new LinkedList<NetworkOperationData>();
|
||||
|
||||
|
@ -520,15 +519,12 @@ public class NetworkLibrary {
|
|||
}
|
||||
|
||||
while (requestList.size() != 0) {
|
||||
final String errorMessage = ZLNetworkManager.Instance().perform(requestList);
|
||||
if (errorMessage != null) {
|
||||
return errorMessage;
|
||||
}
|
||||
ZLNetworkManager.Instance().perform(requestList);
|
||||
|
||||
requestList.clear();
|
||||
|
||||
if (listener.confirmInterrupt()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
for (NetworkOperationData data: dataList) {
|
||||
ZLNetworkRequest request = data.resume();
|
||||
|
@ -537,8 +533,6 @@ public class NetworkLibrary {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ICustomNetworkLink.SaveLinkListener myChangesListener = new ICustomNetworkLink.SaveLinkListener() {
|
||||
|
|
|
@ -22,6 +22,7 @@ package org.geometerplus.fbreader.network.authentication;
|
|||
import java.util.HashMap;
|
||||
|
||||
import org.geometerplus.zlibrary.core.options.ZLStringOption;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
|
||||
import org.geometerplus.fbreader.network.*;
|
||||
|
||||
|
@ -60,7 +61,7 @@ public abstract class NetworkAuthenticationManager {
|
|||
* Common manager methods
|
||||
*/
|
||||
public abstract AuthenticationStatus isAuthorised(boolean useNetwork /* = true */);
|
||||
public abstract String authorise(String password); // returns error message
|
||||
public abstract void authorise(String password) throws ZLNetworkException;
|
||||
public abstract void logOut();
|
||||
public abstract BookReference downloadReference(NetworkBookItem book);
|
||||
|
||||
|
@ -73,9 +74,8 @@ public abstract class NetworkAuthenticationManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
// returns error message
|
||||
public String initialize() {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
public void initialize() throws ZLNetworkException {
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
// returns true if link must be purchased before downloading
|
||||
|
@ -83,9 +83,8 @@ public abstract class NetworkAuthenticationManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
// returns error message
|
||||
public String purchaseBook(NetworkBookItem book) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
public void purchaseBook(NetworkBookItem book) throws ZLNetworkException {
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
public String currentAccount() {
|
||||
|
@ -113,8 +112,8 @@ public abstract class NetworkAuthenticationManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
public String registerUser(String login, String password, String email) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
public void registerUser(String login, String password, String email) throws ZLNetworkException {
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -124,7 +123,7 @@ public abstract class NetworkAuthenticationManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
public String recoverPassword(String email) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
public void recoverPassword(String email) throws ZLNetworkException {
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.*;
|
|||
import org.geometerplus.zlibrary.core.options.ZLStringOption;
|
||||
import org.geometerplus.zlibrary.core.util.ZLNetworkUtil;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
|
||||
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
|
||||
|
||||
import org.geometerplus.fbreader.network.*;
|
||||
|
@ -95,10 +96,10 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String authorise(String password) {
|
||||
public void authorise(String password) throws ZLNetworkException {
|
||||
String url = Link.getLink(INetworkLink.URL_SIGN_IN);
|
||||
if (url == null) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
final String login;
|
||||
synchronized (this) {
|
||||
|
@ -168,7 +169,7 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String purchaseBook(NetworkBookItem book) {
|
||||
public void purchaseBook(NetworkBookItem book) throws ZLNetworkException {
|
||||
final String sid;
|
||||
synchronized (this) {
|
||||
sid = mySidOption.getValue();
|
||||
|
@ -285,17 +286,17 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String initialize() {
|
||||
public void initialize() throws ZLNetworkException {
|
||||
final String sid;
|
||||
final LitResNetworkRequest purchasedBooksRequest;
|
||||
final LitResNetworkRequest accountRequest;
|
||||
synchronized (this) {
|
||||
sid = mySidOption.getValue();
|
||||
if (sid.length() == 0) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_AUTHENTICATION_FAILED);
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_AUTHENTICATION_FAILED);
|
||||
}
|
||||
if (sid.equals(myInitializedDataSid)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
purchasedBooksRequest = loadPurchasedBooks();
|
||||
|
@ -317,7 +318,6 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
myInitializedDataSid = sid;
|
||||
loadPurchasedBooksOnSuccess(purchasedBooksRequest);
|
||||
loadAccountOnSuccess(accountRequest);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,10 +380,10 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String registerUser(String login, String password, String email) {
|
||||
public void registerUser(String login, String password, String email) throws ZLNetworkException {
|
||||
String url = Link.getLink(INetworkLink.URL_SIGN_UP);
|
||||
if (url == null) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
url = ZLNetworkUtil.appendParameter(url, "new_login", login);
|
||||
url = ZLNetworkUtil.appendParameter(url, "new_pwd1", password);
|
||||
|
@ -401,7 +401,6 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
}
|
||||
mySidOption.setValue(xmlReader.Sid);
|
||||
mySidUserNameOption.setValue(login);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,13 +411,13 @@ public class LitResAuthenticationManager extends NetworkAuthenticationManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String recoverPassword(String email) {
|
||||
public void recoverPassword(String email) throws ZLNetworkException {
|
||||
String url = Link.getLink(INetworkLink.URL_RECOVER_PASSWORD);
|
||||
if (url == null) {
|
||||
return NetworkErrors.errorMessage(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
throw new ZLNetworkException(NetworkErrors.ERROR_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
url = ZLNetworkUtil.appendParameter(url, "mail", email);
|
||||
final LitResPasswordRecoveryXMLReader xmlReader = new LitResPasswordRecoveryXMLReader(Link.getSiteName());
|
||||
return ZLNetworkManager.Instance().perform(new LitResNetworkRequest(url, SSLCertificate, xmlReader));
|
||||
ZLNetworkManager.Instance().perform(new LitResNetworkRequest(url, SSLCertificate, xmlReader));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue