Skip to content

Commit

Permalink
Merge pull request #27 from aaronyhsoh/master
Browse files Browse the repository at this point in the history
Added favourite contact feature
  • Loading branch information
Hailinx authored Oct 19, 2017
2 parents b979f92 + 703228b commit 1bda361
Show file tree
Hide file tree
Showing 20 changed files with 299 additions and 18 deletions.
28 changes: 28 additions & 0 deletions docs/UserGuide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,34 @@ Selects the 2nd person in the address book.
`select 1` +
Selects the 1st person in the results of the `find` command.

=== Favouriting a person : `favourite`

Favourites the person identified by the index number used in the last person listing. +
If person identified is already a favourite, the person is no longer a favourite.
Format: `favourite INDEX`

****
* Changes the font colour of the person's name at the specified `INDEX` to red.
* Shifts the person up to the top of the list.
* The index refers to the index number shown in the most recent listing.
* The index *must be a positive integer* `1, 2, 3, ...`
****

Examples:

* `list` +
`favourite 2` +
Favourites the 2nd person in the address book.
* `find Betsy` +
`favourite 1` +
Favourites the 1st person in the results of the `find` command.
* `list` +
`favourite 1` +
Favourites the 1st person in the address book. +
`favourite 1` +
Unfavourite the 1st person in the address book.


=== Listing entered commands : `history`

Lists all the commands that you have entered in reverse chronological order. +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import seedu.address.model.person.exceptions.PersonNotFoundException;

/**
* Deletes a person identified using it's last displayed index from the address book.
* Deletes a person identified using its last displayed index from the address book.
*/
public class DeleteAltCommand extends UndoableCommand {

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/seedu/address/logic/commands/EditCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ private static Person createEditedPerson(ReadOnlyPerson personToEdit,
Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
Set<Tag> updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
boolean originalFavourite = personToEdit.getFavourite();

return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags, originalFavourite);
}

@Override
Expand Down
114 changes: 114 additions & 0 deletions src/main/java/seedu/address/logic/commands/FavouriteCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;

import java.util.List;
import java.util.Set;

import seedu.address.commons.core.Messages;
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
import seedu.address.model.person.ReadOnlyPerson;
import seedu.address.model.person.exceptions.DuplicatePersonException;
import seedu.address.model.person.exceptions.PersonNotFoundException;
import seedu.address.model.tag.Tag;

/**
* Favourites an exisiting contact
*/
public class FavouriteCommand extends UndoableCommand {

public static final String COMMAND_WORD = "favourite";
public static final String COMMAND_ALIAS = "fav";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to your favourite contacts "
+ "by the index number used in the last person listing.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1 ";

public static final String MESSAGE_FAVOURITE_PERSON_SUCCESS = "Added person to favourites: %1$s";
public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
public static final String MESSAGE_UNFAVOURITE_PERSON_SUCCESS = "Person removed from favourites: ";

private final Index index;


/**
* @param index of the person in the filtered person list to edit
*/
public FavouriteCommand(Index index) {
requireNonNull(index);

this.index = index;
}

@Override
public CommandResult executeUndoableCommand() throws CommandException {
List<ReadOnlyPerson> lastShownList = model.getFilteredPersonList();

if (index.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}

ReadOnlyPerson personToFavourite = lastShownList.get(index.getZeroBased());

Person favouritedPerson = createFavouritedPerson(personToFavourite);

try {
model.favouritePerson(personToFavourite, favouritedPerson);
} catch (DuplicatePersonException dpe) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
} catch (PersonNotFoundException pnfe) {
throw new AssertionError("The target person cannot be missing");
}
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);

if (personToFavourite.getFavourite()) {
return new CommandResult(String.format(MESSAGE_UNFAVOURITE_PERSON_SUCCESS, favouritedPerson));
} else {
return new CommandResult(String.format(MESSAGE_FAVOURITE_PERSON_SUCCESS, favouritedPerson));
}

}

