0
0
Fork 0

feat(alert): can customize assets & title for Misskey notification

This commit is contained in:
무라쿠모 2024-08-25 23:19:04 +09:00
parent 1e0b6058cb
commit de6aaeb79b
No known key found for this signature in database
GPG key ID: 139D6573F92DA9F7
10 changed files with 86 additions and 18 deletions

View file

@ -20,6 +20,12 @@ export class UserSetting {
@IsOptional() @IsOptional()
template?: string; template?: string;
@IsOptional()
notificationHeader?: string;
@IsOptional()
notificationIcon?: string;
@IsOptional() @IsOptional()
useRanking?: boolean; useRanking?: boolean;

View file

@ -28,7 +28,7 @@ export class SessionController {
if (setting.alertMode != null) s.alertMode = setting.alertMode; if (setting.alertMode != null) s.alertMode = setting.alertMode;
if (setting.visibility != null) { if (setting.visibility != null) {
console.log(setting.visibility); console.log(setting.visibility);
if (setting.visibility === 'public' || setting.visibility === 'users') { if (setting.visibility === 'public'/* || setting.visibility === 'users'*/) {
throw new BadRequestError('Unsupported visibility'); throw new BadRequestError('Unsupported visibility');
} }
s.visibility = setting.visibility; s.visibility = setting.visibility;
@ -36,6 +36,8 @@ export class SessionController {
if (setting.localOnly != null) s.localOnly = setting.localOnly; if (setting.localOnly != null) s.localOnly = setting.localOnly;
if (setting.remoteFollowersOnly != null) s.remoteFollowersOnly = setting.remoteFollowersOnly; if (setting.remoteFollowersOnly != null) s.remoteFollowersOnly = setting.remoteFollowersOnly;
if (setting.template !== undefined) s.template = setting.template; if (setting.template !== undefined) s.template = setting.template;
if (setting.notificationHeader !== undefined) s.notificationHeader = setting.notificationHeader;
if (setting.notificationIcon !== undefined) s.notificationIcon = setting.notificationIcon;
if (setting.useRanking !== undefined) s.useRanking = setting.useRanking; if (setting.useRanking !== undefined) s.useRanking = setting.useRanking;
if (setting.appendHashtag !== undefined) s.appendHashtag = setting.appendHashtag; if (setting.appendHashtag !== undefined) s.appendHashtag = setting.appendHashtag;
if (Object.keys(s).length === 0) return; if (Object.keys(s).length === 0) return;

View file

@ -81,6 +81,20 @@ export class User implements IUser {
}) })
public template: string | null; public template: string | null;
@Column({
type: 'varchar',
length: 1024,
nullable: true,
})
public notificationHeader: string | null;
@Column({
type: 'varchar',
length: 1024,
nullable: true,
})
public notificationIcon: string | null;
@Column({ @Column({
type: 'real', type: 'real',
default: 0, default: 0,

View file

@ -51,8 +51,8 @@ export const sendNoteAlert = async (text: string, user: User) => {
*/ */
export const sendNotificationAlert = async (text: string, user: User) => { export const sendNotificationAlert = async (text: string, user: User) => {
const res = await api(user.host, 'notifications/create', { const res = await api(user.host, 'notifications/create', {
header: 'Misskey Tools with LycheeBridge', header: user.notificationHeader ?? 'Misskey Tools with LycheeBridge',
icon: 'https://t.psec.dev/assets/lcb.png', icon: user.notificationIcon ?? 'https://t.psec.dev/assets/lcb.png',
body: text, body: text,
}, user.token); }, user.token);

View file

@ -17,6 +17,8 @@ export interface IUser {
template: string | null; template: string | null;
prevRating: number; prevRating: number;
rating: number; rating: number;
notificationHeader: string | null;
notificationIcon: string | null;
bannedFromRanking: boolean; bannedFromRanking: boolean;
isAdmin?: boolean; isAdmin?: boolean;
tokenVersion: number; tokenVersion: number;

View file

@ -51,7 +51,7 @@
"update": "Update", "update": "Update",
"shareMisskeyTools": "Share #MisskeyTools", "shareMisskeyTools": "Share #MisskeyTools",
"shareMisskeyToolsNote": "Try #MisskeyTools !\n\nhttps://t.psec.dev", "shareMisskeyToolsNote": "Try #MisskeyTools !\n\nhttps://t.psec.dev",
"instanceUrlPlaceholder": "k.lapy.link, stella.place, psec.dev...", "instanceUrlPlaceholder": "oscar.surf, hoto.moe, k.lapy.link...",
"settings": "Settings", "settings": "Settings",
"accentColor": "Accent Color", "accentColor": "Accent Color",
"changelog": "Changelog", "changelog": "Changelog",
@ -104,7 +104,10 @@
"order": "Order", "order": "Order",
"showRanking": "Show Ranking", "showRanking": "Show Ranking",
"useRanking": "Join the Ranking", "useRanking": "Join the Ranking",
"appendHashtag": "Add #misshaialert hashtag in template" "appendHashtag": "Add #misshaialert hashtag in template",
"notificationHeader": "Misskey Notification Header (Title)",
"notificationIcon": "Misskey Notification Icon (URL)",
"notificationContent": "Notification Content"
}, },
"_accounts": { "_accounts": {
"switchAccount": "Switch your account", "switchAccount": "Switch your account",

View file

@ -51,7 +51,7 @@
"update": "更新する", "update": "更新する",
"shareMisskeyTools": "#MisskeyTools をシェアする", "shareMisskeyTools": "#MisskeyTools をシェアする",
"shareMisskeyToolsNote": "#MisskeyTools はいいぞ\n\nhttps://t.psec.dev", "shareMisskeyToolsNote": "#MisskeyTools はいいぞ\n\nhttps://t.psec.dev",
"instanceUrlPlaceholder": "k.lapy.link, stella.place, psec.dev...", "instanceUrlPlaceholder": "oscar.surf, hoto.moe, k.lapy.link...",
"settings": "設定", "settings": "設定",
"accentColor": "アクセントカラー", "accentColor": "アクセントカラー",
"changelog": "更新履歴", "changelog": "更新履歴",
@ -104,7 +104,10 @@
"order": "順位", "order": "順位",
"showRanking": "ランキングを見る", "showRanking": "ランキングを見る",
"useRanking": "ランキングに参加する", "useRanking": "ランキングに参加する",
"appendHashtag": "#misshaialertハッシュタグをテンプレートに追加" "appendHashtag": "#misshaialertハッシュタグをテンプレートに追加",
"notificationHeader": "Misskey通知タイトル",
"notificationIcon": "Misskey通知アイコン (URL)",
"notificationContent": "通知内容"
}, },
"_accounts": { "_accounts": {
"switchAccount": "アカウント切り替え", "switchAccount": "アカウント切り替え",

View file

@ -51,7 +51,7 @@
"update": "업데이트하기", "update": "업데이트하기",
"shareMisskeyTools": "#MisskeyTools 공유하기", "shareMisskeyTools": "#MisskeyTools 공유하기",
"shareMisskeyToolsNote": "함께 #MisskeyTools 하지 않으실래요?\n\nhttps://t.psec.dev", "shareMisskeyToolsNote": "함께 #MisskeyTools 하지 않으실래요?\n\nhttps://t.psec.dev",
"instanceUrlPlaceholder": "k.lapy.link, stella.place, psec.dev...", "instanceUrlPlaceholder": "oscar.surf, hoto.moe, k.lapy.link...",
"settings": "설정", "settings": "설정",
"accentColor": "강조 색상", "accentColor": "강조 색상",
"changelog": "업데이트 내역", "changelog": "업데이트 내역",
@ -104,7 +104,10 @@
"order": "순위", "order": "순위",
"showRanking": "랭킹 보기", "showRanking": "랭킹 보기",
"useRanking": "노트왕 랭킹에 이름을 표시하기", "useRanking": "노트왕 랭킹에 이름을 표시하기",
"appendHashtag": "#misshaialert 해시태그를 추가하기" "appendHashtag": "#misshaialert 해시태그를 추가하기",
"notificationHeader": "Misskey 알림 헤더 (제목)",
"notificationIcon": "Misskey 알림 아이콘 (URL)",
"notificationContent": "알림 내용"
}, },
"_accounts": { "_accounts": {
"switchAccount": "계정 전환", "switchAccount": "계정 전환",

View file

@ -36,6 +36,8 @@ type SettingDraftType = Partial<Pick<IUser,
| 'localOnly' | 'localOnly'
| 'remoteFollowersOnly' | 'remoteFollowersOnly'
| 'template' | 'template'
| 'notificationHeader'
| 'notificationIcon'
| 'useRanking' | 'useRanking'
| 'appendHashtag' | 'appendHashtag'
>>; >>;
@ -60,6 +62,8 @@ export const MisshaiPage: React.VFC = () => {
localOnly: data?.localOnly ?? false, localOnly: data?.localOnly ?? false,
remoteFollowersOnly: data?.remoteFollowersOnly ?? false, remoteFollowersOnly: data?.remoteFollowersOnly ?? false,
template: data?.template ?? null, template: data?.template ?? null,
notificationHeader: data?.notificationHeader ?? null,
notificationIcon: data?.notificationIcon ?? null,
useRanking: data?.useRanking ?? false, useRanking: data?.useRanking ?? false,
appendHashtag: data?.appendHashtag ?? true, appendHashtag: data?.appendHashtag ?? true,
}); });
@ -67,8 +71,10 @@ export const MisshaiPage: React.VFC = () => {
const templateTextarea = useRef<HTMLTextAreaElement>(null); const templateTextarea = useRef<HTMLTextAreaElement>(null);
const availableVisibilities: Visibility[] = [ const availableVisibilities: Visibility[] = [
// 'public',
'home', 'home',
'followers' 'followers',
'users',
]; ];
const updateSetting = useCallback((obj: SettingDraftType) => { const updateSetting = useCallback((obj: SettingDraftType) => {
@ -102,6 +108,8 @@ export const MisshaiPage: React.VFC = () => {
localOnly: data.localOnly, localOnly: data.localOnly,
remoteFollowersOnly: data.remoteFollowersOnly, remoteFollowersOnly: data.remoteFollowersOnly,
template: data.template, template: data.template,
notificationHeader: data.notificationHeader,
notificationIcon: data.notificationIcon,
useRanking: data.useRanking, useRanking: data.useRanking,
appendHashtag: data.appendHashtag appendHashtag: data.appendHashtag
}); });
@ -188,6 +196,8 @@ export const MisshaiPage: React.VFC = () => {
}, [session.error]); }, [session.error]);
const defaultTemplate = t('_template.default'); const defaultTemplate = t('_template.default');
const defaultHeader = 'Misskey Tools with LycheeBridge';
const defaultIcon = 'https://t.psec.dev/assets/lcb.png';
const remaining = 1024 - (draft.template ?? defaultTemplate).length; const remaining = 1024 - (draft.template ?? defaultTemplate).length;
@ -309,6 +319,25 @@ export const MisshaiPage: React.VFC = () => {
<i className="fas fa-circle-question"/> <i className="fas fa-circle-question"/>
</button> </button>
</div> </div>
{(draft.alertMode === 'notification' || draft.alertMode === 'both') && (
<>
<label className="input-field">
{t('_missHai.notificationHeader')}
</label>
<input type="text" className="input-field" value={draft.notificationHeader ?? defaultHeader} placeholder={defaultHeader} onChange={(e) => {
dispatchDraft({ notificationHeader: e.target.value });
}}/>
<label className="input-field">
{t('_missHai.notificationIcon')}
</label>
<input type="link" className="input-field" value={draft.notificationIcon ?? defaultIcon} placeholder={defaultIcon} onChange={(e) => {
dispatchDraft({ notificationIcon: e.target.value });
}}/>
</>
)}
<label className="input-field">
{t('_missHai.notificationContent')}
</label>
<div className="textarea-wrapper"> <div className="textarea-wrapper">
<textarea ref={templateTextarea} className="input-field" value={draft.template ?? defaultTemplate} placeholder={defaultTemplate} style={{height: 240}} onChange={(e) => { <textarea ref={templateTextarea} className="input-field" value={draft.template ?? defaultTemplate} placeholder={defaultTemplate} style={{height: 240}} onChange={(e) => {
dispatchDraft({ template: e.target.value }); dispatchDraft({ template: e.target.value });
@ -319,7 +348,11 @@ export const MisshaiPage: React.VFC = () => {
<div className="hstack mt-2"> <div className="hstack mt-2">
<button className="btn danger" onClick={() => dispatchDraft({ template: null })}>{t('resetToDefault')}</button> <button className="btn danger" onClick={() => dispatchDraft({ template: null })}>{t('resetToDefault')}</button>
<button className="btn primary" onClick={() => { <button className="btn primary" onClick={() => {
updateSettingWithDialog({ template: draft.template === '' ? null : draft.template }); updateSettingWithDialog({
template: draft.template === '' ? null : draft.template,
notificationHeader: draft.notificationHeader === '' ? null : draft.notificationHeader,
notificationIcon: draft.notificationIcon === '' ? null : draft.notificationIcon
});
}} disabled={remaining < 0}>{t('save')}</button> }} disabled={remaining < 0}>{t('save')}</button>
</div> </div>
</div> </div>

View file

@ -7,7 +7,7 @@ import styled from 'styled-components';
import { useSelector } from '../store'; import { useSelector } from '../store';
import { IsMobileProp } from '../misc/is-mobile-prop'; import { IsMobileProp } from '../misc/is-mobile-prop';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { useAnnouncements } from '../hooks/useAnnouncements'; // import { useAnnouncements } from '../hooks/useAnnouncements';
const Hero = styled.div<IsMobileProp>` const Hero = styled.div<IsMobileProp>`
display: flex; display: flex;
@ -34,7 +34,7 @@ const Hero = styled.div<IsMobileProp>`
padding: var(--margin); padding: var(--margin);
border-radius: var(--radius); border-radius: var(--radius);
background: var(--black-50); background: var(--black-50);
backdrop-filter: blur(4px) brightness(120%);IconMCustard backdrop-filter: blur(4px) brightness(120%);
display: none; display: none;
} }
} }
@ -68,7 +68,7 @@ export const IndexWelcomePage: React.VFC = () => {
const {isMobile} = useSelector(state => state.screen); const {isMobile} = useSelector(state => state.screen);
const {t} = useTranslation(); const {t} = useTranslation();
const announcements = useAnnouncements(); // const announcements = useAnnouncements();
return ( return (
<> <>
@ -81,6 +81,7 @@ export const IndexWelcomePage: React.VFC = () => {
<LoginForm /> <LoginForm />
</FormWrapper> </FormWrapper>
</div> </div>
{/*
<div className="announcements"> <div className="announcements">
<h2><i className="fas fa-bell"></i> {t('announcements')}</h2> <h2><i className="fas fa-bell"></i> {t('announcements')}</h2>
<div className="menu xmenu"> <div className="menu xmenu">
@ -91,6 +92,7 @@ export const IndexWelcomePage: React.VFC = () => {
))} ))}
</div> </div>
</div> </div>
*/}
<div className="rects"> <div className="rects">
<div className="rect"></div> <div className="rect"></div>
<div className="rect"></div> <div className="rect"></div>