Blog, fixes, improvements

This commit is contained in:
David Straub
2020-12-22 19:41:10 +01:00
parent 49c69ada38
commit 771041c2ce
22 changed files with 618 additions and 130 deletions
+2 -2
View File
@@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="description" content="Gramps.js Genealogical Research Web App">
<base href="/">
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@100;200;300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;200;300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet">
<link rel="icon" href="images/favicon.ico">
<link rel="manifest" href="manifest.json">
+1 -1
View File
@@ -532,7 +532,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
+20 -2
View File
@@ -37,7 +37,9 @@ import './views/GrampsjsViewFamily.js'
import './views/GrampsjsViewPlace.js'
import './views/GrampsjsViewEvent.js'
import './views/GrampsjsViewSource.js'
import './views/GrampsjsViewBlog.js'
import './views/GrampsjsViewCitation.js'
import './views/GrampsjsViewDashboard.js'
import './views/GrampsjsViewRepository.js'
import './views/GrampsjsViewNote.js'
import './views/GrampsjsViewMedia.js'
@@ -236,7 +238,8 @@ export class GrampsJs extends LitElement {
render() {
return html`
${this.renderContent()}
<mwc-snackbar id="error-snackbar"></mwc-snackbar>
<mwc-snackbar id="error-snackbar" leading></mwc-snackbar>
<mwc-snackbar id="notification-snackbar" leading></mwc-snackbar>
`
}
@@ -356,7 +359,7 @@ export class GrampsJs extends LitElement {
<span>${this._('Home Page')}</span>
<mwc-icon slot="graphic">home</mwc-icon>
</grampsjs-list-item>
<grampsjs-list-item href="/" graphic="icon">
<grampsjs-list-item href="/blog" graphic="icon">
<span>${this._('Blog')}</span>
<mwc-icon slot="graphic">rss_feed</mwc-icon>
</grampsjs-list-item>
@@ -394,6 +397,9 @@ export class GrampsJs extends LitElement {
${this._tabHtml(tabs)}
<grampsjs-view-dashboard class="page" ?active=${this._page === 'home'} .strings="${this._strings}"></grampsjs-view-dashboard>
<grampsjs-view-blog class="page" ?active=${this._page === 'blog'} .strings="${this._strings}"></grampsjs-view-blog>
<grampsjs-view-people class="page" ?active=${this._page === 'people'} .strings="${this._strings}"></grampsjs-view-people>
<grampsjs-view-families class="page" ?active=${this._page === 'families'} .strings="${this._strings}"></grampsjs-view-families>
<grampsjs-view-events class="page" ?active=${this._page === 'events'} .strings="${this._strings}"></grampsjs-view-events>
@@ -453,6 +459,7 @@ export class GrampsJs extends LitElement {
installMediaQueryWatcher('(min-width: 768px)', (matches) => {this.wide = matches})
this.addEventListener('nav', this._handleNav.bind(this))
this.addEventListener('grampsjs:error', this._handleError.bind(this))
this.addEventListener('grampsjs:notification', this._handleNotification.bind(this))
this.addEventListener('progress:on', this._progressOn.bind(this))
this.addEventListener('progress:off', this._progressOff.bind(this))
window.addEventListener('user:loggedout', this._handleLogout.bind(this))
@@ -523,6 +530,11 @@ export class GrampsJs extends LitElement {
this._showError(message)
}
_handleNotification(e) {
const {message} = e.detail
this._showToast(message)
}
_handleLoginKey(event) {
if(event.code === 'Enter') {
this._submitLogin()
@@ -597,6 +609,12 @@ export class GrampsJs extends LitElement {
snackbar.show()
}
_showToast(msg) {
const snackbar = this.shadowRoot.getElementById('notification-snackbar')
snackbar.labelText = msg
snackbar.show()
}
_openUserMenu() {
const userMenu = this.shadowRoot.getElementById('user-menu')
userMenu.open = true
+56 -50
View File
@@ -97,7 +97,11 @@ export async function apiGetTokens(username, password) {
export async function apiRefreshAuthToken(refreshToken) {
export async function apiRefreshAuthToken() {
const refreshToken = localStorage.getItem('refresh_token')
if (refreshToken === null) {
return {error: 'No refresh token found!'}
}
try {
const resp = await fetch(`${__APIHOST__}/api/token/refresh/`, {
method: 'POST',
@@ -116,7 +120,7 @@ export async function apiRefreshAuthToken(refreshToken) {
throw(new Error('Access token missing in response'))
}
const expires = Date.now() + 15 * 60 * 1000
storeAuthToken(resp.access_token, expires)
storeAuthToken(data.access_token, expires)
return {}
}
catch (error) {
@@ -162,6 +166,56 @@ export async function apiGet(endpoint) {
}
};
async function apiPutPost(method, endpoint, payload) {
const accessToken = localStorage.getItem('access_token')
let headers = {}
if (accessToken !== null) {
headers = {
'Authorization': `Bearer ${accessToken}`,
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
try {
const resp = await fetch(`${__APIHOST__}${endpoint}`, {
method,
headers,
body: JSON.stringify(payload)
})
if (resp.status === 401) {
const refreshToken = localStorage.getItem('refresh_token')
if (refreshToken === null) {
throw(new Error('Missing refresh token'))
}
const refreshResp = await apiRefreshAuthToken()
if ('error' in refreshResp) {
throw(new Error(refreshResp.error))
}
}
if (resp.status === 400 || resp.status === 401 || resp.status === 403) {
throw(new Error('Authorization error'))
}
if (resp.status !== 201) {
throw(new Error(`Error ${resp.status}`))
}
return {}
}
catch (error) {
return {'error': error.message}
}
};
export async function apiPut(endpoint, payload) {
return apiPutPost('PUT', endpoint, payload)
}
export async function apiPost(endpoint, payload) {
return apiPutPost('POST', endpoint, payload)
}
export function getMediaUrl(handle) {
const jwt = localStorage.getItem('access_token')
if (jwt === null) { return '' }
@@ -189,51 +243,3 @@ export function getThumbnailUrlCropped(handle, rect, size, square=false) {
const [x1, y1, x2, y2] = rect
return `${__APIHOST__}/api/media/${handle}/cropped/${x1}/${y1}/${x2}/${y2}/thumbnail/${size}?jwt=${jwt}&square=${square}`
}
export async function changeOwnPassword(oldPw, newPw) {
try {
const resp = await fetch(`${__APIHOST__}/api/users/-/password/change/`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({'old_password': oldPw, 'new_password': newPw})
})
if (resp.status === 400 || resp.status === 401 || resp.status === 403) {
throw(new Error('Wrong password'))
}
if (resp.status !== 201) {
throw(new Error(`Error ${resp.status}`))
}
return {}
}
catch (error) {
return {'error': error.message}
}
}
export async function changeOwnDetails(payload) {
try {
const resp = await fetch(`${__APIHOST__}/api/users/-/`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
if (resp.status === 400 || resp.status === 401 || resp.status === 403) {
throw(new Error('Not allowed'))
}
if (resp.status !== 201) {
throw(new Error(`Error ${resp.status}`))
}
return {}
}
catch (error) {
return {'error': error.message}
}
}
+2 -2
View File
@@ -14,13 +14,13 @@ class GrampsjsImg extends LitElement {
sharedStyles,
css`
img {
max-height:100vh;
/* max-height:100vh; */
max-width: 100%;
}
.round {
border-radius: 50%;
}
`
]
}
+143
View File
@@ -0,0 +1,143 @@
import {html, css, LitElement} from 'lit-element'
import {sharedStyles} from '../SharedStyles.js'
import '@material/mwc-button'
import './GrampsJsImage.js'
import {prettyTimeDiffTimestamp} from '../util.js'
export class GrampsjsBlogPost extends LitElement {
static get styles() {
return [
sharedStyles,
css`
h2 {
color: #222222;
font-weight: 300;
font-size: 40px;
padding-bottom: 0.3em;
margin-bottom: 0.5em;
padding-top: 0.5em;
text-align: center;
border-bottom: 2px solid #222222;
}
h3.author {
font-family: Roboto;
font-size: 16px;
text-transform: uppercase;
letter-spacing: 0.15em;
margin-bottom: 60px;
text-align: center;
}
#img-container {
width: 100%;
text-align: center;
}
#image {
margin-top: 2em;
margin-bottom: 3em;
}
#note {
margin: 4em 0em;
}
#note-wrapper {
margin: 0 auto;
max-width: 40em;
}
grampsjs-note-content {
--grampsjs-note-line-height: 1.7em;
--grampsjs-note-font-size: 18px;
--grampsjs-note-font-family: Roboto Slab;
--grampsjs-note-color: #333;
}
#btn-details {
margin-top: 2em;
}
@media (min-width: 768px) {
h2 {
font-size: 60px;
}
grampsjs-note-content {
--grampsjs-note-font-size: 22px;
}
}
`
]
}
static get properties() {
return {
source: {type: Object},
note: {type: Object},
strings: {type: Object},
}
}
constructor() {
super()
this.source = {}
this.note = {}
this.strings = {}
}
render() {
if (Object.keys(this.source).length === 0) {
return html``
}
return html`
<div class="blog-preview">
<h2>${this.source.title}</h2>
<h3 class="author">${this.source.author} ~ ${this.strings.__lang__ ? prettyTimeDiffTimestamp(this.source.change, this.strings.__lang__) : ''}</h3>
<div id="image">
${this.source?.media_list?.length ? this._renderImage() : ''}
</div>
<div id="note">
<div id="note-wrapper">
<grampsjs-note-content
?framed="${1 === 2}"
grampsId="${this.note.grampsId}"
content="${this.note?.formatted?.html || this.note?.text?.string || 'Error loading note'}"
>
</grampsjs-note-content>
<mwc-button id="btn-details" @click="${() => this._clickDetails(this.source.gramps_id)}">Details</mwc-button>
</div>
</div>
</div>
`
}
_clickDetails(grampsId) {
this.dispatchEvent(new CustomEvent('nav', {bubbles: true, composed: true, detail: {
path: `source/${grampsId}`}}))
}
_renderImage() {
const ref = this.source.media_list[0]
const obj = this.source.extended.media[0]
return html`
<div id="img-container">
<grampsjs-img
handle="${obj.handle}"
size="1000"
.rect="${ref.rect || []}"
mime="${obj.mime}"
></grampsjs-img>
</div>
`
}
}
window.customElements.define('grampsjs-blog-post', GrampsjsBlogPost)
+68
View File
@@ -0,0 +1,68 @@
import {html, css, LitElement} from 'lit-element'
import {sharedStyles} from '../SharedStyles.js'
import './GrampsJsImage.js'
export class GrampsjsBlogPreview extends LitElement {
static get styles() {
return [
sharedStyles,
css`
`
]
}
static get properties() {
return {
data: {type: Object},
strings: {type: Object},
}
}
constructor() {
super()
this.data = {}
this.strings = {}
}
render() {
if (Object.keys(this.data).length === 0) {
return html``
}
return html`
<div class="blog-preview">
<h2>${this.data.title}</h2>
<h3>${this.data.author}</h3>
${this.data?.extended?.notes?.length ? this._renderNoteSnippet() : ''}
${this.data?.media_list?.length ? this._renderImage() : ''}
</div>
`
}
_renderNoteSnippet() {
const note = this.data.extended.notes[0]
return html`
<p>${note?.text?.string || ''}</p>
`
}
_renderImage() {
const ref = this.data.media_list[0]
const obj = this.data.extended.media[0]
return html`
<grampsjs-img
handle="${obj.handle}"
size="200"
displayHeight="200"
.rect="${ref.rect || []}"
square
mime="${obj.mime}"
></grampsjs-img>
`
}
}
window.customElements.define('grampsjs-blog-preview', GrampsjsBlogPreview)
+1
View File
@@ -21,6 +21,7 @@ export class GrampsjsNote extends GrampsjsObject {
<h2><mwc-icon class="person">sticky_note_2</mwc-icon> ${this._(this.data?.type || 'Note')}</h2>
<grampsjs-note-content
framed
grampsId="${this.data.gramps_id}"
content="${this.data?.formatted?.html || this.data?.text?.string || 'Error loading note'}"
></grampsjs-note-content>
+26 -16
View File
@@ -1,5 +1,5 @@
import { html, css, LitElement } from 'lit-element';
import { sharedStyles } from '../SharedStyles.js'
import {html, css, LitElement} from 'lit-element'
import {sharedStyles} from '../SharedStyles.js'
export class GrampsjsNoteContent extends LitElement {
@@ -8,49 +8,59 @@ export class GrampsjsNoteContent extends LitElement {
sharedStyles,
css`
:host {
font-family: var(--grampsjs-note-font-family, Roboto Slab);
font-size: var(--grampsjs-note-font-size, 20px);
line-height: var(--grampsjs-note-line-height, 1.5em);
color: var(--grampsjs-note-color, #000000);
}
.note {
font-family: Roboto Slab;
font-weight: 300;
font-size: 20px;
line-height: 1.5em;
}
.note.frame {
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 8px;
padding: 20px 25px;
}
.note p {
.note.frame p {
margin: 2em 0em;
}
.note p:first-child {
.note.frame p:first-child {
margin-top:0;
}
.note p:last-child {
.note.frame p:last-child {
margin-bottom:0;
}
`
];
]
}
static get properties() {
return {
grampsId: { type: String },
content: { type: String },
};
grampsId: {type: String},
content: {type: String},
framed: {type: Boolean}
}
}
constructor() {
super()
this.framed = false
}
render() {
// <p>${this.grampsId}</p>
return html`
<div class="note" id="note-content"></div>
<div class="note ${this.framed ? 'frame' : ''}" id="note-content"></div>
`
}
updated() {
const noteContent = this.shadowRoot.getElementById("note-content")
const noteContent = this.shadowRoot.getElementById('note-content')
noteContent.innerHTML = this.content
}
@@ -58,4 +68,4 @@ export class GrampsjsNoteContent extends LitElement {
}
window.customElements.define('grampsjs-note-content', GrampsjsNoteContent);
window.customElements.define('grampsjs-note-content', GrampsjsNoteContent)
+4 -4
View File
@@ -195,11 +195,11 @@ export class GrampsjsObject extends LitElement {
latitude="${this.data.profile.lat}"
longitude="${this.data.profile.long}"
mapid="place-map"
id="map"
>
<grampsjs-map-marker>
<grampsjs-map-marker
latitude="${this.data.profile.lat}"
longitude="${this.data.profile.long}"
popup="Hallo"
>
</grampsjs-map-marker>
</grampsjs-map>`
@@ -224,11 +224,11 @@ export class GrampsjsObject extends LitElement {
case('participants'):
return html`<grampsjs-participants .strings=${this.strings} .data=${[this.data?.profile?.participants]}></grampsjs-participants>`
case('references'):
return html`<grampsjs-references .strings=${this.strings} .data=${[this.data?.extended?.backlinks]}></grampsjs-references>`
return html`<grampsjs-references .strings=${this.strings} .data=${[this.data?.extended?.backlinks]} .profile=${this.data?.profile?.references || {}}></grampsjs-references>`
default:
break
}
return html`Missing: ${this._currentTab}`
return html``
}
_getTabs() {
+15 -3
View File
@@ -1,7 +1,7 @@
import {html} from 'lit-element'
import {GrampsjsTableBase} from './GrampsjsTableBase.js'
import {getName} from '../util.js'
import {getName, getNameFromProfile} from '../util.js'
function capitalize(string) {
@@ -9,6 +9,18 @@ function capitalize(string) {
}
export class GrampsjsReferences extends GrampsjsTableBase {
static get properties() {
return {
profile: {type: Object}
}
}
constructor() {
super()
this.profile = {}
}
render() {
if (this.data.length === 0 || Object.keys(this.data[0]).length === 0) {
return html``
@@ -19,10 +31,10 @@ export class GrampsjsReferences extends GrampsjsTableBase {
<th>${this._('Type')}</th>
<th>${this._('Description')}</th>
</tr>
${Object.keys(this.data[0]).map((type) => this.data[0][type].map((obj) => html`
${Object.keys(this.data[0]).map((type) => this.data[0][type].map((obj, index) => html`
<tr @click=${() => this._handleClick(type, obj.gramps_id)}>
<td>${this._(capitalize(type))}</td>
<td>${getName(obj, type) || ''}</td>
<td>${type in this.profile ? getNameFromProfile(this.profile[type][index] || {}, type, this.strings) || '' : getName(obj, type)}</td>
</tr>
`, this),
this)}
+10 -9
View File
@@ -1,7 +1,7 @@
import { LitElement, css, html } from 'lit-element';
import {LitElement, css, html} from 'lit-element'
import { hex12ToCss } from '../color.js';
import { sharedStyles } from '../SharedStyles.js';
import {hex12ToCss} from '../color.js'
import {sharedStyles} from '../SharedStyles.js'
export class GrampsjsTags extends LitElement {
@@ -22,21 +22,22 @@ export class GrampsjsTags extends LitElement {
}
.tags {
clear: left;
margin: 20px 0px;
}
`
];
]
}
static get properties() {
return {
data: { type: Array },
};
data: {type: Array},
}
}
constructor() {
super();
this.data = [];
super()
this.data = []
}
@@ -59,6 +60,6 @@ export class GrampsjsTags extends LitElement {
}
window.customElements.define('grampsjs-tags', GrampsjsTags);
window.customElements.define('grampsjs-tags', GrampsjsTags)
+2
View File
@@ -28,6 +28,7 @@ export const grampsStrings = [
'Addresses',
'Age at death',
'and',
'Archive',
'Association Note',
'Associations',
'Attribute Note',
@@ -71,6 +72,7 @@ export const grampsStrings = [
'Farm',
'Father',
'Gallery',
'General',
'Given name',
'Gramps ID',
'Hamlet',
+26 -2
View File
@@ -44,7 +44,7 @@ export function eventTitleFromProfile(eventProfile, strings) {
const families = eventProfile?.participants?.families.filter((obj) => obj.role === family) || []
const primaryPeople = `${people.map((obj) => personTitleFromProfile(obj.person)).join(', ')}
${families.map((obj) => familyTitleFromProfile(obj.family)).join(', ')}`
return html`${eventProfile.type}: ${primaryPeople || '?'}`
return html`${eventProfile.type}${primaryPeople.trim() ? `: ${primaryPeople}` : ''}${eventProfile.date ? ` (${eventProfile.date})` : ''}`
}
@@ -115,7 +115,7 @@ export function showObject(type, obj, strings) {
return html`
<mwc-icon class="inline">place</mwc-icon>
<a href="/${type}/${obj.gramps_id}"
>${getName(obj, type) || type}
>${obj?.profile?.name || obj.title || type}
</a>
`
case 'source':
@@ -178,3 +178,27 @@ export function debounce (func, wait) {
timeout = setTimeout(later, wait)
}
}
export function getNameFromProfile(obj, type, strings) {
switch(type) {
case 'person':
return personTitleFromProfile(obj)
case 'event':
return eventTitleFromProfile(obj, strings)
case 'family':
return familyTitleFromProfile(obj)
case 'place':
return obj.title
case 'source':
return obj.title
case 'repository':
return obj.name
case 'citation':
return citationTitleFromProfile(obj)
case 'media':
return obj.desc
default:
return ''
}
}
-1
View File
@@ -50,7 +50,6 @@ export class GrampsjsView extends LitElement {
render() {
if (this.error) {
this.dispatchEvent(new CustomEvent('grampsjs:error', {bubbles: true, composed: true, detail: {message: this._errorMessage}}))
return html``
}
return this.renderContent()
}
+156
View File
@@ -0,0 +1,156 @@
import {css, html} from 'lit-element'
import {GrampsjsView} from './GrampsjsView.js'
import '../components/GrampsjsBlogPost.js'
import {apiGet} from '../api.js'
export class GrampsjsViewBlog extends GrampsjsView {
static get styles() {
return [
super.styles,
css`
.muted {
opacity: 0.4;
}
`
]
}
static get properties() {
return {
_dataSources: {type: Array},
_dataNotes: {type: Array},
_totalCount: {type: Number},
_page: {type: Number},
_pages: {type: Number}
}
}
constructor() {
super()
this._dataSources = []
this._dataNotes = []
this._page = 1
this._pageSize = 1
this._firstLoaded = false
this._totalCount = -1
this._pages = -1
}
renderContent() {
return html`
${this.renderPosts()}
${this._totalCount > 0 ? this.renderPagination() : ''}
`
}
renderPosts() {
if (this._firstLoaded && this._dataSources.length === 0) {
return html`
<h2>${this._('Blog')}</h2>
<p class="muted">To start using the blog, add a source with tag 'Blog'.</p>
`
}
if (this.loading) {
return html``
}
return html`
${this._dataSources.map((source, i) => this.renderPost(source, this._dataNotes[i]), this)}
`
}
renderPagination() {
return html`
<grampsjs-pagination
page="${this._page}"
pages="${this._pages}"
@page:changed="${this._handlePageChanged}"
></grampsjs-pagination>
`
}
_handlePageChanged(event) {
this._page = event.detail.page
this._fetchData()
}
// eslint-disable-next-line no-dupe-class-members
renderPost(source, note) {
return html`
<grampsjs-blog-post
.source="${source}"
.note="${note}"
.strings="${this.strings}"
></grampsjs-blog-post>
`
}
firstUpdated() {
this._fetchData()
}
_getNotesUrl() {
const grampsIds = this._dataSources.map(obj => obj?.extended?.notes[0]?.gramps_id).filter(obj => obj)
const rules = {
function: 'or',
rules: grampsIds.map(grampsId =>({
name: 'HasIdOf',
values: [grampsId]
})
)
}
const options = {
link_format: '/{obj_class}/{gramps_id}'
}
return `/api/notes/?profile=all&extend=all&formats=html&rules=${encodeURIComponent(JSON.stringify(rules))}&format_options=${encodeURIComponent(JSON.stringify(options))}`
}
async _fetchData() {
this.loading = true
const rules = {
rules: [
{
name: 'HasTag',
values: ['Blog']
}
]
}
const uri = `/api/sources/?rules=${encodeURIComponent(JSON.stringify(rules))}&page=${this._page}&pagesize=${this._pageSize}&sort=-change&profile=all&extend=all`
await apiGet(uri).then(data => {
if ('data' in data) {
this._dataSources = data.data
this._totalCount = data.total_count
this._pages = Math.ceil(this._totalCount / this._pageSize)
} else if ('error' in data) {
this.error = true
this._errorMessage = data.error
}
})
const uriNotes = this._getNotesUrl()
await apiGet(uriNotes).then(data => {
if ('data' in data) {
this._dataNotes = this._dataSources.map(obj => {
const noteGrampsId = obj?.extended?.notes[0]?.gramps_id
if (!noteGrampsId) {
return {}
}
return data.data.find(note => note.gramps_id === noteGrampsId) || {}
})
} else if ('error' in data) {
this.error = true
this._errorMessage = data.error
}
})
this.loading = false
this._firstLoaded = true
}
}
window.customElements.define('grampsjs-view-blog', GrampsjsViewBlog)
+22
View File
@@ -0,0 +1,22 @@
import {html} from 'lit-element'
import {GrampsjsView} from './GrampsjsView.js'
export class GrampsjsViewDashboard extends GrampsjsView {
constructor() {
super()
}
renderContent() {
return html`
Dashboard
`
}
}
window.customElements.define('grampsjs-view-dashboard', GrampsjsViewDashboard)
+11 -4
View File
@@ -6,7 +6,7 @@ import '../components/GrampsjsLightbox.js'
import '../components/GrampsjsRectContainer.js'
import '../components/GrampsjsRect.js'
import {apiGet} from '../api.js'
import {getNameFromProfile} from '../util.js'
export class GrampsjsViewMediaLightbox extends GrampsjsView {
static get styles() {
@@ -17,6 +17,10 @@ export class GrampsjsViewMediaLightbox extends GrampsjsView {
:host {
margin: 0px;
}
grampsjs-img {
max-height:100vh;
}
`
]
}
@@ -42,7 +46,7 @@ export class GrampsjsViewMediaLightbox extends GrampsjsView {
${this._getRectangles().map(obj => html`
<grampsjs-rect
.rect="${obj.rect}"
label="..."
label="${obj.label}"
target="${obj.type}/${obj.grampsId}"
>
</grampsjs-rect>
@@ -109,16 +113,19 @@ export class GrampsjsViewMediaLightbox extends GrampsjsView {
}
}
// eslint-disable-next-line class-methods-use-this
_getRectangles() {
const backlinks = this._data?.extended?.backlinks || {}
const references = this._data?.profile?.references || {}
if (Object.keys(backlinks).length === 0) {
return []
}
return Object.keys(backlinks).map(key => backlinks[key].map(obj => {
return Object.keys(backlinks).map(key => backlinks[key].map((obj, index) => {
const refs = key in references ? references[key] : []
const label = refs.length >= index ? getNameFromProfile(refs[index] || {}, key, this.strings) : '...'
return {
rect: obj.media_list.find(mobj => mobj.ref === this._data.handle)?.rect,
type: key,
label,
grampsId: obj.gramps_id
}
})
+1
View File
@@ -47,6 +47,7 @@ export class GrampsjsViewObjectNotes extends GrampsjsViewObjectsDetail {
renderNote(obj) {
return html`
<grampsjs-note-content
framed
grampsId="${obj.gramps_id}"
content="${obj?.formatted?.html || obj?.text?.string}"
></grampsjs-note-content>
+1 -1
View File
@@ -78,7 +78,7 @@ export class GrampsjsViewRecentObject extends GrampsjsView {
return
}
this.loading = true
const query = this._data.map(obj => obj.grampsId).filter(grampsId => grampsId !== undefined).join(' OR ')
const query = this._data.map(obj => obj.grampsId).filter(grampsId => grampsId && grampsId.trim()).join(' OR ')
const data = await apiGet(`/api/search/?query=${query}&profile=all&page=1&pagesize=100`)
this.loading = false
if ('data' in data) {
+12 -3
View File
@@ -1,7 +1,7 @@
import {html} from 'lit-element'
import {GrampsjsViewSettingsOnboarding} from './GrampsjsViewSettingsOnboarding.js'
import {doLogout, changeOwnDetails, changeOwnPassword} from '../api.js'
import {doLogout, apiPost, apiPut} from '../api.js'
import '@material/mwc-textfield'
import '@material/mwc-button'
import '@material/mwc-select'
@@ -91,11 +91,15 @@ export class GrampsjsViewSettings extends GrampsjsViewSettingsOnboarding {
return
}
this.loading = true
changeOwnDetails({email: form.value}).then(data => {
const payload = {email: form.value}
apiPut('/api/users/-/', payload).then(data => {
this.loading = false
if ('error' in data) {
this.error = true
this._errorMessage = data.error
} else{
this.dispatchEvent(new CustomEvent('grampsjs:notification', {bubbles: true, composed: true, detail: {message: 'E-mail successfully updated'}}))
form.value = ''
}
})
}
@@ -107,11 +111,16 @@ export class GrampsjsViewSettings extends GrampsjsViewSettingsOnboarding {
return
}
this.loading = true
changeOwnPassword(formOldPw.value, formNewPw.value).then(data => {
const payload = {old_password: formOldPw.value, new_password: formNewPw.value}
apiPost('/api/users/-/password/change', payload).then(data => {
this.loading = false
if ('error' in data) {
this.error = true
this._errorMessage = data.error
} else {
this.dispatchEvent(new CustomEvent('grampsjs:notification', {bubbles: true, composed: true, detail: {message: 'Password successfully updated'}}))
formOldPw.value = ''
formNewPw.value = ''
}
})
}
+39 -30
View File
@@ -51,6 +51,11 @@ export class GrampsjsViewTree extends GrampsjsView {
--mdc-ripple-focus-opacity: 0;
--mdc-theme-primary: rgba(0, 0, 0, 0.7);
}
#outer-container {
clear: left;
padding-top: 30px;
}
`]
}
@@ -68,7 +73,7 @@ export class GrampsjsViewTree extends GrampsjsView {
super()
this.grampsId = ''
this._data = []
this._depth = 4
this._depth = 3
this._zoom = 1
this._history = []
}
@@ -86,36 +91,40 @@ export class GrampsjsViewTree extends GrampsjsView {
return html`
<section id="pedigree-section">
<div id="slider-block">
<span class="label">${this._('Number of generations:')}</span>
<span class="slider-container"><mwc-slider
value="${this._depth}"
min="2"
max="6"
step="1"
@change="${this._updateDepth}"
id="slider"
pin
></mwc-slider></span>
<div id="controls">
<div id="slider-block">
<span class="label">${this._('Number of generations:')}</span>
<span class="slider-container"><mwc-slider
value="${this._depth}"
min="2"
max="6"
step="1"
@change="${this._updateDepth}"
id="slider"
pin
></mwc-slider></span>
</div>
<div id="button-block">
<mwc-button outlined
id="btn-back"
@click="${this._prevPerson}"
?disabled="${this._history.length < 2}"
icon="undo"></mwc-button>
<mwc-button outlined id="btn-home"
@click="${this._backToHomePerson}"
?disabled="${this.grampsId === this.settings.homePerson}"
>${this._('Home Person')}</mwc-button>
</div>
</div>
<div id="button-block">
<mwc-button outlined
id="btn-back"
@click="${this._prevPerson}"
?disabled="${this._history.length < 2}"
icon="undo"></mwc-button>
<mwc-button outlined id="btn-home"
@click="${this._backToHomePerson}"
?disabled="${this.grampsId === this.settings.homePerson}"
>${this._('Home Person')}</mwc-button>
</div>
<div style="transform: scale(${this._zoom}); transform-origin: top left;" id="pedigree-container">
<grampsjs-pedigree
.people="${this._data}"
grampsId="${this.grampsId}"
depth="${this._depth}"
id="pedigree"
></grampsjs-pedigree>
<div id="outer-container">
<div style="transform: scale(${this._zoom}); transform-origin: top left;" id="pedigree-container">
<grampsjs-pedigree
.people="${this._data}"
grampsId="${this.grampsId}"
depth="${this._depth}"
id="pedigree"
></grampsjs-pedigree>
</div>
</div>
</section>
`