feat(alert): can customize assets & title for Misskey notification
This commit is contained in:
parent
1e0b6058cb
commit
de6aaeb79b
10 changed files with 86 additions and 18 deletions
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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": "アカウント切り替え",
|
||||||
|
|
|
@ -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": "계정 전환",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -273,7 +283,7 @@ export const MisshaiPage: React.VFC = () => {
|
||||||
{t('_alertMode.notificationWarning')}
|
{t('_alertMode.notificationWarning')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{ (draft.alertMode === 'note' || draft.alertMode === 'both') && (
|
{(draft.alertMode === 'note' || draft.alertMode === 'both') && (
|
||||||
<>
|
<>
|
||||||
<h3 className="mt-2">{t('visibility')}</h3>
|
<h3 className="mt-2">{t('visibility')}</h3>
|
||||||
<div className="vstack slim">
|
<div className="vstack slim">
|
||||||
|
@ -306,20 +316,43 @@ export const MisshaiPage: React.VFC = () => {
|
||||||
{t('_template.insertVariables')}
|
{t('_template.insertVariables')}
|
||||||
</button>
|
</button>
|
||||||
<button className="btn link text-info" onClick={onClickInsertVariablesHelp}>
|
<button className="btn link text-info" onClick={onClickInsertVariablesHelp}>
|
||||||
<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 });
|
||||||
}} />
|
}}/>
|
||||||
<span className={`textarea-remaining ${remaining <= 0 ? 'text-red text-bold' : ''}`}>{remaining}</span>
|
<span className={`textarea-remaining ${remaining <= 0 ? 'text-red text-bold' : ''}`}>{remaining}</span>
|
||||||
</div>
|
</div>
|
||||||
<small className="text-dimmed">{t('_template.description2')}</small>
|
<small className="text-dimmed">{t('_template.description2')}</small>
|
||||||
<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>
|
||||||
|
@ -327,7 +360,7 @@ export const MisshaiPage: React.VFC = () => {
|
||||||
</section>
|
</section>
|
||||||
<section className="list-form mt-2">
|
<section className="list-form mt-2">
|
||||||
<button className="item" onClick={onClickSendAlert} disabled={draft.alertMode === 'nothing'}>
|
<button className="item" onClick={onClickSendAlert} disabled={draft.alertMode === 'nothing'}>
|
||||||
<i className="icon fas fa-paper-plane" />
|
<i className="icon fas fa-paper-plane"/>
|
||||||
<div className="body">
|
<div className="body">
|
||||||
<h1>{t('sendAlert')}</h1>
|
<h1>{t('sendAlert')}</h1>
|
||||||
<p className="desc">{t(draft.alertMode === 'nothing' ? 'sendAlertDisabled' : 'sendAlertDescription')}</p>
|
<p className="desc">{t(draft.alertMode === 'nothing' ? 'sendAlertDisabled' : 'sendAlertDescription')}</p>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue