예제 #1
0
 /**
  * See also {@link #reconcile(TurboIssue)}
  *
  * @param otherIssue
  */
 private void reconcileLabels(TurboIssue otherIssue) {
   LocalDateTime thisIssueLabelsModifiedAt = this.getLabelsLastModifiedAt();
   LocalDateTime otherIssueLabelsModifiedAt = otherIssue.getLabelsLastModifiedAt();
   if (thisIssueLabelsModifiedAt.isBefore(otherIssueLabelsModifiedAt)) {
     logger.info(
         "Issue %s's labels %s are stale, replacing with %s",
         this, this.getLabels(), otherIssue.getLabels());
     this.labels = otherIssue.getLabels();
     this.labelsLastModifiedAt = Optional.of(otherIssue.getLabelsLastModifiedAt());
   }
 }
예제 #2
0
 /**
  * Possibly shows a notification or an error dialog depending on {@code success} and {@code
  * isUndo}.
  *
  * @param issue the TurboIssue acted on
  * @param action the Action that acted on the issue
  * @param success whether the action was successful
  * @param isUndo whether action is an undo
  * @return {@code success}
  */
 private boolean handleActionResult(
     TurboIssue issue, Action<TurboIssue> action, Boolean success, boolean isUndo) {
   if (!success) {
     String errorMessage = "Please check if you have write permissions to " + issue.getRepoId();
     showErrorDialog(issue, action, errorMessage);
     return success;
   }
   if (!isUndo) {
     showNotification(issue.getId(), issue.getTitle(), action.getDescription());
     return success;
   }
   return success;
 }
예제 #3
0
  /**
   * Tests that {@code editIssueState} finds issue with the right id and successfully modify the
   * issue's labels
   */
  @Test
  public void editIssueState_successful() {
    String repoId = "testowner/testrepo";
    Optional<TurboIssue> result;

    TurboIssue issue1 = LogicTests.createOpenIssue();
    TurboIssue issue2 = LogicTests.createClosedIssue();
    List<TurboIssue> issues = Arrays.asList(issue2, issue1);
    Model model =
        new Model(
            repoId,
            issues,
            new ArrayList<TurboLabel>(),
            new ArrayList<TurboMilestone>(),
            new ArrayList<TurboUser>());

    result = model.editIssueState(issue1.getId(), false);
    assertEquals(issue1.getId(), result.get().getId());
    assertEquals(false, result.get().isOpen());

    result = model.editIssueState(issue2.getId(), true);
    assertEquals(issue2.getId(), result.get().getId());
    assertEquals(true, result.get().isOpen());

    result = model.editIssueState(issue2.getId(), true);
    assertEquals(issue2.getId(), result.get().getId());
    assertEquals(true, result.get().isOpen());
  }
예제 #4
0
  /**
   * Combines data from a corresponding pull request with data in this issue This method returns a
   * new combined issue and does not mutate this issue
   *
   * @param pullRequest
   * @return new new combined issue
   */
  public TurboIssue combineWithPullRequest(PullRequest pullRequest) {
    TurboIssue newIssue = new TurboIssue(this);

    if (pullRequest.getUpdatedAt() == null) {
      return newIssue;
    }

    LocalDateTime pullRequestUpdatedAt = Utility.dateToLocalDateTime(pullRequest.getUpdatedAt());
    if (pullRequestUpdatedAt.isBefore(newIssue.getUpdatedAt())) {
      return newIssue;
    }

    newIssue.setUpdatedAt(pullRequestUpdatedAt);
    return newIssue;
  }
예제 #5
0
  /**
   * Tests that replaceIssueAssigneeOnServer finds issue with the right id and successfully modify
   * the issue's assignee
   */
  @Test
  public void replaceIssueAssignee_successful() {
    String repoId = "testowner/testrepo";
    String originalAssignee = "user1";
    String newAssignee = "user2";

    TurboIssue issue1 = LogicTests.createIssueWithAssignee(1, originalAssignee);
    TurboIssue issue2 = LogicTests.createIssueWithAssignee(2, originalAssignee);
    TurboIssue issue3 = LogicTests.createIssueWithAssignee(3, originalAssignee);
    List<TurboIssue> issues = Arrays.asList(issue3, issue2, issue1);

    Model model =
        new Model(repoId, issues, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
    Optional<TurboIssue> result =
        model.replaceIssueAssignee(issue1.getId(), Optional.of(newAssignee));
    assertEquals(1, result.get().getId());
    assertEquals(newAssignee, result.get().getAssignee().get());
  }
예제 #6
0
  /**
   * Updates data for issues with corresponding pull requests. Original list of issues and original
   * issue instances are not mutated
   *
   * @param issues
   * @param pullRequests
   * @return a new list of issues
   */
  public static List<TurboIssue> combineWithPullRequests(
      List<TurboIssue> issues, List<PullRequest> pullRequests) {
    List<TurboIssue> issuesCopy = new ArrayList<>(issues);

    for (PullRequest pullRequest : pullRequests) {
      int id = pullRequest.getNumber();

      Optional<Integer> corresponding = findIssueWithId(issuesCopy, id);
      if (corresponding.isPresent()) {
        TurboIssue issue = issuesCopy.get(corresponding.get());
        issuesCopy.set(corresponding.get(), issue.combineWithPullRequest(pullRequest));
      } else {
        String errorMsg = "No corresponding issue for pull request " + pullRequest;
        logger.error(errorMsg);
      }
    }

    return issuesCopy;
  }
예제 #7
0
  /**
   * Tests that replaceIssueLabelsOnServer finds issue with the right id and successfully modify the
   * issue's labels
   */
  @Test
  public void replaceIssueLabels_successful() {
    String repoId = "testowner/testrepo";
    List<String> originalLabels = Arrays.asList("label1", "label2");
    List<String> newLabels = Arrays.asList("label3", "label4");

    TurboIssue issue1 = LogicTests.createIssueWithLabels(1, originalLabels);
    TurboIssue issue2 = LogicTests.createIssueWithLabels(2, originalLabels);
    TurboIssue issue3 = LogicTests.createIssueWithLabels(3, originalLabels);
    List<TurboIssue> issues = Arrays.asList(issue3, issue2, issue1);

    Model model =
        new Model(
            repoId,
            issues,
            new ArrayList<TurboLabel>(),
            new ArrayList<TurboMilestone>(),
            new ArrayList<TurboUser>());
    Optional<TurboIssue> result = model.replaceIssueLabels(issue1.getId(), newLabels);
    assertEquals(1, result.get().getId());
    assertEquals(newLabels, result.get().getLabels());
  }
예제 #8
0
  /**
   * Takes lists of TurboIssues and reconciles the changes between them, returning a list of
   * TurboIssues with updates from the second.
   *
   * @param existing
   * @param changed
   */
  public static List<TurboIssue> reconcile(List<TurboIssue> existing, List<TurboIssue> changed) {
    List<TurboIssue> existingCopy = new ArrayList<>(existing);
    for (TurboIssue issue : changed) {
      int id = issue.getId();

      Optional<Integer> correspondingIssueIndex = findIssueWithId(existingCopy, id);
      if (!correspondingIssueIndex.isPresent()) {
        existingCopy.add(new TurboIssue(issue));
      } else {
        TurboIssue existingIssue = existingCopy.get(correspondingIssueIndex.get());
        TurboIssue newIssue = new TurboIssue(issue);

        // newIssue is constructed from an external Issue object.
        // It won't have the transient state that its TurboIssue
        // counterpart has, so we have to explicitly transfer it.
        newIssue.transferTransientState(existingIssue);
        newIssue.reconcile(existingIssue);

        existingCopy.set(correspondingIssueIndex.get(), newIssue);
      }
    }
    return existingCopy;
  }
예제 #9
0
  /**
   * Tests that replaceIssueMilestone finds issue with the right id and successfully modify the
   * issue's milestone
   */
  @Test
  public void replaceIssueMilestone_successful() {
    Optional<Integer> milestoneIdReplacement = Optional.of(1);
    String repoId = "testowner/testrepo";

    TurboIssue issue1 = LogicTests.createIssueWithMilestone(1, Optional.of(0));
    TurboIssue issue2 = LogicTests.createIssueWithMilestone(2, Optional.of(1));
    TurboIssue issue3 = LogicTests.createIssueWithMilestone(3, Optional.of(1));
    List<TurboIssue> issues = Arrays.asList(issue3, issue2, issue1);

    Model model =
        new Model(
            repoId,
            issues,
            new ArrayList<TurboLabel>(),
            new ArrayList<TurboMilestone>(),
            new ArrayList<TurboUser>());
    Optional<TurboIssue> result =
        model.replaceIssueMilestone(issue1.getId(), milestoneIdReplacement);
    assertEquals(1, result.get().getId());
    assertTrue(result.get().getMilestone().isPresent());
    assertEquals(milestoneIdReplacement, result.get().getMilestone());
  }
예제 #10
0
 /**
  * Determines if an issue has had new comments added (or removed) based on its last-known comment
  * count in {@link #issueCommentCounts}.
  *
  * @param issue
  * @return true if the issue has changed, false otherwise
  */
 private boolean issueHasNewComments(TurboIssue issue, boolean hasMetadata) {
   if (currentFilterExpression.getQualifierNames().contains(Qualifier.UPDATED) && hasMetadata) {
     return issueNonSelfCommentCounts.containsKey(issue.getId())
         && Math.abs(
                 issueNonSelfCommentCounts.get(issue.getId())
                     - issue.getMetadata().getNonSelfCommentCount())
             > 0;
   } else {
     return issueCommentCounts.containsKey(issue.getId())
         && Math.abs(issueCommentCounts.get(issue.getId()) - issue.getCommentCount()) > 0;
   }
 }
예제 #11
0
 /**
  * Updates {@link #issueCommentCounts} with the latest counts. Returns a list of issues which have
  * new comments.
  *
  * @return
  */
 private HashSet<Integer> updateIssueCommentCounts(boolean hasMetadata) {
   HashSet<Integer> result = new HashSet<>();
   for (TurboIssue issue : getIssueList()) {
     if (issueCommentCounts.containsKey(issue.getId())) {
       // We know about this issue; check if it's been updated
       if (issueHasNewComments(issue, hasMetadata)) {
         result.add(issue.getId());
       }
     } else {
       // We don't know about this issue, just put the current comment count.
       issueNonSelfCommentCounts.put(issue.getId(), issue.getMetadata().getNonSelfCommentCount());
       issueCommentCounts.put(issue.getId(), issue.getCommentCount());
     }
   }
   return result;
 }
예제 #12
0
  // Copy constructor
  public TurboIssue(TurboIssue issue) {
    this.id = issue.id;
    this.title = issue.title;
    this.creator = issue.creator;
    this.createdAt = issue.createdAt;
    this.isPullRequest = issue.isPullRequest;

    this.description = issue.description;
    this.updatedAt = replaceNull(issue.updatedAt, this.createdAt);
    this.commentCount = issue.commentCount;
    this.isOpen = issue.isOpen;
    this.assignee = issue.assignee;
    this.labels = new ArrayList<>(issue.labels);
    this.milestone = issue.milestone;

    this.metadata = issue.metadata;
    this.repoId = issue.repoId;
    this.markedReadAt = issue.markedReadAt;
    this.labelsLastModifiedAt = Optional.of(issue.getLabelsLastModifiedAt());
  }
예제 #13
0
  private void setupListView() {
    setVgrow(listView, Priority.ALWAYS);
    setupKeyboardShortcuts();

    listView.setOnItemSelected(
        i -> {
          TurboIssue issue = listView.getItems().get(i);
          ui.triggerEvent(
              new IssueSelectedEvent(
                  issue.getRepoId(), issue.getId(), panelIndex, issue.isPullRequest()));

          // Save the stored comment count as its own comment count.
          // The refreshItems(false) call that follows will remove the highlighted effect of the
          // comment bubble.
          // (if it was there before)
          issueCommentCounts.put(issue.getId(), issue.getCommentCount());
          issueNonSelfCommentCounts.put(
              issue.getId(), issue.getMetadata().getNonSelfCommentCount());
          // We assume we already have metadata, so we pass true to avoid refreshItems from trying
          // to get
          // metadata after clicking.
          refreshItems(true);
        });
  }
 private boolean isExistingAssignee(TurboIssue issue, PickerAssignee assignee) {
   if (!issue.getAssignee().isPresent()) return false;
   return issue.getAssignee().get().equals(assignee.getLoginName());
 }
예제 #15
0
  private void setupKeyboardShortcuts() {
    filterTextField.addEventHandler(
        KeyEvent.KEY_RELEASED,
        event -> {
          if (KeyboardShortcuts.BOX_TO_LIST.match(event)) {
            event.consume();
            listView.selectFirstItem();
          }
          if (event.getCode() == KeyboardShortcuts.DOUBLE_PRESS) {
            event.consume();
          }
          if (KeyPress.isDoublePress(KeyboardShortcuts.DOUBLE_PRESS, event.getCode())) {
            event.consume();
            listView.selectFirstItem();
          }
          if (KeyboardShortcuts.MAXIMIZE_WINDOW.match(event)) {
            ui.maximizeWindow();
          }
          if (KeyboardShortcuts.MINIMIZE_WINDOW.match(event)) {
            ui.minimizeWindow();
          }
          if (KeyboardShortcuts.DEFAULT_SIZE_WINDOW.match(event)) {
            ui.setDefaultWidth();
          }
          if (KeyboardShortcuts.SWITCH_DEFAULT_REPO.match(event)) {
            ui.switchDefaultRepo();
          }
        });

    addEventHandler(
        KeyEvent.KEY_RELEASED,
        event -> {
          if (event.getCode() == KeyboardShortcuts.markAsRead) {
            Optional<TurboIssue> item = listView.getSelectedItem();
            if (!item.isPresent()) {
              return;
            }
            TurboIssue issue = item.get();
            LocalDateTime now = LocalDateTime.now();
            ui.prefs.setMarkedReadAt(issue.getRepoId(), issue.getId(), now);
            issue.setMarkedReadAt(Optional.of(now));
            issue.setIsCurrentlyRead(true);
            parentPanelControl.refresh();
            listView.selectNextItem();
          }
          if (event.getCode() == KeyboardShortcuts.markAsUnread) {
            Optional<TurboIssue> item = listView.getSelectedItem();
            if (!item.isPresent()) {
              return;
            }
            TurboIssue issue = item.get();
            ui.prefs.clearMarkedReadAt(issue.getRepoId(), issue.getId());
            issue.setMarkedReadAt(Optional.empty());
            issue.setIsCurrentlyRead(false);
            parentPanelControl.refresh();
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_DOCS) {
            ui.getBrowserComponent().showDocs();
          }
          if (KeyboardShortcuts.LIST_TO_BOX.match(event)) {
            setFocusToFilterBox();
          }
          if (event.getCode() == KeyboardShortcuts.DOUBLE_PRESS
              && KeyPress.isDoublePress(KeyboardShortcuts.DOUBLE_PRESS, event.getCode())) {

            setFocusToFilterBox();
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_ISSUES) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().showIssues();
            }
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_PULL_REQUESTS) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().showPullRequests();
            }
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_HELP) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().showDocs();
            }
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_KEYBOARD_SHORTCUTS) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().showKeyboardShortcuts();
            }
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_CONTRIBUTORS) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().showContributors();
              event.consume();
            }
          }
          if (event.getCode() == KeyboardShortcuts.scrollToTop) {
            ui.getBrowserComponent().scrollToTop();
          }
          if (event.getCode() == KeyboardShortcuts.scrollToBottom) {
            if (!KeyboardShortcuts.MINIMIZE_WINDOW.match(event)) {
              ui.getBrowserComponent().scrollToBottom();
            }
          }
          if (event.getCode() == KeyboardShortcuts.scrollUp
              || event.getCode() == KeyboardShortcuts.scrollDown) {
            ui.getBrowserComponent().scrollPage(event.getCode() == KeyboardShortcuts.scrollDown);
          }
          if (event.getCode() == KeyboardShortcuts.GOTO_MODIFIER) {
            KeyPress.setLastKeyPressedCodeAndTime(event.getCode());
          }
          if (event.getCode() == KeyboardShortcuts.NEW_COMMENT
              && ui.getBrowserComponent().isCurrentUrlIssue()) {
            ui.getBrowserComponent().jumpToComment();
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_LABELS) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().newLabel();
            } else {
              ui.triggerEvent(new ShowLabelPickerEvent(getSelectedIssue()));
            }
          }
          if (event.getCode() == KeyboardShortcuts.MANAGE_ASSIGNEES
              && ui.getBrowserComponent().isCurrentUrlIssue()) {
            ui.getBrowserComponent().manageAssignees(event.getCode().toString());
          }
          if (event.getCode() == KeyboardShortcuts.SHOW_MILESTONES) {
            if (KeyPress.isValidKeyCombination(KeyboardShortcuts.GOTO_MODIFIER, event.getCode())) {
              ui.getBrowserComponent().showMilestones();
            } else if (ui.getBrowserComponent().isCurrentUrlIssue()) {
              ui.getBrowserComponent().manageMilestones(event.getCode().toString());
            }
          }
          if (KeyboardShortcuts.MAXIMIZE_WINDOW.match(event)) {
            ui.maximizeWindow();
          }
          if (KeyboardShortcuts.MINIMIZE_WINDOW.match(event)) {
            ui.minimizeWindow();
          }
          if (KeyboardShortcuts.DEFAULT_SIZE_WINDOW.match(event)) {
            ui.setDefaultWidth();
          }
          if (KeyboardShortcuts.SWITCH_DEFAULT_REPO.match(event)) {
            ui.switchDefaultRepo();
          }
        });
  }
