Frontend: Add photos to new album

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2019-12-17 04:39:23 +01:00
parent 1cc8cefc92
commit 4ab44c5c23
11 changed files with 130 additions and 44 deletions

View file

@ -103,6 +103,13 @@
</v-list-tile-content>
</v-list-tile>
<!-- v-list-tile v-if="config.albums.length === 0"
@click.stop="createAlbum">
<v-list-tile-content>
<v-list-tile-title>Create Album</v-list-tile-title>
</v-list-tile-content>
</v-list-tile -->
<v-list-tile v-for="(album, index) in config.albums"
:key="index"
:to="{ name: 'album', params: { uuid: album.AlbumUUID, slug: album.AlbumSlug } }">
@ -225,6 +232,9 @@
</template>
<script>
import Album from "../model/album";
import {DateTime} from "luxon";
export default {
name: "p-navigation",
data() {
@ -240,10 +250,17 @@
};
},
methods: {
showNavigation: function () {
showNavigation () {
this.drawer = true;
this.mini = false;
},
createAlbum() {
let name = DateTime.local().toFormat("LLLL yyyy");
const album = new Album({AlbumName: name, AlbumFavorite: true});
album.save().then((a) => {
console.log("created", a)
});
},
logout() {
this.$session.logout();
},

View file

@ -77,7 +77,7 @@
@click.stop="dialog.album = true"
class="p-photo-clipboard-album"
>
<v-icon>create_new_folder</v-icon>
<v-icon>folder</v-icon>
</v-btn>
<v-btn
@ -105,7 +105,7 @@
v-if="album"
class="p-photo-clipboard-delete"
>
<v-icon>remove_circle</v-icon>
<v-icon>delete_outline</v-icon>
</v-btn>
<v-btn
fab

View file

@ -4,12 +4,15 @@
<v-container fluid class="pb-2 pr-2 pl-2">
<v-layout row wrap>
<v-flex xs3 text-xs-center>
<v-icon size="54" color="grey lighten-1">folder</v-icon>
<v-icon size="54" color="grey lighten-1" v-if="newAlbum">create_new_folder</v-icon>
<v-icon size="54" color="grey lighten-1" v-else>folder</v-icon>
</v-flex>
<v-flex xs9 text-xs-left align-self-center>
<v-autocomplete
v-model="album"
:items="albums"
browser-autocomplete="off"
hint="Album Name"
:items="items"
:search-input.sync="search"
:loading="loading"
hide-details
@ -26,7 +29,9 @@
<translate>Cancel</translate>
</v-btn>
<v-btn color="blue-grey lighten-2" depressed dark @click.stop="confirm"
class="p-photo-dialog-confirm"><translate>Add to album</translate>
class="p-photo-dialog-confirm">
<span v-if="newAlbum">{{ labels.createAlbum }}</span>
<span v-else>{{ labels.addToAlbum }}</span>
</v-btn>
</v-flex>
</v-layout>
@ -44,12 +49,16 @@
},
data() {
return {
loading: true,
loading: false,
search: null,
newAlbum: null,
album: "",
albums: [],
items: [],
labels: {
select: this.$gettext("Select album"),
addToAlbum: this.$gettext("Add to album"),
createAlbum: this.$gettext("Create album"),
}
}
},
@ -58,29 +67,59 @@
this.$emit('cancel');
},
confirm() {
this.$emit('confirm', this.album);
if(this.album === "" && this.newAlbum) {
console.log("NEW", this.album, this.newAlbum);
this.loading = true;
this.newAlbum.save().then((a) => {
this.loading = false;
this.$emit('confirm', a.AlbumUUID);
});
} else {
console.log("OLD", this.album, this.newAlbum);
this.$emit('confirm', this.album);
}
},
},
updated() {
if(this.albums.length > 0) {
this.loading = false;
return;
}
const params = {
q: "",
count: 1000,
offset: 0,
};
Album.search(params).then(response => {
if(response.models.length > 0) {
this.album = response.models[0].AlbumUUID;
queryServer(q) {
if(this.loading) {
return;
}
this.albums = response.models;
this.loading = false;
});
this.loading = true;
const params = {
q: q,
count: 1000,
offset: 0,
};
Album.search(params).then(response => {
this.loading = false;
if(response.models.length > 0 && !this.album) {
this.album = response.models[0].AlbumUUID;
}
this.albums = response.models;
this.items = [...this.albums];
}).catch(() => this.loading = false);
},
},
watch: {
search (q) {
const exists = this.albums.findIndex((album) => album.AlbumName === q);
if (exists !== -1 || !q) {
this.items = this.albums;
this.newAlbum = null;
} else {
this.newAlbum = new Album({AlbumName: q, AlbumUUID: ""});
this.items = this.albums.concat([this.newAlbum]);
}
},
},
created() {
this.queryServer("");
},
}
</script>

View file