/**
* Creates and returns a {@code Person} with the details of {@code personToFavourite}
*/
private static Person createFavouritedPerson(ReadOnlyPerson personToFavourite) {
assert personToFavourite != null;

Name originalName = personToFavourite.getName();
Phone originalPhone = personToFavourite.getPhone();
Email originalEmail = personToFavourite.getEmail();
Address originalAddress = personToFavourite.getAddress();
Set<Tag> originalTags = personToFavourite.getTags();
boolean newFavourite = !personToFavourite.getFavourite();

return new Person(originalName, originalPhone, originalEmail, originalAddress,
originalTags, newFavourite);
}

@Override
public boolean equals(Object other) {
// short circuit if same object
if (other == this) {
return true;
}

// instanceof handles nulls
if (!(other instanceof FavouriteCommand)) {
return false;
}

// state check
FavouriteCommand e = (FavouriteCommand) other;
return index.equals(e.index);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ public AddCommand parse(String args) throws ParseException {
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL)).get();
Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS)).get();
Set<Tag> tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
boolean favourite = false;

ReadOnlyPerson person = new Person(name, phone, email, address, tagList);
ReadOnlyPerson person = new Person(name, phone, email, address, tagList, favourite);

return new AddCommand(person);
} catch (IllegalValueException ive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FavouriteCommand;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.HistoryCommand;
Expand Down Expand Up @@ -116,6 +117,10 @@ public Command parseCommand(String userInput) throws ParseException {
case RedoCommand.COMMAND_ALIAS:
return new RedoCommand();

case FavouriteCommand.COMMAND_WORD:
case FavouriteCommand.COMMAND_ALIAS:
return new FavouriteCommandParser().parse(arguments);

case SortCommand.COMMAND_WORD:
return new SortCommandParser().parse(arguments);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package seedu.address.logic.parser;

import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;

import seedu.address.commons.core.index.Index;
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.logic.commands.FavouriteCommand;
import seedu.address.logic.parser.exceptions.ParseException;

/**
* Parses input arguments and creates a new DeleteCommand object
*/
public class FavouriteCommandParser implements Parser<FavouriteCommand> {

/**
* Parses the given {@code String} of arguments in the context of the FavouriteCommand
* and returns an FavouriteCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public FavouriteCommand parse(String args) throws ParseException {
try {
Index index = ParserUtil.parseIndex(args);
return new FavouriteCommand(index);
} catch (IllegalValueException ive) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FavouriteCommand.MESSAGE_USAGE));
}
}

}
36 changes: 36 additions & 0 deletions src/main/java/seedu/address/model/AddressBook.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,49 @@ public void updatePerson(ReadOnlyPerson target, ReadOnlyPerson editedReadOnlyPer
requireNonNull(editedReadOnlyPerson);

Person editedPerson = new Person(editedReadOnlyPerson);

syncMasterTagListWith(editedPerson);
// TODO: the tags master list will be updated even though the below line fails.
// This can cause the tags master list to have additional tags that are not tagged to any person
// in the person list.
persons.setPerson(target, editedPerson);

}
/**
* Replaces the given person {@code target} in the list with {@code favouritedReadOnlyPerson}.
* Sorts the list to show favourite contacts first.
* {@code AddressBook}'s tag list will be updated with the tags of {@code favouritedReadOnlyPerson}.
*
* @throws PersonNotFoundException if {@code target} could not be found in the list.
*
* @see #syncMasterTagListWith(Person)
*/
public void favouritePerson(ReadOnlyPerson target, ReadOnlyPerson favouritedReadOnlyPerson)
throws DuplicatePersonException, PersonNotFoundException {
requireNonNull(favouritedReadOnlyPerson);

Person favouritedPerson = new Person(favouritedReadOnlyPerson);

syncMasterTagListWith(favouritedPerson);
// TODO: the tags master list will be updated even though the below line fails.
// This can cause the tags master list to have additional tags that are not tagged to any person
// in the person list.
persons.setPerson(target, favouritedPerson);

UniquePersonList notFavouriteList = new UniquePersonList();
UniquePersonList favouriteList = new UniquePersonList();
for (ReadOnlyPerson person : persons) {
if (person.getFavourite()) {
favouriteList.add(person);
} else {
notFavouriteList.add(person);
}
}
persons.setPersons(favouriteList);
for (ReadOnlyPerson person : notFavouriteList) {
persons.add(person);
}
}
/**
* Ensures that every tag in this person:
* - exists in the master list {@link #tags}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/seedu/address/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public interface Model {
void updatePerson(ReadOnlyPerson target, ReadOnlyPerson editedPerson)
throws DuplicatePersonException, PersonNotFoundException;

void favouritePerson(ReadOnlyPerson target, ReadOnlyPerson favouritedPerson)
throws DuplicatePersonException, PersonNotFoundException;

/** Returns an unmodifiable view of the filtered person list */
ObservableList<ReadOnlyPerson> getFilteredPersonList();

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/seedu/address/model/ModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public void updatePerson(ReadOnlyPerson target, ReadOnlyPerson editedPerson)
indicateAddressBookChanged();
}

@Override
public void favouritePerson(ReadOnlyPerson target, ReadOnlyPerson favouritedPerson)
throws DuplicatePersonException, PersonNotFoundException {
requireAllNonNull(target, favouritedPerson);

addressBook.favouritePerson(target, favouritedPerson);
indicateAddressBookChanged();
}

@Override
public void sortPerson(String option) throws NoPersonFoundException {
requireNonNull(option);
Expand Down
20 changes: 16 additions & 4 deletions src/main/java/seedu/address/model/person/Person.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,29 @@ public class Person implements ReadOnlyPerson {

private ObjectProperty<UniqueTagList> tags;

private boolean favourite;

/**
* Every field must be present and not null.
*/
public Person(Name name, Phone phone, Email email, Address address, Set<Tag> tags) {


public Person(Name name, Phone phone, Email email, Address address, Set<Tag> tags, boolean favourite) {
requireAllNonNull(name, phone, email, address, tags);
this.name = new SimpleObjectProperty<>(name);
this.phone = new SimpleObjectProperty<>(phone);
this.email = new SimpleObjectProperty<>(email);
this.address = new SimpleObjectProperty<>(address);
// protect internal tags from changes in the arg list
this.tags = new SimpleObjectProperty<>(new UniqueTagList(tags));
this.favourite = favourite;
}

/**
* Creates a copy of the given ReadOnlyPerson.
*/
public Person(ReadOnlyPerson source) {
this(source.getName(), source.getPhone(), source.getEmail(), source.getAddress(),
source.getTags());
source.getTags(), source.getFavourite());
}

public void setName(Name name) {
Expand Down Expand Up @@ -122,6 +126,14 @@ public void setTags(Set<Tag> replacement) {
tags.set(new UniqueTagList(replacement));
}

public boolean getFavourite() {
return favourite;
}

public void setFavourite(boolean favourite) {
this.favourite = favourite;
}

@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
Expand All @@ -132,7 +144,7 @@ public boolean equals(Object other) {
@Override
public int hashCode() {
// use this method for custom fields hashing instead of implementing your own
return Objects.hash(name, phone, email, address, tags);
return Objects.hash(name, phone, email, address, tags, favourite);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public interface ReadOnlyPerson {
Address getAddress();
ObjectProperty<UniqueTagList> tagProperty();
Set<Tag> getTags();
boolean getFavourite();

/**
* Returns true if both have the same state. (interfaces cannot override .equals)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
* @see CollectionUtil#elementsAreUnique(Collection)
*/
public class UniquePersonList implements Iterable<Person> {

private final ObservableList<Person> internalList = FXCollections.observableArrayList();
// used by asObservableList()
private final ObservableList<ReadOnlyPerson> mappedList = EasyBind.map(internalList, (person) -> person);
Expand Down
Loading

0 comments on commit 1bda361

Please sign in to comment.