예제 #16
0
  @Test
  public void getters() {
    // ID
    assertEquals(REPO, modelUpdated.getRepoId());
    assertEquals(modelUpdated.getRepoId(), modelUpdated.getRepoId());

    // Signature
    assertEquals(true, modelEmptySig.getUpdateSignature().isEmpty());
    assertEquals(modelEmptySig.getUpdateSignature(), UpdateSignature.EMPTY);
    assertEquals(modelEmptySig.getUpdateSignature(), modelEmptySig2.getUpdateSignature());

    // Resources
    // Issues
    ArrayList<Integer> issueIds = new ArrayList<>();
    for (int i = 1; i <= DummyRepoState.NO_OF_DUMMY_ISSUES; i++) {
      issueIds.add(i);
    }
    Collections.sort(issueIds); // 1, 2..10
    int issueCount = 1;
    for (TurboIssue issue : modelUpdated.getIssues()) {
      assertEquals(issueCount, modelUpdated.getIssueById(issueCount).get().getId());
      assertEquals(issueIds.get(issueCount - 1).intValue(), issue.getId());
      issueCount++;
    }

    // Labels
    ArrayList<String> labelNames = new ArrayList<>();
    for (int i = 1; i <= DummyRepoState.NO_OF_DUMMY_ISSUES; i++) {
      labelNames.add("Label " + i);
    }
    Collections.sort(labelNames); // Label 1, Label 10..12, Label 2..9
    int labelCount = 1;
    for (TurboLabel label : modelUpdated.getLabels()) {
      if (label.getFullName().startsWith("Label")) {
        assertEquals(labelNames.get(labelCount - 1), label.getFullName());
        assertEquals(
            "Label " + labelCount,
            modelUpdated.getLabelByActualName("Label " + labelCount).get().getFullName());
        labelCount++;
      }
    }

    // Milestones
    ArrayList<Integer> milestoneIds = new ArrayList<>();
    for (int i = 1; i <= DummyRepoState.NO_OF_DUMMY_ISSUES; i++) {
      milestoneIds.add(i);
    }
    Collections.sort(milestoneIds); // 1, 2..10
    int milestoneCount = 1;
    for (TurboMilestone milestone : modelUpdated.getMilestones()) {
      assertEquals(milestoneCount, milestone.getId());
      assertEquals(
          milestoneIds.get(milestoneCount - 1).intValue(),
          modelUpdated.getMilestoneById(milestoneCount).get().getId());
      assertEquals(
          "Milestone " + milestoneCount,
          modelUpdated.getMilestoneByTitle("Milestone " + milestoneCount).get().getTitle());
      milestoneCount++;
    }

    // Users
    ArrayList<String> userLogins = new ArrayList<>();
    for (int i = 1; i <= DummyRepoState.NO_OF_DUMMY_ISSUES; i++) {
      userLogins.add("User " + i);
    }
    Collections.sort(userLogins); // User 1, User 10, User 2..9
    int userCount = 1;
    for (TurboUser user : modelUpdated.getUsers()) {
      assertEquals(userLogins.get(userCount - 1), user.getLoginName());
      assertEquals(
          "User " + userCount,
          modelUpdated.getUserByLogin("User " + userCount).get().getLoginName());
      userCount++;
    }
  }