Merge cb6e2be2a3
into 218070eb13
This commit is contained in:
commit
7ea86f1a45
|
@ -158,13 +158,19 @@ export class ClientServerService {
|
|||
'purpose': 'any',
|
||||
}],
|
||||
'share_target': {
|
||||
'action': '/share/',
|
||||
'method': 'GET',
|
||||
'enctype': 'application/x-www-form-urlencoded',
|
||||
'action': '/sw/share',
|
||||
'method': 'POST',
|
||||
'enctype': 'multipart/form-data',
|
||||
'params': {
|
||||
'title': 'title',
|
||||
'text': 'text',
|
||||
'url': 'url',
|
||||
'files': [
|
||||
{
|
||||
'name': 'files',
|
||||
'accept': '*/*',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'shortcuts': [{
|
||||
|
|
|
@ -26,13 +26,19 @@
|
|||
}
|
||||
],
|
||||
"share_target": {
|
||||
"action": "/share/",
|
||||
"method": "GET",
|
||||
"enctype": "application/x-www-form-urlencoded",
|
||||
"action": "/sw/share",
|
||||
"method": "POST",
|
||||
"enctype": "multipart/form-data",
|
||||
"params": {
|
||||
"title": "title",
|
||||
"text": "text",
|
||||
"url": "url"
|
||||
"url": "url",
|
||||
"files": [
|
||||
{
|
||||
"name": "files",
|
||||
"accept": "*/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"shortcuts": [
|
||||
|
|
|
@ -227,6 +227,10 @@ uploader.events.on('itemUploaded', ctx => {
|
|||
uploader.removeItem(ctx.item);
|
||||
});
|
||||
|
||||
if (props.initialLocalFiles) {
|
||||
uploader.addFiles(props.initialLocalFiles);
|
||||
}
|
||||
|
||||
const draftKey = computed((): string => {
|
||||
let key = targetChannel.value ? `channel:${targetChannel.value.id}` : '';
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:initialText="initialText"
|
||||
:initialVisibility="visibility"
|
||||
:initialFiles="files"
|
||||
:initialLocalFiles="tempFiles"
|
||||
:initialLocalOnly="localOnly"
|
||||
:reply="reply"
|
||||
:renote="renote"
|
||||
|
@ -33,6 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
import { ref, computed } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { get, del } from 'idb-keyval';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkPostForm from '@/components/MkPostForm.vue';
|
||||
import * as os from '@/os.js';
|
||||
|
@ -41,7 +43,19 @@ import { definePage } from '@/page.js';
|
|||
import { postMessageToParentWindow } from '@/utility/post-message.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
//#region parameters
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
// merge hash parameters
|
||||
try {
|
||||
const hashParams = new URLSearchParams(window.location.hash.slice(1));
|
||||
for (const [key, value] of hashParams.entries()) {
|
||||
urlParams.set(key, value);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse hash parameters:', e);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const localOnlyQuery = urlParams.get('localOnly');
|
||||
const visibilityQuery = urlParams.get('visibility') as typeof Misskey.noteVisibilities[number];
|
||||
|
||||
|
@ -56,6 +70,7 @@ const visibility = ref(Misskey.noteVisibilities.includes(visibilityQuery) ? visi
|
|||
const localOnly = ref(localOnlyQuery === '0' ? false : localOnlyQuery === '1' ? true : undefined);
|
||||
const files = ref([] as Misskey.entities.DriveFile[]);
|
||||
const visibleUsers = ref([] as Misskey.entities.UserDetailed[]);
|
||||
const tempFiles = ref([] as File[]);
|
||||
|
||||
async function init() {
|
||||
let noteText = '';
|
||||
|
@ -181,6 +196,29 @@ async function init() {
|
|||
});
|
||||
}
|
||||
|
||||
//#region Local files
|
||||
// If the browser supports IndexedDB, try to get the temporary files from temp.
|
||||
if (typeof window !== 'undefined' ? !!(window.indexedDB && typeof window.indexedDB.open === 'function') : true) {
|
||||
const filesFromIdb = await get<File[]>('share-files-temp');
|
||||
if (Array.isArray(filesFromIdb) && filesFromIdb.length > 0 && filesFromIdb.every(file => file instanceof Blob)) {
|
||||
tempFiles.value = filesFromIdb;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('file') && urlParams.get('file').startsWith('data:')) {
|
||||
try {
|
||||
const file = await window.fetch(urlParams.get('file')).then(res => res.blob());
|
||||
if (file instanceof Blob) {
|
||||
tempFiles.value.push(file as File);
|
||||
} else {
|
||||
console.error('Fetched file is not a Blob:', file);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch file:', e);
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
state.value = 'writing';
|
||||
}
|
||||
|
||||
|
@ -201,10 +239,31 @@ function goToMisskey(): void {
|
|||
|
||||
function onPosted(): void {
|
||||
state.value = 'posted';
|
||||
// SWが保存したファイルは投稿が完了するまでIndexedDBに保持
|
||||
del('share-files-temp');
|
||||
postMessageToParentWindow('misskey:shareForm:shareCompleted');
|
||||
}
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
const headerActions = computed(() => [
|
||||
{
|
||||
icon: 'ti ti-dots',
|
||||
text: i18n.ts.menu,
|
||||
handler: (ev: MouseEvent) => {
|
||||
os.popupMenu([
|
||||
{
|
||||
icon: 'ti ti-home',
|
||||
text: i18n.ts.goToMisskey,
|
||||
action: () => goToMisskey(),
|
||||
},
|
||||
{
|
||||
icon: 'ti ti-x',
|
||||
text: i18n.ts.close,
|
||||
action: () => close(),
|
||||
},
|
||||
], ev.currentTarget ?? ev.target);
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const headerTabs = computed(() => []);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface PostFormProps {
|
|||
initialCw?: string;
|
||||
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
||||
initialFiles?: Misskey.entities.DriveFile[];
|
||||
initialLocalFiles?: File[];
|
||||
initialLocalOnly?: boolean;
|
||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||
initialNote?: Misskey.entities.Note;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { get } from 'idb-keyval';
|
||||
import { get, set } from 'idb-keyval';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import type { PushNotificationDataMap } from '@/types.js';
|
||||
import type { I18n } from '@@/js/i18n.js';
|
||||
|
@ -40,6 +40,34 @@ async function offlineContentHTML() {
|
|||
}
|
||||
|
||||
globalThis.addEventListener('fetch', ev => {
|
||||
//#region /sw/share
|
||||
const url = new URL(ev.request.url);
|
||||
if (url.pathname === '/sw/share') {
|
||||
ev.respondWith((async () => {
|
||||
const responseUrl = new URL(ev.request.url);
|
||||
responseUrl.pathname = '/share';
|
||||
const formData = await ev.request.formData();
|
||||
|
||||
// とりあえず初期化 (IndexedDBの削除は時間がかかる可能性があるため空の配列をセット)
|
||||
await set('share-url-temp', []);
|
||||
if (formData.has('files')) {
|
||||
const files = formData.getAll('files');
|
||||
if (files.length > 0 && files.every(file => file instanceof Blob)) {
|
||||
set('share-files-temp', files);
|
||||
}
|
||||
}
|
||||
|
||||
formData.delete('files');
|
||||
for (const [key, value] of formData.entries()) {
|
||||
responseUrl.searchParams.set(key, value.toString());
|
||||
}
|
||||
|
||||
return Response.redirect(responseUrl, 303);
|
||||
})());
|
||||
return;
|
||||
}
|
||||
|
||||
//#region others
|
||||
let isHTMLRequest = false;
|
||||
if (ev.request.headers.get('sec-fetch-dest') === 'document') {
|
||||
isHTMLRequest = true;
|
||||
|
@ -62,6 +90,7 @@ globalThis.addEventListener('fetch', ev => {
|
|||
});
|
||||
}),
|
||||
);
|
||||
//#endregion
|
||||
});
|
||||
|
||||
globalThis.addEventListener('push', ev => {
|
||||
|
|
Loading…
Reference in New Issue