diff --git a/assets/img/favori_add.png b/assets/img/favori_add.png new file mode 100644 index 0000000000000000000000000000000000000000..f02f205a73bf82eaa22a74f72deaf0d00568e220 Binary files /dev/null and b/assets/img/favori_add.png differ diff --git a/assets/img/favori_remove.png b/assets/img/favori_remove.png new file mode 100644 index 0000000000000000000000000000000000000000..06b43589048636ea9ff8bab2dd43b0f598a8c2e2 Binary files /dev/null and b/assets/img/favori_remove.png differ diff --git a/components/btn/Clipboard.vue b/components/btn/Clipboard.vue index eb8c5f06a7145d3b127733689e08e70d69c2ff57..121b4f5e0a0361bc61182bb4b88a48e08ead3680 100644 --- a/components/btn/Clipboard.vue +++ b/components/btn/Clipboard.vue @@ -33,7 +33,7 @@ export default { $("#btncopy").tooltip("show") setTimeout(() => { $("#btncopy").tooltip("hide") - }, 500) + }, 1000) } } } diff --git a/components/member/Card.vue b/components/member/Card.vue index 442ce2fbe43b7cd3c14316e4609602371d4b3d0c..10bae0633b529aec87e6fb7ef34b0c9f763bcda7 100644 --- a/components/member/Card.vue +++ b/components/member/Card.vue @@ -1,7 +1,15 @@ <template> <div class="card member"> + <button + id="favori" + class="btn btn-light position-absolute" + :class="{ + add: !isFavorite, + remove: isFavorite + }" + @click="toggleFavourite"></button> <div class="card-body"> - <h2 class="card-title text-center"> + <h2 class="card-title text-center mb-4"> {{ hash.uid }} <BadgeStatus :membre="hash" /> </h2> @@ -121,13 +129,74 @@ <script> export default { + data() { + return { + favourites: [] + } + }, props: { hash: Object + }, + methods: { + toggleFavourite() { + let $this = this + + $("#favori").tooltip({ + title: function () { + return $this.isFavorite + ? $this.$t("favoris.supprime") + : $this.$t("favoris.enregistre") + }, + html: true, + trigger: "manual" + }) + + $("#favori").tooltip("show") + setTimeout(() => { + $("#favori").tooltip("hide") + }, 1000) + + if (!this.isFavorite) { + this.favourites.push(this.hash.uid) + } else { + this.favourites.splice(this.favourites.indexOf(this.hash.uid), 1) + } + + localStorage.favourites = JSON.stringify(this.favourites) + } + }, + computed: { + isFavorite() { + this.favourites = localStorage.favourites + ? JSON.parse(localStorage.favourites) + : [] + + return this.favourites.includes(this.hash.uid) + } } } </script> <style lang="scss"> +#favori { + top: 1.25rem; + left: 1.25rem; + background-color: var(--light); + background-size: 75%; + background-repeat: no-repeat; + background-position: center; + width: 50px; + height: 50px; + + &.add { + background-image: url("~/assets/img/favori_add.png"); + } + + &.remove { + background-image: url("~/assets/img/favori_remove.png"); + } +} + .member { .table { text-align: center; diff --git a/graphql/cache.js b/graphql/cache.js index 21055e0225ac2552ebaeaf74e99473d9d76f5495..b8dff3ab0aa40ff1915392b50022bfd76b8cfe1a 100644 --- a/graphql/cache.js +++ b/graphql/cache.js @@ -1,21 +1,32 @@ -import { InMemoryCache, IntrospectionFragmentMatcher, defaultDataIdFromObject } from 'apollo-cache-inmemory' -import introspectionQueryResultData from './fragmentTypes.json'; +import { + InMemoryCache, + IntrospectionFragmentMatcher, + defaultDataIdFromObject +} from "apollo-cache-inmemory" +import introspectionQueryResultData from "./fragmentTypes.json" const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData + introspectionQueryResultData }) // Apparemment il faut utiliser la syntaxe Apollo v2 export const cache = new InMemoryCache({ - addTypename: false, - fragmentMatcher, - dataIdFromObject: object => { - switch (object.__typename) { - case 'Identity': return object.hash - case 'Event': return object.block.number - case 'EventId': return `${object.member.hash}:${object.inOut}` - case 'Forecast': return `${object.member.hash}:${object.date}:${object.after}:${object.proba}` - default: return defaultDataIdFromObject(object); // fall back to default handling - } - } -}) \ No newline at end of file + addTypename: false, + fragmentMatcher, + dataIdFromObject: (object) => { + switch (object.__typename) { + case "Identity": + return object.hash + case "Event": + return object.block.number + case "EventId": + return `${object.member.hash}:${object.inOut}` + case "Forecast": + return `${object.member.hash}:${object.date}:${object.after}:${object.proba}` + case "GroupId": + return `${object.id.hash}` + default: + return defaultDataIdFromObject(object) // fall back to default handling + } + } +}) diff --git a/graphql/queries.js b/graphql/queries.js index 0df3c0f8e3365d1373da9e4e98b0fd42b28e2a96..5e0b8ddfc63a4ae0e271ab1b1bc48f2987f2f713 100644 --- a/graphql/queries.js +++ b/graphql/queries.js @@ -1,243 +1,208 @@ import gql from "graphql-tag" // Pour la sidebar -export const LAST_BLOCK = gql`query LastBlock{ - countMax { - number - bct - utc0 - } -}` +export const LAST_BLOCK = gql` + query LastBlock { + countMax { + number + bct + utc0 + } + } +` // Pour la page index -export const LAST_EVENTS = gql`query LastEvents($start: Int64, $end: Int64) { - membersCount(start: $start, end: $end) { - idList { - __typename - member : id { - __typename - pubkey - uid - status - hash - limitDate - history { - __typename - in - block { - __typename - number - } - } - received_certifications { - __typename - limit - } - } - inOut - }, - block { - __typename - number - } - } -} ` - -// Pour la page previsions/index -export const PREVISIONS = gql`query GetDossiers { - now { - number - bct - __typename - } - parameter(name: sigQty) { - sigQty: value - __typename - } - wwFile(full: true) { - certifs_dossiers { - ... on MarkedDatedCertification { - datedCertification { - date - certification { - from { - uid - __typename - } - to { - uid - __typename - } - expires_on - __typename - } - __typename - } - __typename - } - ... on MarkedDossier { - dossier { - main_certifs - newcomer { - uid - lastApplication { - lastAppDate: bct - __typename - } - distance: distanceE { - value { - ratio - __typename - } - dist_ok - __typename - } - __typename - } - date - minDate - expires_on: limit - certifications { - date - certification { - from { - uid - quality { - ratio - __typename - } - __typename - } - expires_on - __typename - } - __typename - } - __typename - } - __typename - } - } - __typename - } - }` +export const LAST_EVENTS = gql` + query LastEvents($start: Int64, $end: Int64) { + membersCount(start: $start, end: $end) { + idList { + __typename + member: id { + __typename + pubkey + uid + status + hash + limitDate + history { + __typename + in + block { + __typename + number + } + } + received_certifications { + __typename + limit + } + } + inOut + } + block { + __typename + number + } + } + } +` // Pour la page previsions/newcomers -export const NEWCOMERS = gql`query GetNewcomers{ - wwResult { - __typename - permutations_nb - dossiers_nb - certifs_nb - forecastsByNames { - __typename - member : id { - __typename - pubkey - uid - status - hash - limitDate - received_certifications { - __typename - limit - } - } - date - after - proba - } - } -} ` +export const NEWCOMERS = gql` + query GetNewcomers { + wwResult { + __typename + permutations_nb + dossiers_nb + certifs_nb + forecastsByNames { + __typename + member: id { + __typename + pubkey + uid + status + hash + limitDate + received_certifications { + __typename + limit + } + } + date + after + proba + } + } + } +` // Pour la page membres/index -export const SEARCH_MEMBERS = gql`query SearchMember($hint: String) { - idSearch(with: {hint: $hint}) { - __typename - ids { - __typename - pubkey - uid - status - hash - limitDate - received_certifications { - __typename - limit - } - } - } -} ` +export const SEARCH_MEMBERS = gql` + query SearchMember($hint: String) { + idSearch(with: { hint: $hint }) { + __typename + ids { + __typename + pubkey + uid + status + hash + limitDate + received_certifications { + __typename + limit + } + } + } + } +` // Pour la page membres/_hash -export const SEARCH_MEMBER = gql`query SearchMemberWithHash($hash: Hash!) { - idFromHash(hash: $hash) { - ...attr - pubkey - isLeaving - sentry - membership_pending - limitDate - distanceE { - __typename - value { - __typename - ratio - } - dist_ok - } - distance { - __typename - value { - __typename - ratio - } - dist_ok - } - received_certifications { - __typename - certifications { - __typename - from { - ...attr - } - expires_on - pending - } - } - sent_certifications { - __typename - to { - ...attr - } - expires_on - pending - } - } -} -fragment attr on Identity { - __typename - uid - hash - status - minDate - minDatePassed - quality { - __typename - ratio - } - received_certifications { - __typename - limit - } -}` +export const SEARCH_MEMBER = gql` + query SearchMemberWithHash($hash: Hash!) { + idFromHash(hash: $hash) { + ...attr + pubkey + isLeaving + sentry + membership_pending + limitDate + distanceE { + __typename + value { + __typename + ratio + } + dist_ok + } + distance { + __typename + value { + __typename + ratio + } + dist_ok + } + received_certifications { + __typename + certifications { + __typename + from { + ...attr + } + expires_on + pending + } + } + sent_certifications { + __typename + to { + ...attr + } + expires_on + pending + } + } + } + fragment attr on Identity { + __typename + uid + hash + status + minDate + minDatePassed + quality { + __typename + ratio + } + received_certifications { + __typename + limit + } + } +` // Pour la page parametres -export const PARAMS = gql`query getParams{ - allParameters { - name - par_type - value - comment - } -}` \ No newline at end of file +export const PARAMS = gql` + query getParams { + allParameters { + name + par_type + value + comment + } + } +` +// Pour la page favoris +export const FAVORIS = gql` + query getFavoris($group: [String!]!) { + filterGroup(group: $group) { + __typename + selected { + __typename + id { + ...attr + } + } + others { + __typename + id { + ...attr + } + } + } + } + fragment attr on Identity { + __typename + pubkey + uid + status + hash + limitDate + received_certifications { + __typename + limit + } + } +` diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 3346c2a805d65d889cbd0f3716d00a5ef98ab5d2..fbfd6b4cae7818f9955a9ea97e1aaf7d8644ce25 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -62,6 +62,12 @@ "title": "Duniter" }, "expire": "Expires", + "favoris": { + "enregistre": "Saved to favorites !", + "none": "You don't have any favorites yet", + "supprime": "Deleted from favourites !", + "title": "My favourites" + }, "futuremembers": "Future members", "infos": "Informations", "inout": "Entries and exits of the web of trust for the last 2 days", diff --git a/i18n/locales/es.json b/i18n/locales/es.json index 431d87eb55c7d5895f79f62568d1cd9d03fdb065..22e9c868d823c5b65cf896bc0677113e0ced26dd 100644 --- a/i18n/locales/es.json +++ b/i18n/locales/es.json @@ -62,6 +62,12 @@ "title": "Duniter" }, "expire": "Expira el", + "favoris": { + "enregistre": "¡Guardado en favoritos!", + "none": "Aún no tienes favoritos", + "supprime": "¡Eliminado de favoritos!", + "title": "Mis favoritos" + }, "futuremembers": "Futuros miembros", "infos": "Informaciones", "inout": "Entradas y salidas de la red de confianza en los últimos 2 dÃas", diff --git a/i18n/locales/fr.json b/i18n/locales/fr.json index c0a1b104cc81b803708451829ef28e6856b573c3..0407b24ba65a1b0545232d1d212df2048c49d72a 100644 --- a/i18n/locales/fr.json +++ b/i18n/locales/fr.json @@ -62,6 +62,12 @@ "title": "Duniter" }, "expire": "Expire le", + "favoris": { + "enregistre": "Enregistré dans les favoris !", + "none": "Vous n'avez pas encore de favoris", + "supprime": "Supprimé des favoris !", + "title": "Mes favoris" + }, "futuremembers": "Futurs membres", "infos": "Informations", "inout": "Entrées et sorties de la toile de confiance des 2 derniers jours", diff --git a/layouts/default.vue b/layouts/default.vue index f3ee63e12571041daafe342b4b08f147b23e0e2b..651572ffc0ca15a236d8ca5dedb4809645c36ad2 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -14,7 +14,10 @@ export default { menus: [ { title: "wot.title", - items: [{ path: "/membres", title: "membres" }] + items: [ + { path: "/membres", title: "membres" }, + { path: "/favoris", title: "favoris.title" } + ] }, { title: "previsions.title", diff --git a/pages/favoris.vue b/pages/favoris.vue new file mode 100644 index 0000000000000000000000000000000000000000..a17eb948f07221f9d59dc1a229390bb376a7d9ed --- /dev/null +++ b/pages/favoris.vue @@ -0,0 +1,94 @@ +<template> + <main class="container"> + <h2 class="text-center my-5 font-weight-light"> + {{ $t("favoris.title") }} + </h2> + + <NavigationLoader :isLoading="$apollo.queries.favoris.loading" /> + <div class="row text-center"> + <div class="col"> + <transition name="fade"> + <div class="alert alert-danger" v-if="error">{{ error }}</div> + </transition> + <transition name="fade"> + <MemberList + :members="favoris" + v-if="favoris && favoris.length != 0" /> + </transition> + <transition name="fade"> + <div + class="alert alert-info" + v-if="!$apollo.queries.favoris.loading && favoris.length == 0"> + {{ $t("favoris.none") }} + </div> + </transition> + </div> + </div> + </main> +</template> + +<script> +import { FAVORIS } from "@/graphql/queries.js" + +export default { + data() { + return { + breadcrumb: [ + { + text: this.$t("accueil"), + to: "/" + }, + { + text: this.$t("favoris.title"), + active: true + } + ], + error: null + } + }, + // local functions. You can use : + // {{ myFunction() }} in template if a value is returned + // - this.myFunction everywhere in the page but not in arrows functions + // - @event="myFunction" on Vue eventHandlers + // methods: { + // myFunction() { + + // } + // }, + // For computed values. Use {{ myComputedValue }} in the template + // computed: { + // myComputedValue : function() { + // return this.var * 3 + // } + // }, + apollo: { + favoris: { + query: FAVORIS, + variables() { + return { group: JSON.parse(localStorage.favourites) } + }, + update(data) { + let retour = [] + for (let i = 0; i < data.filterGroup.selected.length; i++) { + retour[i] = data.filterGroup.selected[i].id + } + + return retour + }, + error(err) { + this.error = err.message + } + } + }, + nuxtI18n: { + paths: { + fr: "/favoris", + en: "/favourites", + es: "/favoritos" + } + }, + mounted() { + $nuxt.$emit("changeRoute", this.breadcrumb) + } +} +</script>