@ -94,6 +94,7 @@
methods: {
viewType() {
let queryParam = this.$route.query['view'];
let defaultType = window.localStorage.getItem("photo_view_type");
let storedType = window.localStorage.getItem("album_view_type");
if (queryParam) {
@ -101,6 +102,8 @@
return queryParam;
} else if (storedType) {
return storedType;
} else if (defaultType) {
return defaultType;
} else if (window.innerWidth < 960) {
return 'mosaic';
} else if (window.innerWidth > 1600) {

View file

@ -132,6 +132,7 @@
import Album from "model/album";
import {DateTime} from "luxon";
import Util from "common/util";
import Event from "pubsub-js";
export default {
name: 'p-page-albums',
@ -156,6 +157,7 @@
const settings = {};
return {
subId: null,
results: [],
loading: true,
scrollDisabled: true,
@ -313,10 +315,17 @@
} else {
this.selection.push(uuid)
}
},
onCount() {
// TODO
}
},
created() {
this.search();
this.subId = Event.subscribe("count.albums", (ev, data) => this.onCount(ev, data));
},
destroyed() {
Event.unsubscribe(this.subId);
},
};
</script>

View file

@ -223,17 +223,17 @@
}).catch(() => this.loading = false);
},
onKeypress(event) {
if (event.key === "Escape") {
/* if (event.key === "Escape") {
this.$clipboard.clear();
}
} */
},
},
created() {
this.search();
window.addEventListener('keydown', this.onKeypress);
// window.addEventListener('keydown', this.onKeypress);
},
destroyed() {
window.removeEventListener('keydown', this.onKeypress);
// window.removeEventListener('keydown', this.onKeypress);
}
};
</script>

View file

@ -80,6 +80,9 @@ func CreateAlbum(router *gin.RouterGroup, conf *config.Config) {
}
m := entity.NewAlbum(f.AlbumName)
m.AlbumFavorite = f.AlbumFavorite
log.Debugf("create album: %+v %+v", f, m)
if res := conf.Db().Create(m); res.Error != nil {
log.Error(res.Error.Error())
@ -93,6 +96,8 @@ func CreateAlbum(router *gin.RouterGroup, conf *config.Config) {
event.Success(fmt.Sprintf("album \"%s\" created", m.AlbumName))
// event.Publish("config.updated", event.Data(conf.ClientConfig()))
c.JSON(http.StatusOK, m)
})
}

View file

@ -127,7 +127,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
if !ok {
log.Errorf("invalid type: %s", typeName)
c.Data(http.StatusBadRequest, "image/svg+xml", photoIconSvg)
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
return
}
@ -145,7 +145,8 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
file, err := r.FindLabelThumbByUUID(labelUUID)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": util.UcFirst(err.Error())})
log.Errorf(err.Error())
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
return
}
@ -153,7 +154,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
if !util.Exists(fileName) {
log.Errorf("could not find original for thumbnail: %s", fileName)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore
file.FileMissing = true
@ -166,7 +167,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
if err != nil {
log.Errorf("could not read thumbnail: %s", err)
c.Data(http.StatusInternalServerError, "image/svg+xml", photoIconSvg)
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
return
}
@ -178,7 +179,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
} else {
log.Errorf("could not create thumbnail: %s", err)
c.Data(http.StatusInternalServerError, "image/svg+xml", photoIconSvg)
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
return
}
})

View file

@ -65,13 +65,13 @@ func TestLabelThumbnail(t *testing.T) {
LabelThumbnail(router, ctx)
result := PerformRequest(app, "GET", "/api/v1/labels/dog/thumbnail/xxx")
assert.Equal(t, http.StatusBadRequest, result.Code)
assert.Equal(t, http.StatusOK, result.Code)
})
t.Run("invalid label", func(t *testing.T) {
app, router, ctx := NewApiTest()
LabelThumbnail(router, ctx)
result := PerformRequest(app, "GET", "/api/v1/labels/xxx/thumbnail/tile_500")
assert.Equal(t, http.StatusNotFound, result.Code)
assert.Equal(t, http.StatusOK, result.Code)
})
}

View file

@ -9,3 +9,6 @@ var photoIconSvg = []byte(`
var albumIconSvg = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
<path d="M0 0h24v24H0z" fill="none"/></svg>`)
var labelIconSvg = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg>`)

View file

@ -63,17 +63,26 @@ func (s *Repo) FindLabelThumbBySlug(labelSlug string) (file entity.File, err err
// FindLabelThumbByUUID returns a label preview file based on the label UUID.
func (s *Repo) FindLabelThumbByUUID(labelUUID string) (file entity.File, err error) {
// s.db.LogMode(true)
if err := s.db.Where("files.file_primary AND files.deleted_at IS NULL").
// Search matching label
err = s.db.Where("files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN labels ON labels.label_uuid = ?", labelUUID).
Joins("JOIN photos_labels ON photos_labels.label_id = labels.id AND photos_labels.photo_id = files.photo_id").
Order("photos_labels.label_uncertainty ASC").
First(&file).Error; err != nil {
return file, err
First(&file).Error
if err == nil {
return file, nil
}
return file, nil
// If failed, search for category instead
err = s.db.Where("files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN photos_labels ON photos_labels.photo_id = files.photo_id").
Joins("JOIN categories c ON photos_labels.label_id = c.label_id").
Joins("JOIN labels ON c.category_id = labels.id AND labels.label_uuid= ?", labelUUID).
Order("photos_labels.label_uncertainty ASC").
First(&file).Error
return file, err
}
// Labels searches labels based on their name.