mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-11-30 12:20:15 +01:00
Merge 36e26ff723 into b531bc95ea
This commit is contained in:
commit
5dc35c53cb
8 changed files with 106 additions and 4 deletions
2
Makefile
2
Makefile
|
|
@ -1,7 +1,7 @@
|
|||
MAKEFLAGS := --jobs=1
|
||||
PYTHON := python3
|
||||
PIP := pip3
|
||||
VERSION := $(shell git describe --tag)
|
||||
VERSION := 1111
|
||||
COMMIT := $(shell git rev-parse --short HEAD)
|
||||
|
||||
.PHONY:
|
||||
|
|
|
|||
|
|
@ -636,6 +636,16 @@ func (s *Server) handleWebManifest(w http.ResponseWriter, _ *http.Request, _ *vi
|
|||
{SRC: "/static/images/pwa-192x192.png", Sizes: "192x192", Type: "image/png"},
|
||||
{SRC: "/static/images/pwa-512x512.png", Sizes: "512x512", Type: "image/png"},
|
||||
},
|
||||
ShareTarget: &webManifestShareTarget{
|
||||
Action: "/share-target",
|
||||
Method: "POST",
|
||||
Enctype: "multipart/form-data",
|
||||
Params: &webManifestShareParams{
|
||||
Title: "title",
|
||||
Text: "text",
|
||||
Url: "url",
|
||||
},
|
||||
},
|
||||
}
|
||||
return s.writeJSONWithContentType(w, response, "application/manifest+json")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -587,6 +587,8 @@ type webManifestResponse struct {
|
|||
BackgroundColor string `json:"background_color"`
|
||||
ThemeColor string `json:"theme_color"`
|
||||
Icons []*webManifestIcon `json:"icons"`
|
||||
// Optional PWA share_target support
|
||||
ShareTarget *webManifestShareTarget `json:"share_target,omitempty"`
|
||||
}
|
||||
|
||||
type webManifestIcon struct {
|
||||
|
|
@ -594,3 +596,16 @@ type webManifestIcon struct {
|
|||
Sizes string `json:"sizes"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type webManifestShareTarget struct {
|
||||
Action string `json:"action"`
|
||||
Method string `json:"method"`
|
||||
Enctype string `json:"enctype"`
|
||||
Params *webManifestShareParams `json:"params"`
|
||||
}
|
||||
|
||||
type webManifestShareParams struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "NODE_OPTIONS=\"--enable-source-maps\" vite",
|
||||
"start": "NODE_OPTIONS=\"--enable-source-maps\" vite --host",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"format": "prettier . --write",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,40 @@ self.addEventListener("notificationclick", (event) => {
|
|||
event.waitUntil(handleClick(event));
|
||||
});
|
||||
|
||||
// Handle incoming Share Target POSTs from installed PWA share action.
|
||||
// The share_target in the manifest posts to `/share-target` as a `multipart/form-data` POST.
|
||||
// We parse the form and redirect to the app with query params so the client can pick them up.
|
||||
self.addEventListener("fetch", (event) => {
|
||||
try {
|
||||
const reqUrl = new URL(event.request.url);
|
||||
if (reqUrl.pathname === "/share-target" && event.request.method === "POST") {
|
||||
event.respondWith(
|
||||
(async () => {
|
||||
try {
|
||||
const formData = await event.request.formData();
|
||||
const title = formData.get("title") || "";
|
||||
const text = formData.get("text") || "";
|
||||
const sharedUrl = formData.get("url") || "";
|
||||
const params = new URLSearchParams();
|
||||
if (title) params.set("share_title", title);
|
||||
if (text) params.set("share_text", text);
|
||||
if (sharedUrl) params.set("share_url", sharedUrl);
|
||||
// todo support files?
|
||||
// redirect to app.html which is the SPA entry used by the service worker
|
||||
const redirectUrl = `/app.html?${params.toString()}`;
|
||||
return Response.redirect(redirectUrl, 303);
|
||||
} catch (e) {
|
||||
return new Response("", { status: 500 });
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
// other fetches are not handled here
|
||||
} catch (e) {
|
||||
// ignore malformed urls
|
||||
}
|
||||
});
|
||||
|
||||
// See https://vite-pwa-org.netlify.app/guide/inject-manifest.html#service-worker-code
|
||||
// self.__WB_MANIFEST is the workbox injection point that injects the manifest of the
|
||||
// vite dist files and their revision ids, for example:
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ const Layout = () => {
|
|||
const { account, setAccount } = useContext(AccountContext);
|
||||
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||
const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
|
||||
const [sharePayload, setSharePayload] = useState(null);
|
||||
const users = useLiveQuery(() => userManager.all());
|
||||
const subscriptions = useLiveQuery(() => subscriptionManager.all());
|
||||
const webPushTopics = useWebPushTopics();
|
||||
|
|
@ -120,6 +121,29 @@ const Layout = () => {
|
|||
useBackgroundProcesses();
|
||||
useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
|
||||
|
||||
// Check URL for share-target params inserted by the service worker redirect
|
||||
useEffect(() => {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const title = params.get("share_title");
|
||||
const text = params.get("share_text");
|
||||
const url = params.get(" share_url");
|
||||
if (title || text || url) {
|
||||
const payload = {
|
||||
title: title || undefined,
|
||||
message: text || undefined,
|
||||
url: url || undefined,
|
||||
};
|
||||
setSharePayload(payload);
|
||||
setSendDialogOpenMode(PublishDialog.OPEN_MODE_DEFAULT);
|
||||
// remove params to avoid repeated triggers
|
||||
window.history.replaceState({}, document.title, window.location.pathname + window.location.hash);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<ActionBar selected={selected} onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)} />
|
||||
|
|
@ -139,7 +163,12 @@ const Layout = () => {
|
|||
}}
|
||||
/>
|
||||
</Main>
|
||||
<Messaging selected={selected} dialogOpenMode={sendDialogOpenMode} onDialogOpenModeChange={setSendDialogOpenMode} />
|
||||
<Messaging
|
||||
selected={selected}
|
||||
dialogOpenMode={sendDialogOpenMode}
|
||||
onDialogOpenModeChange={setSendDialogOpenMode}
|
||||
sharePayload={sharePayload}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ const Messaging = (props) => {
|
|||
openMode={dialogOpenMode}
|
||||
baseUrl={subscription?.baseUrl ?? config.base_url}
|
||||
topic={subscription?.topic ?? ""}
|
||||
message={message}
|
||||
message={props.sharePayload?.message ?? message}
|
||||
title={props.sharePayload?.title}
|
||||
clickUrl={props.sharePayload?.url}
|
||||
attachFile={attachFile}
|
||||
getPastedImage={getPastedImage}
|
||||
onClose={handleDialogClose}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,18 @@ const PublishDialog = (props) => {
|
|||
setMessage(props.message);
|
||||
}, [props.message]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof props.title !== "undefined") {
|
||||
setTitle(props.title || "");
|
||||
}
|
||||
}, [props.title]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof props.clickUrl !== "undefined") {
|
||||
setClickUrl(props.clickUrl || "");
|
||||
}
|
||||
}, [props.clickUrl]);
|
||||
|
||||
const updateBaseUrl = (newVal) => {
|
||||
if (validUrl(newVal)) {
|
||||
setBaseUrl(newVal.replace(/\/$/, "")); // strip traililng slash after https?://
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue