branch bow-v2-go updated (5b2a7d6 -> 235505f)
This is an automated email from the git hooks/post-receive script. New change to branch bow-v2-go in repository bow. See https://gitlab.nuiton.org/chorem/bow.git from 5b2a7d6 on ne met plus les actions dans le cookie new 235505f correction policy sur bookmark ajout preference (debut) amelioration affichage bookmark recherche par clique sur tag (et mise a jour zone de recherche) The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 235505ff301ebd30c8797c5de8a7fe323378eb90 Author: Benjamin <poussin@codelutin.com> Date: Sat May 16 01:06:12 2020 +0200 correction policy sur bookmark ajout preference (debut) amelioration affichage bookmark recherche par clique sur tag (et mise a jour zone de recherche) Summary of changes: .gitlab-ci.yml | 3 + cmd/bow/main.go | 8 +- migrate/001_init_schema.sql | 8 +- pkg/http/opensearchResource.go | 2 +- pkg/http/router.go | 45 +++++++---- pkg/repository/database.go | 8 +- pkg/repository/userRepository.go | 2 +- web/package.json | 2 +- web/src/class-component-hooks.js | 8 ++ web/src/components/Bookmark.vue | 89 ++++++++++++++++------ web/src/components/SearchInput.vue | 23 +++--- web/src/components/Tags.vue | 18 ----- web/src/components/bookmark/Alias.vue | 25 ++++++ web/src/components/bookmark/BookmarkDate.vue | 41 ++++++++++ web/src/components/{ => bookmark}/Description.vue | 9 +++ web/src/components/{ => bookmark}/LinkCount.vue | 0 web/src/components/bookmark/Tags.vue | 86 +++++++++++++++++++++ web/src/components/bookmark/Visit.vue | 32 ++++++++ web/src/components/layout/Header.vue | 2 +- web/src/components/preferences/Action.vue | 37 +++++++++ web/src/components/preferences/Actions.vue | 53 +++++++++++++ web/src/components/preferences/MaxResultEditor.vue | 43 +++++++++++ web/src/main.js | 11 +-- web/src/views/Home.vue | 17 +---- web/src/views/Preferences.vue | 28 +++++-- 25 files changed, 492 insertions(+), 108 deletions(-) create mode 100644 web/src/class-component-hooks.js delete mode 100644 web/src/components/Tags.vue create mode 100644 web/src/components/bookmark/Alias.vue create mode 100644 web/src/components/bookmark/BookmarkDate.vue rename web/src/components/{ => bookmark}/Description.vue (78%) rename web/src/components/{ => bookmark}/LinkCount.vue (100%) create mode 100644 web/src/components/bookmark/Tags.vue create mode 100644 web/src/components/bookmark/Visit.vue create mode 100644 web/src/components/preferences/Action.vue create mode 100644 web/src/components/preferences/Actions.vue create mode 100644 web/src/components/preferences/MaxResultEditor.vue -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch bow-v2-go in repository bow. See https://gitlab.nuiton.org/chorem/bow.git commit 235505ff301ebd30c8797c5de8a7fe323378eb90 Author: Benjamin <poussin@codelutin.com> Date: Sat May 16 01:06:12 2020 +0200 correction policy sur bookmark ajout preference (debut) amelioration affichage bookmark recherche par clique sur tag (et mise a jour zone de recherche) --- .gitlab-ci.yml | 3 + cmd/bow/main.go | 8 +- migrate/001_init_schema.sql | 8 +- pkg/http/opensearchResource.go | 2 +- pkg/http/router.go | 45 +++++++---- pkg/repository/database.go | 8 +- pkg/repository/userRepository.go | 2 +- web/package.json | 2 +- web/src/class-component-hooks.js | 8 ++ web/src/components/Bookmark.vue | 89 ++++++++++++++++------ web/src/components/SearchInput.vue | 23 +++--- web/src/components/Tags.vue | 18 ----- web/src/components/bookmark/Alias.vue | 25 ++++++ web/src/components/bookmark/BookmarkDate.vue | 41 ++++++++++ web/src/components/{ => bookmark}/Description.vue | 9 +++ web/src/components/{ => bookmark}/LinkCount.vue | 0 web/src/components/bookmark/Tags.vue | 86 +++++++++++++++++++++ web/src/components/bookmark/Visit.vue | 32 ++++++++ web/src/components/layout/Header.vue | 2 +- web/src/components/preferences/Action.vue | 37 +++++++++ web/src/components/preferences/Actions.vue | 53 +++++++++++++ web/src/components/preferences/MaxResultEditor.vue | 43 +++++++++++ web/src/main.js | 11 +-- web/src/views/Home.vue | 17 +---- web/src/views/Preferences.vue | 28 +++++-- 25 files changed, 492 insertions(+), 108 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2767dd4..a1080b9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,3 +30,6 @@ deploy: - for f in script config secret; do if [ -d $f ]; then FILES="$FILES $f"; fi; done - zip -r $ZIP_NAME $FILES - curl -F "name=$CI_PROJECT_NAME" -F "flavour=$CI_COMMIT_REF_SLUG" -F "target=saas" -F "info=${CI_COMMIT_REF_SLUG/master/latest}(${CI_COMMIT_SHORT_SHA}) - $CI_COMMIT_MESSAGE" -F "composeFile=$STACK_FILE" -F "zip=@$ZIP_NAME" "https://swarm-deployer.cloud.codelutin.com/api/v1/projects?swarm-deployer-to..." + environment: + name: ${CI_BUILD_REF_NAME} + url: https://bookmarks.cl diff --git a/cmd/bow/main.go b/cmd/bow/main.go index 78cd526..6c6dbb2 100644 --- a/cmd/bow/main.go +++ b/cmd/bow/main.go @@ -13,13 +13,19 @@ func main() { databaseURL := os.Getenv("DATABASE_URL") secretKey := os.Getenv("SECRET_KEY") bowPublicURL := os.Getenv("BOW_PUBLIC_URL") + migrateDir := os.Getenv("DATABASE_MIGRATION_DIR") if secretKey == "" { // for dev only secretKey = "AZERTYUIOPQSDFGHJKLMWXCVBN" } + + if migrateDir == "" { + migrateDir = "migrate" + } + log.Println("Init database") - repository.Init(databaseURL, true) + repository.Init(databaseURL, migrateDir, true) utils.JwtInit([]byte(secretKey)) // u := model.BowUser{ID: utils.GenUUID(), Login: "bpoussin", Emails: []string{"benjamin@pouss.in"}, Password: "toto"} diff --git a/migrate/001_init_schema.sql b/migrate/001_init_schema.sql index 6e3e0bc..bdec668 100644 --- a/migrate/001_init_schema.sql +++ b/migrate/001_init_schema.sql @@ -208,13 +208,16 @@ CREATE POLICY bowgroup_access_update ON bowgroup FOR UPDATE USING (admin @> ('{' || current_user || '}')::uuid[]); --- tout le monde peut lire les url et les publicAlias des bookmarks (peut etre faire une table PublicAlias pour les sortir) +-- Seul le propriétaire peut tout faire sur son bookmark CREATE POLICY bookmark_access ON bookmark - USING (true); + FOR ALL + USING (owner = current_user::uuid); -- si un bookmark a comme tag '@toto' et proprietaire '1234' alors il est visible de toutes les utilisateurs -- appartenant (admin, writer, reader) au groupe 'toto' dont l'utilisateur '1234' est admin ou writer CREATE POLICY bookmark_access_group ON bookmark + FOR SELECT + TO person USING ( current_user::uuid in (SELECT unnest(admin || writer || reader) FROM bowgroup WHERE tags @> ('{@' || name || '}')::text[] AND ('{' || owner || '}')::uuid[] <@ (admin || writer))); @@ -236,6 +239,7 @@ CREATE POLICY bowUser_nobody_insert ON bowUser TO nobody WITH CHECK (true); +-- pour lire les urls et les alias publics CREATE POLICY bookmark_nobody_access on bookmark FOR SELECT TO nobody diff --git a/pkg/http/opensearchResource.go b/pkg/http/opensearchResource.go index 013be16..9855b33 100644 --- a/pkg/http/opensearchResource.go +++ b/pkg/http/opensearchResource.go @@ -139,7 +139,7 @@ func getAction(currentUser model.BowUser, ask string) model.Action { } var result model.Action - for _, a := range currentUser.Actions { + for _, a := range actions { prefix := a.Prefix log.Printf("getAction %v == %v %v (%v && %v || %v) \n", ask, result, a, strings.HasPrefix(ask, prefix), result.Prefix == "", len(prefix) > len(result.Prefix)) if strings.HasPrefix(ask, prefix) && (result.Prefix == "" || len(prefix) > len(result.Prefix)) { diff --git a/pkg/http/router.go b/pkg/http/router.go index 69d38a6..04a0c55 100644 --- a/pkg/http/router.go +++ b/pkg/http/router.go @@ -38,20 +38,24 @@ func Start(bowPublicURL string, addr string) { s.HandleFunc("/system/liveness", isAlive).Methods(http.MethodGet, http.MethodOptions) s.HandleFunc("/users", createUser).Methods(http.MethodPost, http.MethodOptions) s.HandleFunc("/users/auth", createAuth).Methods(http.MethodPost, http.MethodOptions) - s.HandleFunc("/users/{id}", getUser).Methods(http.MethodGet, http.MethodOptions) - s.HandleFunc("/users/{id}", deleteUser).Methods(http.MethodDelete, http.MethodOptions) - s.HandleFunc("/users/{id}/auth", createAuth).Methods(http.MethodPost, http.MethodOptions) - s.HandleFunc("/users/{id}/auth", deleteAuth).Methods(http.MethodDelete, http.MethodOptions) - s.HandleFunc("/users/{id}/password", updateUserPassword).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/users/{id}/token", addUserToken).Methods(http.MethodPost, http.MethodOptions) - s.HandleFunc("/users/{id}/unconfirmedemails", addUserUnconfirmedEmail).Methods(http.MethodPost, http.MethodOptions) - s.HandleFunc("/users/{id}/unconfirmedemails/{token}", confirmUserEmail).Methods(http.MethodGet, http.MethodOptions) - s.HandleFunc("/users/{id}/authenticationinfo", updateUserAuthenticationInfo).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/users/{id}/actions", updateUserActions).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/users/{id}/autoscreenshot", updateUserAutoScreenshot).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/users/{id}/autofavicon", updateUserAutoFavicon).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/users/{id}/maxtagincloud", updateUserMaxTagInCloud).Methods(http.MethodPut, http.MethodOptions) - s.HandleFunc("/users/{id}/maxresult", updateUserMaxResult).Methods(http.MethodPut, http.MethodOptions) + + u := s.PathPrefix("/users/{id}").Subrouter() + u.Use(convertCurrentToId) + u.HandleFunc("", getUser).Methods(http.MethodGet, http.MethodOptions) + u.HandleFunc("", deleteUser).Methods(http.MethodDelete, http.MethodOptions) + u.HandleFunc("/auth", createAuth).Methods(http.MethodPost, http.MethodOptions) + u.HandleFunc("/auth", deleteAuth).Methods(http.MethodDelete, http.MethodOptions) + u.HandleFunc("/password", updateUserPassword).Methods(http.MethodPut, http.MethodOptions) + u.HandleFunc("/token", addUserToken).Methods(http.MethodPost, http.MethodOptions) + u.HandleFunc("/unconfirmedemails", addUserUnconfirmedEmail).Methods(http.MethodPost, http.MethodOptions) + u.HandleFunc("/unconfirmedemails/{token}", confirmUserEmail).Methods(http.MethodGet, http.MethodOptions) + u.HandleFunc("/authenticationinfo", updateUserAuthenticationInfo).Methods(http.MethodPut, http.MethodOptions) + u.HandleFunc("/actions", updateUserActions).Methods(http.MethodPut, http.MethodOptions) + u.HandleFunc("/autoscreenshot", updateUserAutoScreenshot).Methods(http.MethodPut, http.MethodOptions) + u.HandleFunc("/autofavicon", updateUserAutoFavicon).Methods(http.MethodPut, http.MethodOptions) + u.HandleFunc("/maxtagincloud", updateUserMaxTagInCloud).Methods(http.MethodPut, http.MethodOptions) + u.HandleFunc("/maxresult", updateUserMaxResult).Methods(http.MethodPut, http.MethodOptions) + s.HandleFunc("/bookmarks", getBookmarks).Methods(http.MethodGet, http.MethodOptions) s.HandleFunc("/bookmarks", addBookmark).Methods(http.MethodPost, http.MethodOptions) s.HandleFunc("/bookmarks/tags", getTags).Methods(http.MethodGet, http.MethodOptions) @@ -60,6 +64,7 @@ func Start(bowPublicURL string, addr string) { s.HandleFunc("/bookmarks/{id}", updateBookmark).Methods(http.MethodPut, http.MethodOptions) s.HandleFunc("/bookmarks/{id}/visit", addOneVisit).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) s.HandleFunc("/bookmarks/{id}/authenticationinfo", updateBookmarkAuthenticationInfo).Methods(http.MethodPut, http.MethodOptions) + s.HandleFunc("/opensearch", doActions).Methods(http.MethodGet, http.MethodPost, http.MethodOptions).Queries(constant.Action, "{ask}") s.HandleFunc("/opensearch", doSuggestion).Methods(http.MethodGet, http.MethodPost, http.MethodOptions).Queries(constant.Suggestion, "{ask}") s.HandleFunc("/opensearch", opensearchFile).Methods(http.MethodGet, http.MethodOptions) @@ -115,6 +120,18 @@ func withoutAuthenticationEndpoint(r *http.Request) bool { return result } +func convertCurrentToId(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + if id == "current" { + currentUser := r.Context().Value(constant.User).(model.BowUser) + mux.Vars(r)["id"] = currentUser.ID + } + + next.ServeHTTP(w, r) + }) +} + func authentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if withoutAuthenticationEndpoint(r) { diff --git a/pkg/repository/database.go b/pkg/repository/database.go index 128b596..b7fb676 100644 --- a/pkg/repository/database.go +++ b/pkg/repository/database.go @@ -33,11 +33,11 @@ var bookmakFields = make(map[string]string) /* Init initialise la connexion a la base en utilisant */ -func Init(databaseURL string, doMigration bool) { +func Init(databaseURL string, migrateDir string, doMigration bool) { computeFieldAvailable() if doMigration { - migrateDatabase(databaseURL) + migrateDatabase(databaseURL, migrateDir) } poolConfig, err := pgxpool.ParseConfig(databaseURL) @@ -96,7 +96,7 @@ func extractErrorLine(source string, position int) (errorLineExtract, error) { return ele, nil } -func migrateDatabase(databaseURL string) { +func migrateDatabase(databaseURL string, migrateDir string) { ctx := context.Background() dbConfig, err := pgx.ParseConfig(databaseURL) @@ -119,7 +119,7 @@ func migrateDatabase(databaseURL string) { data["nobody_password"] = dbConfig.Password migrator.Data = data - err = migrator.LoadMigrations("migrate") + err = migrator.LoadMigrations(migrateDir) if err != nil { log.Fatalf("Error loading migrations:\n %v\n", err) } diff --git a/pkg/repository/userRepository.go b/pkg/repository/userRepository.go index 620fcb5..24d4acb 100644 --- a/pkg/repository/userRepository.go +++ b/pkg/repository/userRepository.go @@ -28,7 +28,7 @@ func UserJSON(currentUser model.BowUser, id string, fields ...string) (string, e askedFields = strings.Join(fields, ", ") } - allFields := strings.ReplaceAll(askedFields, "unconfirmedemailsList,", "") + allFields := strings.ReplaceAll(askedFields, "unconfirmedemails,", "") q := &query{sql: fmt.Sprintf(` WITH unconfirmedemailsList as (select id as eid, jsonb_array_elements(unconfirmedemails)::jsonb->'email' as unconfirmedemailsList from bowuser u where id=$1), diff --git a/web/package.json b/web/package.json index 5d1d465..e265de3 100644 --- a/web/package.json +++ b/web/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "serve": "vue-cli-service serve", + "serve": "vue-cli-service serve --port 7780", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, diff --git a/web/src/class-component-hooks.js b/web/src/class-component-hooks.js new file mode 100644 index 0000000..7dd318c --- /dev/null +++ b/web/src/class-component-hooks.js @@ -0,0 +1,8 @@ +import Component from 'vue-class-component' + +// Register the router hooks with their names +Component.registerHooks([ + 'beforeRouteEnter', + 'beforeRouteLeave', + 'beforeRouteUpdate' +]) diff --git a/web/src/components/Bookmark.vue b/web/src/components/Bookmark.vue index aecb3e7..ecf83cc 100644 --- a/web/src/components/Bookmark.vue +++ b/web/src/components/Bookmark.vue @@ -1,44 +1,58 @@ <template> +<div> <div class="bookmark"> - <LinkCount :bookmarkId="bookmark.id" :link="bookmark.uri"> - <img v-if="bookmark.favicon" :src="hexaToImg(bookmark.favicon)" /> - </LinkCount> - <LinkCount :bookmarkId="bookmark.id" :link="bookmark.uri"> + <LinkCount class="screenshot" :bookmarkId="bookmark.id" :link="bookmark.uri"> <img v-if="bookmark.screenshot" :src="hexaToImg(bookmark.screenshot)" /> </LinkCount> - <LinkCount :bookmarkId="bookmark.id" :link="bookmark.uri"> - {{ bookmark.uri }} - </LinkCount> - <span>{{ bookmark.visit }}</span> - <!-- <span>{{ bookmark.authenticationinfo }}</span> faire afficher une popup de modification de l'objet auth--> - <span>{{ bookmark.importdate || bookmark.creationdate }}</span> - <Description :description="bookmark.description"></Description> - <Tags :tags="bookmark.tags"></Tags> - <!-- <span>{{ bookmark.lang }}</span> --> - <!-- <span>{{ bookmark.owner }}</span> si mon id n'est pas le meme alors c'est le bookmark d'un autre partage par un group --> - <span>{{ bookmark.privatealias }}</span> - <span>{{ bookmark.publicalias }}</span> - <span>{{ bookmark.updatedate }}</span> - <button @click.prevent="edit">Edit</button> + <div class="info"> + <div class="info-header"> + <Visit :visit="bookmark.visit"></Visit> + <LinkCount :bookmarkId="bookmark.id" :link="bookmark.uri"> + <img v-if="bookmark.favicon" :src="hexaToImg(bookmark.favicon)" /> + </LinkCount> + <LinkCount class="uri" :bookmarkId="bookmark.id" :link="bookmark.uri"> + {{ bookmark.uri }} + </LinkCount> + <button @click.prevent="edit">Edit</button> + </div> + <!-- <span>{{ bookmark.authenticationinfo }}</span> faire afficher une popup de modification de l'objet auth--> + <Description :description="bookmark.description"></Description> + <div class="info-footer"> + <BookmarkDate :creationDate="date" :updateDate="bookmark.updatedate"></BookmarkDate> + <Tags :tags="bookmark.tags"></Tags> + <!-- <span>{{ bookmark.lang }}</span> --> + <!-- <span>{{ bookmark.owner }}</span> si mon id n'est pas le meme alors c'est le bookmark d'un autre partage par un group --> + <Aliases :bookmarkId="bookmark.id" :link="bookmark.uri" :aliases="bookmark.privatealias" title="alias privé"></Aliases> + <Aliases :bookmarkId="bookmark.id" :link="bookmark.uri" :aliases="bookmark.publicalias" title="alias public"></Aliases> + </div> + </div> </div> +</div> </template> <script> import { Component, Prop, Vue } from 'vue-property-decorator' -import Tags from '@/components/Tags' -import Description from '@/components/Description' -import LinkCount from '@/components/LinkCount' +import Aliases from '@/components/bookmark/Alias' +import BookmarkDate from '@/components/bookmark/BookmarkDate' +import Tags from '@/components/bookmark/Tags' +import Description from '@/components/bookmark/Description' +import LinkCount from '@/components/bookmark/LinkCount' +import Visit from '@/components/bookmark/Visit' @Component({ name: 'Bookmark', props: ['bookmark'], - components: { Description, LinkCount, Tags } + components: { Aliases, BookmarkDate, Description, LinkCount, Tags, Visit } }) class Bookmark extends Vue { @Prop bookmark + get date() { + return this.bookmark.importdate || this.bookmark.creationdate + } + edit() { - this.$router.push({ name: 'Edit', params: { id: this.bookmark.id }}) + this.$router.push({ name: 'Edit', params: { id: this.bookmark.id } }) } hexaToImg(hexa) { @@ -64,6 +78,7 @@ export default Bookmark .bookmark { display: flex; flex-direction: row; + align-items: stretch; justify-content: space-around; //center; align-items: center; @@ -71,5 +86,31 @@ export default Bookmark background-color: var(--color-bg-bookmark); border: 1px solid #eee; + + .screenshot { + flex-grow: 0; + width: 65px; + margin: 2px; + } + + .info, .uri { + flex-grow: 2; + } + + .info { + display: flex; + flex-direction: column; + + .info-header, .info-footer { + margin: 2px; + display: flex; + flex-direction: row; + justify-content: space-between; + } + } + + Description { + width: 100%; + } } -</style> \ No newline at end of file +</style> diff --git a/web/src/components/SearchInput.vue b/web/src/components/SearchInput.vue index 487e01b..775fee5 100644 --- a/web/src/components/SearchInput.vue +++ b/web/src/components/SearchInput.vue @@ -20,7 +20,7 @@ </template> <script> -import { Component, Vue } from 'vue-property-decorator' +import { Component, Vue, Watch } from 'vue-property-decorator' @Component({ name: 'SearchInput', @@ -75,13 +75,13 @@ class SearchInput extends Vue { console.log('search', this.mQuery) } - update() { - this.tags = this.$route.query.tags - this.fulltext = this.$route.query.fulltext - this.query = this.$route.query.query - this.orderby = this.$route.query.orderby - this.orderdesc = this.$route.query.orderdesc - this.first = this.$route.query.first + update(route) { + this.tags = route.query.tags + this.fulltext = route.query.fulltext + this.query = route.query.query + this.orderby = route.query.orderby + this.orderdesc = route.query.orderdesc + this.first = route.query.first this.mTags = this.tags || '' this.mFulltext = this.fulltext || '' @@ -91,9 +91,10 @@ class SearchInput extends Vue { this.mFirst = this.first || 0 } - beforeMount() { - console.log('beforeMount SearchInput') - this.update() + @Watch('$route', { immediate: true, deep: true }) + onRouteChange(to, from) { + console.log('onRouteChange SearchInput', to, from) + this.update(to) } } diff --git a/web/src/components/Tags.vue b/web/src/components/Tags.vue deleted file mode 100644 index 2bf67d2..0000000 --- a/web/src/components/Tags.vue +++ /dev/null @@ -1,18 +0,0 @@ -<template> - <div class="tags">{{ (tags || []).join(" ") }}</div> -</template> - -<script> -import { Component, Prop, Vue } from 'vue-property-decorator' - -@Component({ - name: 'Tags', - props: ['tags'], - components: {} -}) -class Tags extends Vue { - @Prop tags -} - -export default Tags -</script> diff --git a/web/src/components/bookmark/Alias.vue b/web/src/components/bookmark/Alias.vue new file mode 100644 index 0000000..e932e23 --- /dev/null +++ b/web/src/components/bookmark/Alias.vue @@ -0,0 +1,25 @@ +<template> + <span class="aliases"> + <LinkCount class="alias" v-for="alias in aliases || []" :key="alias" :bookmarkId="bookmarkId" :link="link">{{ + alias + }}</LinkCount> + </span> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' +import LinkCount from '@/components/bookmark/LinkCount' + +@Component({ + name: 'Aliases', + props: ['bookmarkId', 'link', 'aliases'], + components: { LinkCount } +}) +class Aliases extends Vue { + @Prop bookmarkId + @Prop link + @Prop aliases +} + +export default Aliases +</script> diff --git a/web/src/components/bookmark/BookmarkDate.vue b/web/src/components/bookmark/BookmarkDate.vue new file mode 100644 index 0000000..09e0820 --- /dev/null +++ b/web/src/components/bookmark/BookmarkDate.vue @@ -0,0 +1,41 @@ +<template> + <div :title="allDate" class="date">{{ shortCreationDate }}</div> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' + +@Component({ + name: 'BookmarkDate', + props: ['creationDate', 'updateDate'], + components: {} +}) +class BookmarkDate extends Vue { + @Prop creationDate + @Prop updateDate + + get shortCreationDate() { + let d = new Date(this.creationDate) + return `${d.getFullYear()}/${this.padding0(d.getMonth() + 1)}/${this.padding0(d.getDate())}` + } + + padding0(number) { + if (number <= 9) { + return '0' + number + } + return number + } + + get allDate() { + let c = new Date(this.creationDate) + let result = `created at ${c.toString()}` + if (this.updateDate) { + let u = new Date(this.updateDate) + result += `\nupdated at ${u.toString()}` + } + return result + } +} + +export default BookmarkDate +</script> diff --git a/web/src/components/Description.vue b/web/src/components/bookmark/Description.vue similarity index 78% rename from web/src/components/Description.vue rename to web/src/components/bookmark/Description.vue index 016039f..a8b1d8e 100644 --- a/web/src/components/Description.vue +++ b/web/src/components/bookmark/Description.vue @@ -18,3 +18,12 @@ class Description extends Vue { export default Description </script> + +<style scoped lang="less"> +.description { + width: 100%; + textarea { + width: 100%; + } +} +</style> \ No newline at end of file diff --git a/web/src/components/LinkCount.vue b/web/src/components/bookmark/LinkCount.vue similarity index 100% rename from web/src/components/LinkCount.vue rename to web/src/components/bookmark/LinkCount.vue diff --git a/web/src/components/bookmark/Tags.vue b/web/src/components/bookmark/Tags.vue new file mode 100644 index 0000000..c3b892c --- /dev/null +++ b/web/src/components/bookmark/Tags.vue @@ -0,0 +1,86 @@ +<template> + <div class="tags"> + <span @click.prevent="addTag(tag)" class="tag" v-for="tag in tags || []" :key="tag">{{ tag }}</span> + </div> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' + +@Component({ + name: 'Tags', + props: ['tags'], + components: {} +}) +class Tags extends Vue { + @Prop tags + + addTag(tag) { + console.log(tag) + let tags; + if (this.$route.query.tags) { + tags = this.$route.query.tags + ' ' + tag + } else { + tags = tag + } + this.$router.push({ name: 'Home', query: { tags } }) + } +} + +export default Tags +</script> + +<style scoped lang="less"> +.tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; +} +.tags .tag { + float: left; + position: relative; + width: auto; + height: 20px; + margin-left: 20px; + padding: 0 12px; + line-height: 20px; + background: #1f8dd6; + color: #fff; + // font-size: 18px; + // font-weight: 600; + text-decoration: none; +} + +.tags .tag:before { + content: ''; + position: absolute; + top: 0; + width: 0; + height: 0; + border-style: solid; + + right: -10px; + border-color: transparent transparent transparent #1f8dd6; + border-width: 10px 0 10px 10px; +} + +.tags .tag:after { + content: ''; + position: absolute; + top: 9px; + width: 4px; + height: 4px; + border-radius: 2px; + background: #fff; + box-shadow: -1px -1px 2px #004977; + right: -2px; +} + +.tag:hover { + background: #555; +} +.tag:hover:before { + border-color: transparent transparent transparent #555; +} +</style> diff --git a/web/src/components/bookmark/Visit.vue b/web/src/components/bookmark/Visit.vue new file mode 100644 index 0000000..ff306ea --- /dev/null +++ b/web/src/components/bookmark/Visit.vue @@ -0,0 +1,32 @@ +<template> + <span class="visit" title="nombre de visite">{{ visit }}</span> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' + +@Component({ + name: 'Visit', + props: ['visit'], + components: {} +}) +class Visit extends Vue { + @Prop visit +} + +export default Visit +</script> + +<style scoped lang="less"> + .visit { + border-radius: 50%; + color: white; + background-color: rgb(163, 19, 9); + text-align: center; + padding: 8px; + } + + .visit * { + vertical-align: middle; + } +</style> diff --git a/web/src/components/layout/Header.vue b/web/src/components/layout/Header.vue index 5598a51..b7efaf2 100644 --- a/web/src/components/layout/Header.vue +++ b/web/src/components/layout/Header.vue @@ -47,7 +47,7 @@ class Header extends Vue { loadData() { console.log('loadData Header') - this.user = this.$store.get('bow-user') + this.user = this.$store.get('bow-user') || {} this.authenticated = !!this.$store.getCookie('bow-token') } diff --git a/web/src/components/preferences/Action.vue b/web/src/components/preferences/Action.vue new file mode 100644 index 0000000..53eca5f --- /dev/null +++ b/web/src/components/preferences/Action.vue @@ -0,0 +1,37 @@ +<template> + <div class="action"> + <input class="prefix" v-model="action.prefix" /> + <input class="action-item" v-model="action.action" /> + <input class="suggest" v-model="action.suggest" /> + </div> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' + +@Component({ + name: 'Action', + props: ['action'], + components: {} +}) +class Action extends Vue { + @Prop action +} + +export default Action +</script> + +<style scoped lang="less"> +.action { + display: flex; + flex-direction: row; + align-items: stretch; + + .prefix { + width: 5em; + } + .action-item, .suggest { + flex-grow: 1 + } +} +</style> diff --git a/web/src/components/preferences/Actions.vue b/web/src/components/preferences/Actions.vue new file mode 100644 index 0000000..b7f7fcf --- /dev/null +++ b/web/src/components/preferences/Actions.vue @@ -0,0 +1,53 @@ +<template> + <div class="actions"> + <Action v-for="(action, i) in actions" :key="i" :action="action"></Action> + <div class="buttons"> + <button class="add" @click.prevent="addAction">Add action</button> + <button class="save" @click.prevent="save">Save actions</button> + </div> + </div> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' +import Action from '@/components/preferences/Action' + +@Component({ + name: 'Actions', + props: ['actions'], + components: { Action } +}) +class Actions extends Vue { + @Prop actions + + addAction() { + this.actions.push({ prefix: '', action: '', suggest: '' }) + } + + save() { + this.$fetch.put('/users/current/actions', this.actions).then( + () => { + this.errorMsg = 'saved' + }, + (err) => { + console.log('ko', err) + this.errorMsg = err.cause + } + ) + } +} + +export default Actions +</script> + +<style scoped lang="less"> +.actions { + display: flex; + flex-direction: column; +} +.buttons { + display: flex; + flex-direction: row; + justify-content: center; +} +</style> diff --git a/web/src/components/preferences/MaxResultEditor.vue b/web/src/components/preferences/MaxResultEditor.vue new file mode 100644 index 0000000..e5991ae --- /dev/null +++ b/web/src/components/preferences/MaxResultEditor.vue @@ -0,0 +1,43 @@ +<template> + <div class="max-result-editor"> + <label for="maxresult">Max result</label><input id="maxresult" class="maxresult" v-model="user.maxresult" /> + <button class="save" @click.prevent="save">Save max result</button> + <span class="errorMsg">{{ errorMsg }}</span> + </div> +</template> + +<script> +import { Component, Prop, Vue } from 'vue-property-decorator' + +@Component({ + name: 'MaxResultEditor', + props: ['user'], + components: {} +}) +class MaxResultEditor extends Vue { + @Prop user + + errorMsg = '' + + save() { + this.$fetch.put('/users/current/maxresult', {maxresult: parseInt(this.user.maxresult)}).then( + () => { + this.errorMsg = 'saved' + }, + (err) => { + console.log('ko', err) + this.errorMsg = err.cause + } + ) + } +} + +export default MaxResultEditor +</script> + +<style scoped lang="less"> +.max-result-editor { + display: flex; + flex-direction: row; +} +</style> diff --git a/web/src/main.js b/web/src/main.js index 631e1e9..3d471b5 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -1,17 +1,12 @@ +// Make sure to register before importing any components +import './class-component-hooks' + import Vue from 'vue' import App from './App.vue' import router from './router' import FetchHelper from '@/utils/FetchHelper.js' -import Component from 'vue-class-component' import StoreHelper from './utils/Store' -// Register the router hooks with their names -Component.registerHooks([ - 'beforeRouteEnter', - 'beforeRouteLeave', - 'beforeRouteUpdate' -]) - window.BACKEND_URL = process.env.VUE_APP_BACKEND_URL window.FRONTEND_URL = process.env.BASE_URL diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index 96c2bbd..404de4c 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -64,27 +64,12 @@ class Home extends Vue { console.log('beforeMounted home') this.fetchBookmark() } - - // beforeUpdate() { - // console.log('beforeUpdate home') - // this.fetchBookmark() - // } - - beforeRouteEnter(to, from, next) { - console.log('beforeRouteEnter home', to, from, next) - next() - } - - beforeRouteUpdate(to, from, next) { - console.log('beforeRouteUpdate home', to, from, next) - next() - } } export default Home </script> -<style lang="less"> +<style scoped lang="less"> .bookmarks-list { padding-top: var(--margin--large); padding-left: var(--margin--medium); diff --git a/web/src/views/Preferences.vue b/web/src/views/Preferences.vue index 8710f4a..249c824 100644 --- a/web/src/views/Preferences.vue +++ b/web/src/views/Preferences.vue @@ -2,30 +2,46 @@ <div class="preferences"> <div>{{ errorMsg }}</div> <a :href="bookmarkletAdd">bookmarklet add</a> + <MaxResultEditor :user="user"></MaxResultEditor> + <Actions :actions="user.actions"></Actions> </div> </template> <script> // @ is an alias to /src import { Component, Vue } from 'vue-property-decorator' +import MaxResultEditor from '@/components/preferences/MaxResultEditor' +import Actions from '@/components/preferences/Actions' @Component({ - name: 'Preferences' + name: 'Preferences', + components: {MaxResultEditor, Actions} }) class Preferences extends Vue { - errorMsg = '' user = {} - bookmarkletAdd=`javascript:document.location='${location.protocol + '//' + location.host}/edit/new?uri='+encodeURIComponent(location.href)+'&description='+encodeURIComponent(document.title + '\\n' + (document.getSelection() || ''))` + bookmarkletAdd = `javascript:document.location='${location.protocol}//${location.host}/edit/new?uri='+encodeURIComponent(location.href)+'&description='+encodeURIComponent(document.title + '\\n' + (document.getSelection() || ''))` + + load() { + this.$fetch.get('/users/current').then( + (user) => { + console.log('ok', user) + this.user = user + }, + (err) => { + console.log('ko', err) + this.errorMsg = err.cause + } + ) + } beforeMount() { console.log('beforeMounted preference') + this.load() } - } export default Preferences </script> -<style lang="less"> -</style> +<style lang="less"></style> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm