diff --git a/bin/require.pip b/bin/require.pip
index d691a00e5d..e9f2315b2d 100644
--- a/bin/require.pip
+++ b/bin/require.pip
@@ -3,3 +3,4 @@ GitPython==2.1.3
requests==2.18.2
sarge==0.1.4
pytest==3.2.1
+psycopg2==2.7.5
diff --git a/docs/METRICS.md b/docs/METRICS.md
index b65666c95e..96a89e5764 100644
--- a/docs/METRICS.md
+++ b/docs/METRICS.md
@@ -290,11 +290,6 @@ These are events that an add-on user can encounter on a shot they own
8. [x] Confirm delete from shot index `web/delete/my-shots-popup-confirm`
9. [x] Cancel delete from shot index, `web/cancel-delete/my-shots-popup-confirm`
7. [x] Click My Shots `web/goto-myshots/navbar`
-11. [x] Try to change expiration time `web/start-expiration-change/navbar`
- 12. [x] Cancel changing expiration time `web/cancel-expiration-change/navbar`
- 13. [x] Change expiration time `web/set-expiration/navbar`
- 14. [x] Change expiration time to specific time `web/set-expiration-to-time/navbar`
- 15. [x] Change expiration time to indefinite `web/set-expiration-to-indefinite/navbar`
16. [x] View expired shot `web/view-expired/owner`
17. [x] Recover expired shot `web/recover-expired`
19. [x] Visit original page `web/view-original/navbar-owner`
@@ -316,6 +311,8 @@ These are events that an add-on user can encounter on a shot they own
33. [x] Visit an image directly, when the image isn't embedded directly in a Screenshots shot page, `web/visit/direct-view-owner`
34. [x] View an image directly, when the image is being shown as part of a Facebook/Twitter style preview (the og:image or twitter:image), `web/visit/direct-view-embedded-owner`
35. [x] Close new edit tools promotion dialog, `web/promo-closed`
+1. [x] Add shot to favorites `web/set-favorite/navbar`
+1. [x] Remove shot from favorites `web-unset-favorite/navbar`
#### Shot Index (My Shots)
diff --git a/locales/en-US/server.ftl b/locales/en-US/server.ftl
index 30d7967ab8..3d5da4b2b0 100644
--- a/locales/en-US/server.ftl
+++ b/locales/en-US/server.ftl
@@ -289,6 +289,11 @@ shotIndexPageNextPage =
# language/culture).
shotIndexNoExpirationSymbol = ∞
.title = This shot does not expire
+# This is the tooltip for a "heart" symbol in the lower right corner of the
+# card for a shot on the My Shots page. It indicate that the shot was marked as
+# a favorite by the owner.
+shotIndexFavoriteIcon =
+ .title = This is a favorite shot and it does not expire
## Delete Confirmation Dialog
diff --git a/server/src/pages/shot/model.js b/server/src/pages/shot/model.js
index e896ffb2ed..81b7fe5a6f 100644
--- a/server/src/pages/shot/model.js
+++ b/server/src/pages/shot/model.js
@@ -15,6 +15,7 @@ exports.createModel = function(req) {
}
const title = req.getText("shotPageTitle", {originalTitle: req.shot.title});
const enableAnnotations = req.config.enableAnnotations;
+ const isFxaAuthenticated = req.accountId && req.accountId === req.shot.accountId;
const serverPayload = {
title,
staticLink: req.staticLink,
@@ -25,7 +26,8 @@ exports.createModel = function(req) {
id: req.shot.id,
productName: req.config.productName,
isExtInstalled: !!req.deviceId,
- isOwner: req.deviceId === req.shot.ownerId || (req.accountId && req.accountId === req.shot.accountId),
+ isOwner: req.deviceId === req.shot.ownerId || isFxaAuthenticated,
+ isFxaAuthenticated,
gaId: req.config.gaId,
deviceId: req.deviceId,
authenticated: !!req.deviceId,
@@ -54,7 +56,8 @@ exports.createModel = function(req) {
id: req.shot.id,
productName: req.config.productName,
isExtInstalled: !!req.deviceId,
- isOwner: req.deviceId === req.shot.ownerId || (req.accountId && req.accountId === req.shot.accountId),
+ isOwner: req.deviceId === req.shot.ownerId || isFxaAuthenticated,
+ isFxaAuthenticated,
gaId: req.config.gaId,
deviceId: req.deviceId,
authenticated: !!req.deviceId,
diff --git a/server/src/pages/shot/view.js b/server/src/pages/shot/view.js
index 780ad42893..411808ccfc 100644
--- a/server/src/pages/shot/view.js
+++ b/server/src/pages/shot/view.js
@@ -176,7 +176,6 @@ class Body extends React.Component {
this.state = {
hidden: false,
closeBanner: false,
- isChangingExpire: false,
imageEditing: false
};
}
@@ -342,19 +341,21 @@ class Body extends React.Component {
const linkTextShort = shot.urlDisplay;
const timeDiff = ;
- let expiresDiff = null;
- if (this.props.isOwner) {
- expiresDiff =
-
- ;
- }
+ let favoriteShotButton = null;
let trashOrFlagButton;
let editButton;
const highlight = this.state.highlightEditButton ?