mirror of
https://github.com/wekan/wekan.git
synced 2026-11-07 11:02:33 -05:00
The web app manifest declares a share_target, so on a phone where WeKan is installed as an app it appears in Android's Share menu. Sharing a web page, a note or a message opens /share-target, "Add to WeKan": pick a board and a list, and what was shared becomes a card there - the page's title or the text's first line as the title, the text and the address in its description, the address once - and the card opens. Only boards the member may add cards to are offered, and the server checks the right to write in that list, on that board, as for any card added. models/lib/shareTarget.js turns a share into a card and holds the manifest's declaration; tests/shareTarget.test.cjs covers what browsers, notes and chat apps share, an empty share, javascript: addresses, the manifest, the route and the server's checks. Documented in docs/Platforms/FOSS/OS/Android.md. The four new strings are English placeholders until the next translation pass. Thanks to memeplex and xet7 ! Fixes #1293.
43 lines
1.7 KiB
JavaScript
43 lines
1.7 KiB
JavaScript
'use strict';
|
|
|
|
// #1293: WeKan as an Android share target. The web app manifest declares
|
|
// `share_target`, so on a phone where WeKan is installed as an app, Share →
|
|
// WeKan opens /share-target?title=…&text=…&url=…, where the member picks a
|
|
// board and list and the shared thing becomes a card there.
|
|
//
|
|
// What a share carries differs by app - a browser shares a page's title and
|
|
// URL, a notes app only text, some apps put the URL inside the text - so this
|
|
// turns it into one card title and description. Pure:
|
|
// tests/shareTarget.test.cjs.
|
|
|
|
const MAX_TITLE = 300;
|
|
const MAX_DESCRIPTION = 20000;
|
|
|
|
function clean(value, max) {
|
|
return typeof value === 'string' ? value.replace(/\r\n?/g, '\n').trim().slice(0, max) : '';
|
|
}
|
|
|
|
function sharedCard({ title, text, url } = {}) {
|
|
const t = clean(title, MAX_TITLE);
|
|
const x = clean(text, MAX_DESCRIPTION);
|
|
let u = clean(url, 2000);
|
|
if (u && !/^https?:\/\//i.test(u)) u = '';
|
|
const firstLine = x.split('\n').find(line => line.trim()) || '';
|
|
const cardTitle = (t || firstLine || u).replace(/\s+/g, ' ').trim().slice(0, MAX_TITLE);
|
|
const parts = [];
|
|
// The text, unless it is only the title again.
|
|
if (x && x !== cardTitle) parts.push(x);
|
|
// The address, unless the text already carries it or it is the title.
|
|
if (u && !x.includes(u) && u !== cardTitle) parts.push(u);
|
|
return { title: cardTitle, description: parts.join('\n\n').slice(0, MAX_DESCRIPTION) };
|
|
}
|
|
|
|
// The manifest's declaration, relative to the manifest's own address.
|
|
const SHARE_TARGET = Object.freeze({
|
|
action: 'share-target',
|
|
method: 'GET',
|
|
params: Object.freeze({ title: 'title', text: 'text', url: 'url' }),
|
|
});
|
|
|
|
module.exports = { sharedCard, SHARE_TARGET, MAX_TITLE };
|