1
Fork 0

Add set password from share dropdown in sidebar

This commit is contained in:
viktorstrate 2020-06-14 15:07:07 +02:00
parent abfcea9072
commit bbb6e2eca2
5 changed files with 423 additions and 90 deletions

View File

@ -76,6 +76,7 @@ type ComplexityRoot struct {
DeleteShareToken func(childComplexity int, token string) int
DeleteUser func(childComplexity int, id int) int
InitialSetupWizard func(childComplexity int, username string, password string, rootPath string) int
ProtectShareToken func(childComplexity int, token string, password *string) int
RegisterUser func(childComplexity int, username string, password string, rootPath string) int
ScanAll func(childComplexity int) int
ScanUser func(childComplexity int, userID int) int
@ -161,12 +162,13 @@ type ComplexityRoot struct {
}
ShareToken struct {
Album func(childComplexity int) int
Expire func(childComplexity int) int
ID func(childComplexity int) int
Owner func(childComplexity int) int
Photo func(childComplexity int) int
Token func(childComplexity int) int
Album func(childComplexity int) int
Expire func(childComplexity int) int
HasPassword func(childComplexity int) int
ID func(childComplexity int) int
Owner func(childComplexity int) int
Photo func(childComplexity int) int
Token func(childComplexity int) int
}
SiteInfo struct {
@ -204,6 +206,7 @@ type MutationResolver interface {
ShareAlbum(ctx context.Context, albumID int, expire *time.Time, password *string) (*models.ShareToken, error)
SharePhoto(ctx context.Context, photoID int, expire *time.Time, password *string) (*models.ShareToken, error)
DeleteShareToken(ctx context.Context, token string) (*models.ShareToken, error)
ProtectShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error)
UpdateUser(ctx context.Context, id int, username *string, rootPath *string, password *string, admin *bool) (*models.User, error)
CreateUser(ctx context.Context, username string, rootPath string, password *string, admin bool) (*models.User, error)
DeleteUser(ctx context.Context, id int) (*models.User, error)
@ -230,6 +233,7 @@ type QueryResolver interface {
type ShareTokenResolver interface {
Owner(ctx context.Context, obj *models.ShareToken) (*models.User, error)
HasPassword(ctx context.Context, obj *models.ShareToken) (bool, error)
Album(ctx context.Context, obj *models.ShareToken) (*models.Album, error)
Photo(ctx context.Context, obj *models.ShareToken) (*models.Photo, error)
}
@ -413,6 +417,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.InitialSetupWizard(childComplexity, args["username"].(string), args["password"].(string), args["rootPath"].(string)), true
case "Mutation.protectShareToken":
if e.complexity.Mutation.ProtectShareToken == nil {
break
}
args, err := ec.field_Mutation_protectShareToken_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.ProtectShareToken(childComplexity, args["token"].(string), args["password"].(*string)), true
case "Mutation.registerUser":
if e.complexity.Mutation.RegisterUser == nil {
break
@ -893,6 +909,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ShareToken.Expire(childComplexity), true
case "ShareToken.hasPassword":
if e.complexity.ShareToken.HasPassword == nil {
break
}
return e.complexity.ShareToken.HasPassword(childComplexity), true
case "ShareToken.id":
if e.complexity.ShareToken.ID == nil {
break
@ -1117,6 +1140,8 @@ type Mutation {
sharePhoto(photoId: Int!, expire: Time, password: String): ShareToken
"Delete a share token by it's token value"
deleteShareToken(token: String!): ShareToken
"Set a password for a token, if null is passed for the password argument, the password will be cleared"
protectShareToken(token: String!, password: String): ShareToken
updateUser(
id: Int!
@ -1178,6 +1203,8 @@ type ShareToken {
owner: User!
"Optional expire date"
expire: Time
"Whether or not a password is needed to access the share"
hasPassword: Boolean!
"The album this token shares"
album: Album
@ -1437,6 +1464,28 @@ func (ec *executionContext) field_Mutation_initialSetupWizard_args(ctx context.C
return args, nil
}
func (ec *executionContext) field_Mutation_protectShareToken_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["token"]; ok {
arg0, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["token"] = arg0
var arg1 *string
if tmp, ok := rawArgs["password"]; ok {
arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg1
return args, nil
}
func (ec *executionContext) field_Mutation_registerUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -2548,6 +2597,44 @@ func (ec *executionContext) _Mutation_deleteShareToken(ctx context.Context, fiel
return ec.marshalOShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_protectShareToken(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Mutation_protectShareToken_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().ProtectShareToken(rctx, args["token"].(string), args["password"].(*string))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*models.ShareToken)
fc.Result = res
return ec.marshalOShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_updateUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -4713,6 +4800,40 @@ func (ec *executionContext) _ShareToken_expire(ctx context.Context, field graphq
return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
}
func (ec *executionContext) _ShareToken_hasPassword(ctx context.Context, field graphql.CollectedField, obj *models.ShareToken) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "ShareToken",
Field: field,
Args: nil,
IsMethod: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.ShareToken().HasPassword(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _ShareToken_album(ctx context.Context, field graphql.CollectedField, obj *models.ShareToken) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -6311,6 +6432,8 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
out.Values[i] = ec._Mutation_sharePhoto(ctx, field)
case "deleteShareToken":
out.Values[i] = ec._Mutation_deleteShareToken(ctx, field)
case "protectShareToken":
out.Values[i] = ec._Mutation_protectShareToken(ctx, field)
case "updateUser":
out.Values[i] = ec._Mutation_updateUser(ctx, field)
case "createUser":
@ -6896,6 +7019,20 @@ func (ec *executionContext) _ShareToken(ctx context.Context, sel ast.SelectionSe
})
case "expire":
out.Values[i] = ec._ShareToken_expire(ctx, field, obj)
case "hasPassword":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._ShareToken_hasPassword(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "album":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {

View File

@ -3,9 +3,10 @@ package resolvers
import (
"context"
"database/sql"
"errors"
"time"
"github.com/pkg/errors"
api "github.com/viktorstrate/photoview/api/graphql"
"github.com/viktorstrate/photoview/api/graphql/auth"
"github.com/viktorstrate/photoview/api/graphql/models"
@ -54,6 +55,11 @@ func (r *shareTokenResolver) Photo(ctx context.Context, obj *models.ShareToken)
return photo, nil
}
func (r *shareTokenResolver) HasPassword(ctx context.Context, obj *models.ShareToken) (bool, error) {
hasPassword := obj.Password != nil
return hasPassword, nil
}
func (r *queryResolver) ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error) {
row := r.Database.QueryRow("SELECT * FROM share_token WHERE value = ? AND (password = ? OR password IS NULL)", token, password)
@ -131,14 +137,9 @@ func (r *mutationResolver) SharePhoto(ctx context.Context, photoID int, expire *
}
rows.Close()
var hashed_password *string = nil
if password != nil {
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
if err != nil {
return nil, err
}
hashed_str := string(hashedPassBytes)
hashed_password = &hashed_str
hashed_password, err := hashSharePassword(password)
if err != nil {
return nil, err
}
token := utils.GenerateToken()
@ -169,7 +170,59 @@ func (r *mutationResolver) DeleteShareToken(ctx context.Context, tokenValue stri
return nil, auth.ErrUnauthorized
}
row := r.Database.QueryRow(`
token, err := getUserToken(r.Database, user, tokenValue)
if err != nil {
return nil, err
}
if _, err := r.Database.Exec("DELETE FROM share_token WHERE token_id = ?", token.TokenID); err != nil {
return nil, errors.Wrapf(err, "Error occurred when trying to delete share token (%s) from database", tokenValue)
}
return token, nil
}
func (r *mutationResolver) ProtectShareToken(ctx context.Context, tokenValue string, password *string) (*models.ShareToken, error) {
user := auth.UserFromContext(ctx)
if user == nil {
return nil, auth.ErrUnauthorized
}
token, err := getUserToken(r.Database, user, tokenValue)
if err != nil {
return nil, err
}
hashed_password, err := hashSharePassword(password)
if err != nil {
return nil, err
}
_, err = r.Database.Exec("UPDATE share_token SET password = ? WHERE token_id = ?", hashed_password, token.TokenID)
if err != nil {
return nil, errors.Wrap(err, "Failed to update password for share token")
}
updatedToken := r.Database.QueryRow("SELECT * FROM share_token WHERE value = ?", tokenValue)
return models.NewShareTokenFromRow(updatedToken)
}
func hashSharePassword(password *string) (*string, error) {
var hashed_password *string = nil
if password != nil {
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
if err != nil {
return nil, err
}
hashed_str := string(hashedPassBytes)
hashed_password = &hashed_str
}
return hashed_password, nil
}
func getUserToken(db *sql.DB, user *models.User, tokenValue string) (*models.ShareToken, error) {
row := db.QueryRow(`
SELECT share_token.* FROM share_token, user WHERE
share_token.value = ? AND
share_token.owner_id = user.user_id AND
@ -181,9 +234,5 @@ func (r *mutationResolver) DeleteShareToken(ctx context.Context, tokenValue stri
return nil, err
}
if _, err := r.Database.Exec("DELETE FROM share_token WHERE token_id = ?", token.TokenID); err != nil {
return nil, err
}
return token, nil
}

View File

@ -71,6 +71,8 @@ type Mutation {
sharePhoto(photoId: Int!, expire: Time, password: String): ShareToken
"Delete a share token by it's token value"
deleteShareToken(token: String!): ShareToken
"Set a password for a token, if null is passed for the password argument, the password will be cleared"
protectShareToken(token: String!, password: String): ShareToken
updateUser(
id: Int!
@ -132,6 +134,8 @@ type ShareToken {
owner: User!
"Optional expire date"
expire: Time
"Whether or not a password is needed to access the share"
hasPassword: Boolean!
"The album this token shares"
album: Album

90
ui/package-lock.json generated
View File

@ -5563,25 +5563,25 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"resolved": "",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"resolved": "",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
},
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"resolved": "",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"resolved": "",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"optional": true,
"requires": {
@ -5591,13 +5591,13 @@
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"resolved": "",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"resolved": "",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true,
"requires": {
@ -5613,25 +5613,25 @@
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"resolved": "",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"resolved": "",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"resolved": "",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"resolved": "",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"optional": true
},
@ -5646,19 +5646,19 @@
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"resolved": "",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"resolved": "",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"resolved": "",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
},
@ -5673,13 +5673,13 @@
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"resolved": "",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"optional": true
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"resolved": "",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true,
"requires": {
@ -5709,13 +5709,13 @@
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"resolved": "",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"resolved": "",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"optional": true,
"requires": {
@ -5733,7 +5733,7 @@
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"resolved": "",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"optional": true,
"requires": {
@ -5749,13 +5749,13 @@
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"resolved": "",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"resolved": "",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": {
@ -5764,13 +5764,13 @@
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"resolved": "",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"resolved": "",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true,
"requires": {
@ -5842,7 +5842,7 @@
},
"nopt": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
"resolved": "",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"optional": true,
"requires": {
@ -5877,7 +5877,7 @@
},
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"resolved": "",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true,
"requires": {
@ -5889,19 +5889,19 @@
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"resolved": "",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"optional": true
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"resolved": "",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"optional": true
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"resolved": "",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": {
@ -5910,19 +5910,19 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"resolved": "",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"resolved": "",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"optional": true
},
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"resolved": "",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"optional": true,
"requires": {
@ -5932,7 +5932,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"optional": true
},
@ -5944,7 +5944,7 @@
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"resolved": "",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"optional": true,
"requires": {
@ -5964,7 +5964,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"optional": true,
"requires": {
@ -5988,19 +5988,19 @@
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"resolved": "",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"resolved": "",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"optional": true
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"resolved": "",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true
},
@ -6012,19 +6012,19 @@
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"resolved": "",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"resolved": "",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"optional": true
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"resolved": "",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": {
@ -6035,7 +6035,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"resolved": "",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"optional": true,
"requires": {
@ -6044,7 +6044,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": {
@ -6053,7 +6053,7 @@
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"resolved": "",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"optional": true
},
@ -6074,13 +6074,13 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"resolved": "",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"resolved": "",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"optional": true,
"requires": {
@ -6089,7 +6089,7 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"resolved": "",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
},

View File

@ -1,8 +1,15 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { useMutation, useQuery } from 'react-apollo'
import gql from 'graphql-tag'
import { Table, Button, Dropdown } from 'semantic-ui-react'
import {
Table,
Button,
Dropdown,
Checkbox,
Input,
Icon,
} from 'semantic-ui-react'
import copy from 'copy-to-clipboard'
const sharePhotoQuery = gql`
@ -11,6 +18,7 @@ const sharePhotoQuery = gql`
id
shares {
token
hasPassword
}
}
}
@ -22,6 +30,7 @@ const shareAlbumQuery = gql`
id
shares {
token
hasPassword
}
}
}
@ -43,6 +52,15 @@ const addAlbumShareMutation = gql`
}
`
const protectShareMutation = gql`
mutation sidebarProtectShare($token: String!, $password: String) {
protectShareToken(token: $token, password: $password) {
token
hasPassword
}
}
`
const deleteShareMutation = gql`
mutation sidebareDeleteShare($token: String!) {
deleteShareToken(token: $token) {
@ -51,6 +69,153 @@ const deleteShareMutation = gql`
}
`
const ShareItemMoreDropdown = ({ id, share, isPhoto }) => {
const query = isPhoto ? sharePhotoQuery : shareAlbumQuery
const [deleteShare, { loading: deleteShareLoading }] = useMutation(
deleteShareMutation,
{
refetchQueries: [{ query: query, variables: { id } }],
}
)
const [addingPassword, setAddingPassword] = useState(false)
const showPasswordInput = addingPassword || share.hasPassword
const [passwordInputValue, setPasswordInputValue] = useState(
share.hasPassword ? '**********' : ''
)
const [passwordHidden, setPasswordHidden] = useState(share.hasPassword)
const hidePassword = hide => {
setPasswordHidden(hide)
if (hide) {
setPasswordInputValue('**********')
}
}
const [setPassword, { loading: setPasswordLoading }] = useMutation(
protectShareMutation,
{
refetchQueries: [{ query: query, variables: { id } }],
onCompleted: data => {
console.log('data', data)
hidePassword(data.protectShareToken.hasPassword)
},
// refetchQueries: [{ query: query, variables: { id } }],
variables: {
token: share.token,
},
}
)
let addPasswordInput = null
if (showPasswordInput) {
const setPasswordEvent = event => {
if (!passwordHidden && passwordInputValue != '' && event.key == 'Enter') {
event.preventDefault()
setPassword({
variables: {
password: event.target.value,
},
})
}
}
addPasswordInput = (
<Input
disabled={setPasswordLoading}
loading={setPasswordLoading}
style={{ marginTop: 8, marginRight: 0, display: 'block' }}
onClick={e => e.stopPropagation()}
value={passwordInputValue}
type={passwordHidden ? 'password' : 'text'}
onKeyUp={setPasswordEvent}
onChange={event => {
hidePassword(false)
setPasswordInputValue(event.target.value)
}}
placeholder="Password"
icon={
<Icon
name={passwordHidden ? 'lock' : 'arrow right'}
link={!passwordHidden}
onClick={setPasswordEvent}
/>
}
/>
)
}
const checkboxClick = () => {
const enable = !showPasswordInput
setAddingPassword(enable)
if (!enable) {
setPassword({
variables: {
password: null,
},
})
setPasswordInputValue('')
}
}
// const [dropdownOpen, setDropdownOpen] = useState(false)
return (
<Dropdown
// onBlur={event => {
// console.log('Blur')
// }}
// onClick={() => setDropdownOpen(state => !state)}
// onClose={() => setDropdownOpen(false)}
// open={dropdownOpen}
button
text="More"
closeOnChange={false}
closeOnBlur={false}
>
<Dropdown.Menu>
<Dropdown.Item
onKeyDown={e => e.stopPropagation()}
onClick={e => {
e.stopPropagation()
checkboxClick()
}}
>
<Checkbox
label="Password"
onClick={e => e.stopPropagation()}
checked={showPasswordInput}
onChange={() => {
checkboxClick()
}}
/>
{addPasswordInput}
</Dropdown.Item>
<Dropdown.Item
text="Delete"
icon="delete"
disabled={deleteShareLoading}
onClick={() => {
deleteShare({
variables: {
token: share.token,
},
})
}}
/>
</Dropdown.Menu>
</Dropdown>
)
}
ShareItemMoreDropdown.propTypes = {
id: PropTypes.number.isRequired,
isPhoto: PropTypes.bool.isRequired,
share: PropTypes.object.isRequired,
}
const SidebarShare = ({ photo, album }) => {
if ((!photo || !photo.id) && (!album || !album.id)) return null
if (!localStorage.getItem('token')) return null
@ -71,13 +236,6 @@ const SidebarShare = ({ photo, album }) => {
variables: { id },
})
const [deleteShare, { loading: deleteShareLoading }] = useMutation(
deleteShareMutation,
{
refetchQueries: [{ query: query, variables: { id } }],
}
)
const [sharePhoto, { loading: sharePhotoLoading }] = useMutation(
addShareMutation,
{
@ -112,22 +270,7 @@ const SidebarShare = ({ photo, album }) => {
copy(`${location.origin}/share/${share.token}`)
}}
/>
<Dropdown button text="More">
<Dropdown.Menu>
<Dropdown.Item
text="Delete"
icon="delete"
disabled={deleteShareLoading}
onClick={() => {
deleteShare({
variables: {
token: share.token,
},
})
}}
/>
</Dropdown.Menu>
</Dropdown>
<ShareItemMoreDropdown share={share} id={id} isPhoto={isPhoto} />
</Button.Group>
</Table.Cell>
</Table.Row>