Better test messages
This commit is contained in:
		
							parent
							
								
									488aeb119b
								
							
						
					
					
						commit
						3f978bc45f
					
				
					 7 changed files with 60 additions and 12 deletions
				
			
		
							
								
								
									
										6
									
								
								docs/static/css/extra.css
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								docs/static/css/extra.css
									
										
									
									
										vendored
									
									
								
							|  | @ -1,7 +1,7 @@ | ||||||
| :root { | :root { | ||||||
|     --md-primary-fg-color:        #3a9784; |     --md-primary-fg-color:        #338574; | ||||||
|     --md-primary-fg-color--light: #3a9784; |     --md-primary-fg-color--light: #338574; | ||||||
|     --md-primary-fg-color--dark:  #3a9784; |     --md-primary-fg-color--dark:  #338574; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .md-header__button.md-logo :is(img, svg) { | .md-header__button.md-logo :is(img, svg) { | ||||||
|  |  | ||||||
|  | @ -881,8 +881,8 @@ func parseSince(r *http.Request, poll bool) (sinceMarker, error) { | ||||||
| 
 | 
 | ||||||
| func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request) error { | func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request) error { | ||||||
| 	w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST") | 	w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST") | ||||||
| 	w.Header().Set("Access-Control-Allow-Origin", "*")              // CORS, allow cross-origin requests | 	w.Header().Set("Access-Control-Allow-Origin", "*")  // CORS, allow cross-origin requests | ||||||
| 	w.Header().Set("Access-Control-Allow-Headers", "Authorization") // CORS, allow auth via JS | 	w.Header().Set("Access-Control-Allow-Headers", "*") // CORS, allow auth via JS // FIXME is this terrible? | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,14 +26,24 @@ class Api { | ||||||
|         return messages; |         return messages; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async publish(baseUrl, topic, message) { |     async publish(baseUrl, topic, message, title, priority, tags) { | ||||||
|         const user = await userManager.get(baseUrl); |         const user = await userManager.get(baseUrl); | ||||||
|         const url = topicUrl(baseUrl, topic); |         const url = topicUrl(baseUrl, topic); | ||||||
|         console.log(`[Api] Publishing message to ${url}`); |         console.log(`[Api] Publishing message to ${url}`); | ||||||
|  |         const headers = {}; | ||||||
|  |         if (title) { | ||||||
|  |             headers["X-Title"] = title; | ||||||
|  |         } | ||||||
|  |         if (priority !== 3) { | ||||||
|  |             headers["X-Priority"] = `${priority}`; | ||||||
|  |         } | ||||||
|  |         if (tags.length > 0) { | ||||||
|  |             headers["X-Tags"] = tags.join(","); | ||||||
|  |         } | ||||||
|         await fetch(url, { |         await fetch(url, { | ||||||
|             method: 'PUT', |             method: 'PUT', | ||||||
|             body: message, |             body: message, | ||||||
|             headers: maybeWithBasicAuth({}, user) |             headers: maybeWithBasicAuth(headers, user) | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,6 +104,17 @@ export const encodeBase64Url = (s) => { | ||||||
|     return Base64.encodeURI(s); |     return Base64.encodeURI(s); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export const shuffle = (arr) => { | ||||||
|  |     let j, x; | ||||||
|  |     for (let index = arr.length - 1; index > 0; index--) { | ||||||
|  |         j = Math.floor(Math.random() * (index + 1)); | ||||||
|  |         x = arr[index]; | ||||||
|  |         arr[index] = arr[j]; | ||||||
|  |         arr[j] = x; | ||||||
|  |     } | ||||||
|  |     return arr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // https://jameshfisher.com/2017/10/30/web-cryptography-api-hello-world/
 | // https://jameshfisher.com/2017/10/30/web-cryptography-api-hello-world/
 | ||||||
| export const sha256 = async (s) => { | export const sha256 = async (s) => { | ||||||
|     const buf = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(s)); |     const buf = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(s)); | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ import Typography from "@mui/material/Typography"; | ||||||
| import * as React from "react"; | import * as React from "react"; | ||||||
| import {useEffect, useRef, useState} from "react"; | import {useEffect, useRef, useState} from "react"; | ||||||
| import Box from "@mui/material/Box"; | import Box from "@mui/material/Box"; | ||||||
| import {topicShortUrl} from "../app/utils"; | import {formatShortDateTime, shuffle, topicShortUrl} from "../app/utils"; | ||||||
| import {useLocation, useNavigate} from "react-router-dom"; | import {useLocation, useNavigate} from "react-router-dom"; | ||||||
| import ClickAwayListener from '@mui/material/ClickAwayListener'; | import ClickAwayListener from '@mui/material/ClickAwayListener'; | ||||||
| import Grow from '@mui/material/Grow'; | import Grow from '@mui/material/Grow'; | ||||||
|  | @ -108,8 +108,31 @@ const SettingsIcons = (props) => { | ||||||
|     const handleSendTestMessage = () => { |     const handleSendTestMessage = () => { | ||||||
|         const baseUrl = props.subscription.baseUrl; |         const baseUrl = props.subscription.baseUrl; | ||||||
|         const topic = props.subscription.topic; |         const topic = props.subscription.topic; | ||||||
|         api.publish(baseUrl, topic, |         const tags = shuffle([ | ||||||
|             `This is a test notification sent by the ntfy Web UI at ${new Date().toString()}.`); // FIXME result ignored
 |             "grinning", "octopus", "upside_down_face", "palm_tree", "maple_leaf", "apple", "skull", "warning", "jack_o_lantern", | ||||||
|  |             "de-server-1", "backups", "cron-script", "script-error", "phils-automation", "mouse", "go-rocks", "hi-ben"]) | ||||||
|  |                 .slice(0, Math.round(Math.random() * 4)); | ||||||
|  |         const priority = shuffle([1, 2, 3, 4, 5])[0]; | ||||||
|  |         const title = shuffle([ | ||||||
|  |             "", | ||||||
|  |             "", | ||||||
|  |             "", // Higher chance of no title
 | ||||||
|  |             "Oh my, another test message?", | ||||||
|  |             "Titles are optional, did you know that?", | ||||||
|  |             "ntfy is open source, and will always be free. Cool, right?", | ||||||
|  |             "I don't really like apples", | ||||||
|  |             "My favorite TV show is The Wire. You should watch it!" | ||||||
|  |         ])[0]; | ||||||
|  |         const message = shuffle([ | ||||||
|  |             `Hello friend, this is a test notification from ntfy web. It's ${formatShortDateTime(Date.now())} right now. Is that early or late?`, | ||||||
|  |             `So I heard you like ntfy? If that's true, go to GitHub and star it, or to the Play store and rate it. Thanks! Oh yeah, this is a test notification.`, | ||||||
|  |             `It's almost like you want to hear what I have to say. I'm not even a machine. I'm just a sentence that Phil typed on a random Thursday.`, | ||||||
|  |             `Alright then, it's ${formatShortDateTime(Date.now())} already. Boy oh boy, where did the time go? I hope you're alright, friend.`, | ||||||
|  |             `There are nine million bicycles in Beijing That's a fact; It's a thing we can't deny. I wonder if that's true ...`, | ||||||
|  |             `I'm really excited that you're trying out ntfy. Did you know that there are a few public topics, such as ntfy.sh/stats and ntfy.sh/annoucements.`, | ||||||
|  |             `It's interesting to hear what people use ntfy for. I've heard people talk about using it for so many cool things. What do you use it for?` | ||||||
|  |         ])[0]; | ||||||
|  |         api.publish(baseUrl, topic, message, title, priority, tags); | ||||||
|         setOpen(false); |         setOpen(false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,7 +20,6 @@ import ErrorBoundary from "./ErrorBoundary"; | ||||||
| import routes from "./routes"; | import routes from "./routes"; | ||||||
| import {useAutoSubscribe, useConnectionListeners} from "./hooks"; | import {useAutoSubscribe, useConnectionListeners} from "./hooks"; | ||||||
| 
 | 
 | ||||||
| // TODO better "send test message" (a la android app)
 |  | ||||||
| // TODO docs
 | // TODO docs
 | ||||||
| // TODO screenshot on homepage
 | // TODO screenshot on homepage
 | ||||||
| // TODO "copy url" toast
 | // TODO "copy url" toast
 | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ import SubscribeDialog from "./SubscribeDialog"; | ||||||
| import {Alert, AlertTitle, Badge, CircularProgress, ListSubheader} from "@mui/material"; | import {Alert, AlertTitle, Badge, CircularProgress, ListSubheader} from "@mui/material"; | ||||||
| import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||||
| import Typography from "@mui/material/Typography"; | import Typography from "@mui/material/Typography"; | ||||||
| import {topicShortUrl, topicUrl} from "../app/utils"; | import {openUrl, topicShortUrl, topicUrl} from "../app/utils"; | ||||||
| import routes from "./routes"; | import routes from "./routes"; | ||||||
| import {ConnectionState} from "../app/Connection"; | import {ConnectionState} from "../app/Connection"; | ||||||
| import {useLocation, useNavigate} from "react-router-dom"; | import {useLocation, useNavigate} from "react-router-dom"; | ||||||
|  | @ -23,6 +23,7 @@ import {ChatBubble, NotificationsOffOutlined} from "@mui/icons-material"; | ||||||
| import Box from "@mui/material/Box"; | import Box from "@mui/material/Box"; | ||||||
| import notifier from "../app/Notifier"; | import notifier from "../app/Notifier"; | ||||||
| import config from "../app/config"; | import config from "../app/config"; | ||||||
|  | import ArticleIcon from '@mui/icons-material/Article'; | ||||||
| 
 | 
 | ||||||
| const navWidth = 280; | const navWidth = 280; | ||||||
| 
 | 
 | ||||||
|  | @ -113,6 +114,10 @@ const NavList = (props) => { | ||||||
|                     <ListItemIcon><SettingsIcon/></ListItemIcon> |                     <ListItemIcon><SettingsIcon/></ListItemIcon> | ||||||
|                     <ListItemText primary="Settings"/> |                     <ListItemText primary="Settings"/> | ||||||
|                 </ListItemButton> |                 </ListItemButton> | ||||||
|  |                 <ListItemButton onClick={() => openUrl("/docs")}> | ||||||
|  |                     <ListItemIcon><ArticleIcon/></ListItemIcon> | ||||||
|  |                     <ListItemText primary="Documentation"/> | ||||||
|  |                 </ListItemButton> | ||||||
|                 <ListItemButton onClick={() => setSubscribeDialogOpen(true)}> |                 <ListItemButton onClick={() => setSubscribeDialogOpen(true)}> | ||||||
|                     <ListItemIcon><AddIcon/></ListItemIcon> |                     <ListItemIcon><AddIcon/></ListItemIcon> | ||||||
|                     <ListItemText primary="Add subscription"/> |                     <ListItemText primary="Add subscription"/> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue