Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete Trips #82

Merged
merged 16 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions frontend/src/components/ViewTrips/delete-trip-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from 'react';

import app from '../Firebase/';
import Button from 'react-bootstrap/Button';

import * as DB from '../../constants/database.js';
zghera marked this conversation as resolved.
Show resolved Hide resolved

const db = app.firestore();
const LIMIT_QUERY_DOCS_RETRIEVED = 5;

/**
* Component used to delete a Trip.
*
*
* @param {Object} props These are the props for this component:
* - tripId: Document ID for the current Trip document.
* - refreshTripsContainer: Handler that refreshes the TripsContainer
* component upon trip creation (Remove when fix Issue #62).
*/
const DeleteTripsButton = (props) => {
/**
* Deletes documents in query with a batch delete.
*
* This was taken from the delete collection snippets in the documentation
* at https://firebase.google.com/docs/firestore/manage-data/delete-data.
*
* @param {firebase.firestore.Firestore} db Firestore database instance.
* @param {firebase.firestore.Query} query Query containing documents from
* the activities subcollection of a trip documents.
* @param {Function} resolve Resolve function that returns a void Promise.
*/
zghera marked this conversation as resolved.
Show resolved Hide resolved
async function deleteQueryBatch(db, query, resolve) {
zghera marked this conversation as resolved.
Show resolved Hide resolved
const snapshot = await query.get();

const batchSize = snapshot.size;
if (batchSize === 0) {
// When there are no documents left, we are done
resolve();
return;
}

// Delete documents in a batch
zghera marked this conversation as resolved.
Show resolved Hide resolved
const batch = db.batch();
snapshot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
await batch.commit();

// Recurse on the next process tick, to avoid
// exploding the stack.
process.nextTick(() => {
deleteQueryBatch(db, query, resolve);
});
}

/**
* Deletes a trips subcollection of activities corrsponding to the
zghera marked this conversation as resolved.
Show resolved Hide resolved
* `tripId` prop.
*
* This was adapted from the delete collection snippets in the documentation
* at https://firebase.google.com/docs/firestore/manage-data/delete-data.
*
* TODO(Issue #81): Consider deleting data with callabable cloud function
zghera marked this conversation as resolved.
Show resolved Hide resolved
* https://firebase.google.com/docs/firestore/solutions/delete-collections
zghera marked this conversation as resolved.
Show resolved Hide resolved
*/
async function deleteTripActivities() {
const query = db.collection(DB.COLLECTION_TRIPS)
.doc(props.tripId)
.collection(DB.COLLECTION_ACTIVITIES)
.orderBy(DB.ACTIVITIES_TITLE)
.limit(LIMIT_QUERY_DOCS_RETRIEVED);

return new Promise((resolve, reject) => {
deleteQueryBatch(db, query, resolve).catch(reject);
});
}

/**
* Deletes a trip and its subcollection of activities corrsponding to the
* `tripId` prop and then refreshes the TripsContainer component.
*
* TODO(Issue #62): Remove refreshTripsContainer.
*/
async function deleteTrip() {
if (window.confirm('Are you sure you want to delete this trip? This' +
' action cannot be undone!')) {
await deleteTripActivities()
.then(() => {
console.log("Activity subcollection successfully deleted for trip" +
" with id: ", props.tripId);
anan-ya-y marked this conversation as resolved.
Show resolved Hide resolved
})
.catch(error => {
console.error("Error deleting activities subcollection: ", error);
zghera marked this conversation as resolved.
Show resolved Hide resolved
});

db.collection(DB.COLLECTION_TRIPS)
.doc(props.tripId)
.delete()
.then(() => {
console.log("Document successfully deleted with id: ", props.tripId);
zghera marked this conversation as resolved.
Show resolved Hide resolved
}).catch(error => {
console.error("Error removing document: ", error);
});

props.refreshTripsContainer();
}
}

return (
<Button
type='button'
variant='primary'
onClick={deleteTrip} >
Delete Trip
</Button>
);
}

export default DeleteTripsButton;
1 change: 1 addition & 0 deletions frontend/src/components/ViewTrips/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ViewTrips extends React.Component {
</div>
<TripsContainer
handleEditTrip={this.showEditTripModal}
refreshTripsContainer={this.refreshTripsContainer}
key={this.state.refreshTripsContainer}
/>
</div>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/ViewTrips/trip.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Button from 'react-bootstrap/Button';

import { timestampToISOString } from '../Utils/time.js';
import { getUserEmailFromUid } from '../Utils/temp-auth-utils.js';
import DeleteTripButton from './delete-trip-button.js';
import ViewActivitiesButton from './view-activities-button.js';

/**
Expand Down Expand Up @@ -51,6 +52,8 @@ export function getCollaboratorEmails(collaboratorUidArr) {
* - tripData: Object holding a Trip document fields and corresponding values.
* - tripId: Document ID for the current Trip document.
* - handleEditTrip: Handler that displays the edit trip modal.
* - refreshTripsContainer: Handler that refreshes the TripsContainer
* component upon trip creation (Remove when fix Issue #62).
zghera marked this conversation as resolved.
Show resolved Hide resolved
* - key: Special React attribute that ensures a new Trip instance is
* created whenever this key is updated
*/
Expand Down Expand Up @@ -78,6 +81,10 @@ const Trip = (props) => {
<p>{description}</p>
<p>{collaboratorEmailsStr}</p>

<DeleteTripButton
tripId={props.tripId}
refreshTripsContainer={props.refreshTripsContainer}
/>
<Button
type='button'
onClick={() => props.handleEditTrip(props.tripId, placeholderObj)}
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/components/ViewTrips/trips-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,21 @@ function queryUserTrips(db) {
* `<Trip>` elements as defined in `trip.js`.
*
* @param {Promise<!firebase.firestore.QuerySnapshot>} querySnapshot Promise
* object containing the query results with zero or more Trip documents.
* object containing the query results with zero or more Trip documents.
* @param {EventHandler} handleEditTrip Displays the edit trip modal.
* @param {EventHandler} refreshTripsContainer Refreshed the TripsContainer
* component (Remove when fix Issue #62).
* @return {Promise<!Array<Trip>>} Promise object containing an array
* of Trip React/HTML elements corresponding to the Trip documents included
* in `querySnapshot`.
* of Trip React/HTML elements corresponding to the Trip documents included
* in `querySnapshot`.
*/
function serveTrips(querySnapshot, handleEditTrip) {
function serveTrips(querySnapshot, handleEditTrip, refreshTripsContainer) {
return new Promise(function(resolve) {
const tripsContainer = querySnapshot.docs.map(doc =>
( <Trip tripData={doc.data()} tripId={doc.id}
handleEditTrip={handleEditTrip} key={doc.id} />
handleEditTrip={handleEditTrip}
refreshTripsContainer={refreshTripsContainer}
key={doc.id} />
zghera marked this conversation as resolved.
Show resolved Hide resolved
)
);
resolve(tripsContainer);
Expand All @@ -66,6 +70,8 @@ function getErrorElement(error) {
*
* @param {Object} props These are the props for this component:
* - handleEditTrip: Handler that displays the edit trip modal.
* - refreshTripsContainer: Handler that refreshes the TripsContainer
* component upon trip creation (Remove when fix Issue #62).
zghera marked this conversation as resolved.
Show resolved Hide resolved
* - key: Special React attribute that ensures a new TripsContainer instance is
* created whenever this key is updated (Remove when fix Issue #62).
* @extends React.Component
Expand All @@ -82,7 +88,8 @@ class TripsContainer extends React.Component {
try {
const querySnapshot = await queryUserTrips(db);
let tripsContainer = await serveTrips(querySnapshot,
this.props.handleEditTrip);
this.props.handleEditTrip,
this.props.refreshTripsContainer);
this.setState({ trips: tripsContainer });
}
catch (error) {
Expand Down