Overview
ɯeme is a meme manager app for those who prefer to use a desktop app for managing memes. More importantly, ɯeme is optimized for those who prefer to work with a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). Users can view, tag, search, import and export a collection of meme. They can also create their own memes from meme templates.
Summary of contributions
-
Major enhancement: added the ability to undo/redo previous commands
-
What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
-
Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
-
Highlights: This enhancement affects existing and future commands, as well as the design of the model of the application. The inclusion of files, as we are handling meme images, complicates a lot of logic with the existing commands. Commands that were easy to implement such as
add
were not trivial to undo when local files are being manipulated. As a result, many alternatives had to be considered before arriving to a well-planned conclusion. A thorough understanding of how the entire application functioned was necessary to ensure that the undo/redo implementation syncs well with existing and future commands, making the design of the feature much more complicated. -
Credits: Basic idea from AB4 implementation.
-
-
Minor enhancement:
-
Code contributed: [RepoSense Code]
-
Other contributions:
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Undoing previous commands : undo
Undoes commands and tells you the command you just undid.
Format: undo
Example: undo
Redoing previously undone commands: redo
Redoes previously undone commands and tells you the command you just redid.
Format: redo
Example: redo
Listing archived memes: archives
Lists all memes that are archived in the memes tab.
Format: archives
Archiving a meme: archive
Archives the meme at the specified index. Hides the meme from the default view.
Format: archive INDEX
Unarchiving a meme: unarchive
Unarchives the meme at the specified index. Shows the meme in the default view.
Format: unarchive INDEX
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Undo/Redo feature
The Undo and Redo commands are necessary to give users the flexibility of undoing or redoing a wrongly executed command. Especially in Weme where we deal with image files, it is possible to key in the wrong file when adding a meme. Hence, simply entering the command undo
allows the user to revert this mistake and add the correct file accordingly.
Current implementation
The undo/redo mechanism is facilitated by VersionedWeme
.
VersionedWeme
extends Weme
with an undo/redo history, stored internally as a versionedWemeStates
, stateIndex
and a feedbackList
.
Additionally, it implements the following operations:
-
VersionedWeme#commit()
— Saves the current Weme state in its history. -
VersionedWeme#undo()
— Restores the previous Weme state from its history and returns the feedback message of the undone command. -
VersionedWeme#redo()
— Restores a previously undone Weme state from its history and returns the feedback message of the redone command.
These operations are exposed in the Model
interface as Model#commitWeme()
, Model#undoWeme()
and Model#redoWeme()
respectively.
Only state changes on the internal structure of Weme are undoable. |
Commands such as list
, find
that only change the user interface, commands such as export
and load
that are related to external files, as well as commands such as edit
and delete
in the import tab that modifies the import list are not supported.
These are the list of commands that support undo / redo operations:
-
Memes Tab:
add
,edit
,delete
,clear
,archive
,unarchive
,like
,dislike
,stage
-
Templates Tab:
add
,edit
,delete
,clear
,archive
,unarchive
,use
-
Create Tab:
add
,edit
,delete
,move
,abort
,create
-
Export Tab:
unstage
,clear
-
Import Tab:
import
undo
and redo
works between tabs. This means that if you make a change in the Memes tab, by editing a meme, and then you switch to the Templates tab, when you execute undo
, it reverts the change in the Memes tab as well. However, undo
/redo
is not usable while viewing a meme.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedWeme
will be initialized with the initial Weme state, and the stateIndex
pointing to that single Weme state.
Step 2. The user executes delete 5
command to delete the 5th meme in the meme list. The delete
command calls Model#commitWeme()
with the success feedback message as a parameter, causing the modified state of the Weme after the delete 5
command executes to be saved in the versionedWemeStates
, the stateIndex
is shifted to the newly inserted Weme state, and finally the delete command’s feedback message is inserted into the feedbackList
.
Step 3. The user executes edit 2 d/surprised pikachu
to edit a meme’s description. The edit
command also calls Model#commitWeme()
, causing another modified Weme state to be saved into the versionedWemeStates
.
If a command fails its execution, it will not call Model#commitWeme() , so the Weme state will not be saved into the versionedWemeStates .
|
Step 4. The user now decides that editing the meme was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call Model#undoWeme()
, which will shift the stateIndex
once to the left, pointing it to the previous Weme state, and restores the Weme to that state. The feedback message is then returned to pass into and construct the CommandResult.
If the stateIndex is at index 0, pointing to the initial Weme state, then there are no previous Weme states to restore. The undo command uses Model#canUndoWeme() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.
|
The following sequence diagram shows how the undo operation works:
The lifeline for UndoCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
The redo
command does the opposite — it calls Model#redoWeme()
, which shifts the stateIndex
once to the right, pointing to the previously undone state, and restores the Weme to that state.
If the stateIndex is at index versionedWemeStates.size() - 1 , pointing to the latest Weme state, then there are no undone Weme states to restore. The redo command uses Model#canRedoWeme() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
|
Step 5. The user then decides to execute the command list
. Commands that do not modify the Weme, such as list
, will usually not call Model#commitWeme()
, Model#undoWeme()
or Model#redoWeme()
. Thus, the versionedWemeStates
remains unchanged.
Step 6. The user executes clear
, which calls Model#commitWeme()
. Since the stateIndex
is not pointing at the end of the versionedWemeStates
, all Weme states after the stateIndex
will be purged. We designed it this way because it no longer makes sense to redo the edit 2 d/surprised pikachu
command. This is the behavior that most modern desktop applications follow.
The following activity diagram summarizes what happens when a user executes a new command:
The addition of undo redo complicates certain commands. An example of this complication is when undoing add or delete commands. Originally, deleting a Meme will delete the corresponding image file on the disk. However, this means it is not possible to retrieve the file afterwards when attempting to undo. Hence, the current implementation is to delete the Meme entry in the json, but keep the original image file until Weme is closed. When Weme is closed, a thread will clean up all unreferenced image files in the image folder. This is part of the reason why certain commands such as load
are not supported.
The following sequence diagram shows how the clean up works:
When the handleExit command is called, MainWindow will create a Thread to call logic.cleanUp()
to prevent the GUI from slowing down. The thread then further spawns other threads to clean up the files in the data folder, deleting those images that are not found in the memes and templates list stored on Weme. The cleanTemplateStorage()
part of the UML diagram has been truncated as it is similar to cleanMemeStorage()
.
Design Considerations
Aspect: How undo & redo executes
-
Alternative 1 (current choice): Saves the entire state.
-
Pros: Easy to implement.
-
Cons: May have performance issues in terms of memory usage.
-
-
Alternative 2: Individual command knows how to undo/redo by itself.
-
Pros: Will use less memory (e.g. for
delete
, just save the meme being deleted). -
Cons: We must ensure that the implementation of each individual command are correct. This gets complicated when dealing with files.
-
Aspect: Types of commands to undo
-
Alternative 1 (current choice): Includes only commands that modify the underlying data. (Add, Edit, Clear, Delete)
-
Pros: Only changes that permanently affect the application are reverted.
-
Cons: Might be less intuitive as a user calling
tab templates
thenundo
might expect to revert the Tab command instead.
-
-
Alternative 2: Includes all commands
-
Pros: Intuitive
-
Cons: Might be very troublesome for a user if they want to revert the state instead of the view. More unexpected behaviours as certain commands such as
load
depends on files outside Weme’s data folder. If there is an error on redoing a command, there is no easy way to find out.
-
Aspect: Context for commands to be undoable
-
Alternative 1 (current choice): Allow undoing throughout the application regardless of context.
-
Pros: User in a different context is able to easily undo the state.
-
Cons: User might expect to undo only when they are in the same context. i.e. Undo Meme commands in Meme context.
-
-
Alternative 2: Restrict undoing to its own context
-
Pros: More user intuitive. Commands will only affect their own context.
-
Cons: Heavily complicates the model. Model will then need to keep track of a versioning of every single context. Does not allow for commands such as
create
which affects the Creation tab and Memes tab without many modifications to the existing structure.
-
Aspect: Data structure to support the undo/redo commands
-
Alternative 1 (current choice): Use a list to store the history of past states.
-
Pros: Easy to understand and adjust according to needs. Undo and redo simply moves along the list to change the state.
-
Cons: Clutters up the
Weme
class.
-
-
Alternative 2: Use a wrapper class
-
Pros: Everything will be handled within a single UndoRedoManager class.
-
Cons: Might introduce complications as managing states now needs to go through another class instead of just the model.
-
Aspect: Handling file changes
-
Alternative 1 (current choice): Remove files only on exit.
-
Pros: No need to deal with files when managing commands. Easy to execute add and delete commands without an issue without worrying whether a file is present.
-
Cons: Might take a while to delete if we had a lot of images. (Resolved with threads)
-
-
Alternative 2: Implement a recycle bin to move files to/from on command.
-
Pros: Commands do what they are fully expected to do (Delete deletes the image as well).
-
Cons: Heavily complicates the logic with a need to copy and paste when undoing and redoing. Very difficult to understand and error-prone. Still needs to eventually clear the recycle bin on exit. Repeated work.
-
-
Alternative 3: Make file-related commands undoable.
-
Pros: No need to deal with file manipulation.
-
Cons: Makes undo redo feature a lot more useless as it loses support for certain key commands.
-