// Path: src/main/java/com/xemantic/ankh/shared/error/Errors.java
// public final class Errors {
// 
//   private Errors() { /* util class, non-instantiable */ }
// 
//   public static void onError(Throwable error) {
//     Objects.requireNonNull(error);
//     Thread currentThread = Thread.currentThread();
//     Thread.UncaughtExceptionHandler handler = currentThread.getUncaughtExceptionHandler();
//     handler.uncaughtException(currentThread, error);
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/ankh/shared/event/Trigger.java
// public final class Trigger {
// 
//   public static final Trigger INSTANCE = new Trigger();
// 
//   private Trigger() { /* no instantiation */ }
// 
//   public static void fire(Observer<Trigger> observer) {
//     (Objects.requireNonNull(observer)).onNext(Trigger.INSTANCE);
//   }
// 
//   public static Observable<Trigger> noTriggers() {
//     return Observable.empty();
//   }
// 
//   /**
//    * Creates {@link Observable} emitting only one {@link Trigger} instance on
//    * creation. For subsequent subscribers it behaves like {@link Observable#empty()}.
//    *
//    * @return the one time firing Observable.
//    */
//   public static Observable<Trigger> oneTime() {
//     AtomicBoolean firstTime = new AtomicBoolean(true);
//     return Observable.create(e -> {
//       if (firstTime.get()) {
//         firstTime.set(false);
//         e.onNext(Trigger.INSTANCE);
//       }
//     });
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/ankh/shared/presenter/Presenter.java
// public abstract class Presenter {
// 
//   private final List<Observable<?>> observables = new LinkedList<>();
// 
//   private final List<Disposable> subscriptions = new LinkedList<>();
// 
//   private Action onStart = Functions.EMPTY_ACTION;
// 
//   protected Presenter(Observable<?> ... observables) {
//     this.observables.addAll(Arrays.asList(observables));
//   }
// 
//   protected void register(Observable<?> observable) {
//     observables.add(observable);
//   }
// 
//   protected void onStart(Action onStart) {
//     this.onStart = onStart;
//   }
// 
//   public void start() {
//     subscribeObservables();
//     runOnStart();
//   }
// 
//   /**
//    * Stops this presenter. This method is supposed to be called
//    * when it is required to cancel all the pending subscriptions,
//    * like ongoing HTTP requests, and unsubscribe from any {@link Observable}.
//    * It might happen for example when user is navigating to another
//    * {@code Activity} on Android platform.
//    */
//   public void stop() {
//     for (Disposable subscription : subscriptions) {
//       subscription.dispose();
//     }
//     subscriptions.clear();
//   }
// 
//   private void subscribeObservables() {
//     for (Observable<?> observable : observables) {
//       subscriptions.add(subscribe(observable));
//     }
//   }
// 
//   private void runOnStart() {
//     try {
//       onStart.run();
//     } catch (Exception e) {
//       throw new RuntimeException("Could not start presenter", e);
//     }
//   }
// 
//   private static Disposable subscribe(Observable<?> observable) {
//     return observable
//         .retry(throwable -> { // always retry, will cause unconditional resubscription
//           Errors.onError(throwable);
//           return true;
//         })
//         .subscribe();
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/event/UserQueryEvent.java
// @Event
// public class UserQueryEvent {
// 
//   private final String query;
// 
//   public UserQueryEvent(String query) {
//     this.query = Objects.requireNonNull(query);
//   }
// 
//   public String getQuery() {
//     return query;
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/model/SearchResult.java
// public interface SearchResult {
// 
//   int getTotalCount();
// 
//   boolean isIncompleteResult();
// 
//   List<User> getItems();
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/model/User.java
// public interface User {
// 
//   String getLogin();
// 
//   String getAvatarUrl();
// 
//   String getHtmlUrl();
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/service/UserService.java
// public interface UserService {
// 
//   Single<SearchResult> find(String query, int page, int perPage);
// 
// }


import javax.inject.Inject;
import javax.inject.Named;
import java.util.LinkedList;
import java.util.List;
import com.xemantic.ankh.shared.error.Errors;
import com.xemantic.ankh.shared.event.Trigger;
import com.xemantic.ankh.shared.presenter.Presenter;
import com.xemantic.githubusers.logic.event.UserQueryEvent;
import com.xemantic.githubusers.logic.model.SearchResult;
import com.xemantic.githubusers.logic.model.User;
import com.xemantic.githubusers.logic.service.UserService;
import io.reactivex.Observable;
                                if (page == 1) clearOnFirstPage(view);
                              })
                              .retry(throwable -> {
                                Errors.onError(throwable);
                                view.enableLoadMore(true);
                                if (page == 1) clearOnFirstPage(view);
                                return true;
                              })
                      );
                },
                1 // one page to prefetch
            )
            .flatMapIterable(SearchResult::getItems)
            .doOnNext(user -> view.add(newUserView(user)))
    );
  }

  @Override
  public void stop() {
    super.stop();
    clearActiveUserPresenters();
  }

  private void clearOnFirstPage(UserListView view) {
    view.loadingFirstPage(false);
    view.clear();
    clearActiveUserPresenters();
  }

  private void clearActiveUserPresenters() {
// Path: src/main/java/com/xemantic/ankh/shared/error/Errors.java
// public final class Errors {
// 
//   private Errors() { /* util class, non-instantiable */ }
// 
//   public static void onError(Throwable error) {
//     Objects.requireNonNull(error);
//     Thread currentThread = Thread.currentThread();
//     Thread.UncaughtExceptionHandler handler = currentThread.getUncaughtExceptionHandler();
//     handler.uncaughtException(currentThread, error);
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/ankh/shared/event/Trigger.java
// public final class Trigger {
// 
//   public static final Trigger INSTANCE = new Trigger();
// 
//   private Trigger() { /* no instantiation */ }
// 
//   public static void fire(Observer<Trigger> observer) {
//     (Objects.requireNonNull(observer)).onNext(Trigger.INSTANCE);
//   }
// 
//   public static Observable<Trigger> noTriggers() {
//     return Observable.empty();
//   }
// 
//   /**
//    * Creates {@link Observable} emitting only one {@link Trigger} instance on
//    * creation. For subsequent subscribers it behaves like {@link Observable#empty()}.
//    *
//    * @return the one time firing Observable.
//    */
//   public static Observable<Trigger> oneTime() {
//     AtomicBoolean firstTime = new AtomicBoolean(true);
//     return Observable.create(e -> {
//       if (firstTime.get()) {
//         firstTime.set(false);
//         e.onNext(Trigger.INSTANCE);
//       }
//     });
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/ankh/shared/presenter/Presenter.java
// public abstract class Presenter {
// 
//   private final List<Observable<?>> observables = new LinkedList<>();
// 
//   private final List<Disposable> subscriptions = new LinkedList<>();
// 
//   private Action onStart = Functions.EMPTY_ACTION;
// 
//   protected Presenter(Observable<?> ... observables) {
//     this.observables.addAll(Arrays.asList(observables));
//   }
// 
//   protected void register(Observable<?> observable) {
//     observables.add(observable);
//   }
// 
//   protected void onStart(Action onStart) {
//     this.onStart = onStart;
//   }
// 
//   public void start() {
//     subscribeObservables();
//     runOnStart();
//   }
// 
//   /**
//    * Stops this presenter. This method is supposed to be called
//    * when it is required to cancel all the pending subscriptions,
//    * like ongoing HTTP requests, and unsubscribe from any {@link Observable}.
//    * It might happen for example when user is navigating to another
//    * {@code Activity} on Android platform.
//    */
//   public void stop() {
//     for (Disposable subscription : subscriptions) {
//       subscription.dispose();
//     }
//     subscriptions.clear();
//   }
// 
//   private void subscribeObservables() {
//     for (Observable<?> observable : observables) {
//       subscriptions.add(subscribe(observable));
//     }
//   }
// 
//   private void runOnStart() {
//     try {
//       onStart.run();
//     } catch (Exception e) {
//       throw new RuntimeException("Could not start presenter", e);
//     }
//   }
// 
//   private static Disposable subscribe(Observable<?> observable) {
//     return observable
//         .retry(throwable -> { // always retry, will cause unconditional resubscription
//           Errors.onError(throwable);
//           return true;
//         })
//         .subscribe();
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/event/UserQueryEvent.java
// @Event
// public class UserQueryEvent {
// 
//   private final String query;
// 
//   public UserQueryEvent(String query) {
//     this.query = Objects.requireNonNull(query);
//   }
// 
//   public String getQuery() {
//     return query;
//   }
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/model/SearchResult.java
// public interface SearchResult {
// 
//   int getTotalCount();
// 
//   boolean isIncompleteResult();
// 
//   List<User> getItems();
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/model/User.java
// public interface User {
// 
//   String getLogin();
// 
//   String getAvatarUrl();
// 
//   String getHtmlUrl();
// 
// }
// 
// Path: src/main/java/com/xemantic/githubusers/logic/service/UserService.java
// public interface UserService {
// 
//   Single<SearchResult> find(String query, int page, int perPage);
// 
// }


// Path: src/main/java/com/xemantic/githubusers/logic/user/UserListPresenter.java
import javax.inject.Inject;
import javax.inject.Named;
import java.util.LinkedList;
import java.util.List;
import com.xemantic.ankh.shared.error.Errors;
import com.xemantic.ankh.shared.event.Trigger;
import com.xemantic.ankh.shared.presenter.Presenter;
import com.xemantic.githubusers.logic.event.UserQueryEvent;
import com.xemantic.githubusers.logic.model.SearchResult;
import com.xemantic.githubusers.logic.model.User;
import com.xemantic.githubusers.logic.service.UserService;
import io.reactivex.Observable;

                                if (page == 1) clearOnFirstPage(view);
                              })
                              .retry(throwable -> {
                                Errors.onError(throwable);
                                view.enableLoadMore(true);
                                if (page == 1) clearOnFirstPage(view);
                                return true;
                              })
                      );
                },
                1 // one page to prefetch
            )
            .flatMapIterable(SearchResult::getItems)
            .doOnNext(user -> view.add(newUserView(user)))
    );
  }

  @Override
  public void stop() {
    super.stop();
    clearActiveUserPresenters();
  }

  private void clearOnFirstPage(UserListView view) {
    view.loadingFirstPage(false);
    view.clear();
    clearActiveUserPresenters();
  }

  private void clearActiveUserPresenters() {
    for (Presenter presenter : activeUserPresenters) {