mirror of
https://github.com/MisskeyIO/misskey
synced 2024-11-27 14:28:49 +09:00
watermark wip
This commit is contained in:
parent
122ed3c82d
commit
e69b57072f
@ -464,6 +464,9 @@ share: "Share"
|
|||||||
notFound: "Not found"
|
notFound: "Not found"
|
||||||
notFoundDescription: "No page corresponding to this URL could be found."
|
notFoundDescription: "No page corresponding to this URL could be found."
|
||||||
uploadFolder: "Default folder for uploads"
|
uploadFolder: "Default folder for uploads"
|
||||||
|
watermark: "Watermark"
|
||||||
|
useWatermark: "Use watermark"
|
||||||
|
useWatermarkDescription: "Add a watermark to images"
|
||||||
markAsReadAllNotifications: "Mark all notifications as read"
|
markAsReadAllNotifications: "Mark all notifications as read"
|
||||||
markAsReadAllUnreadNotes: "Mark all notes as read"
|
markAsReadAllUnreadNotes: "Mark all notes as read"
|
||||||
markAsReadAllTalkMessages: "Mark all messages as read"
|
markAsReadAllTalkMessages: "Mark all messages as read"
|
||||||
@ -778,8 +781,11 @@ makeExplorable: "Make account visible in \"Explore\""
|
|||||||
makeExplorableDescription: "If you turn this off, your account will not show up in the \"Explore\" section."
|
makeExplorableDescription: "If you turn this off, your account will not show up in the \"Explore\" section."
|
||||||
showGapBetweenNotesInTimeline: "Show a gap between posts on the timeline"
|
showGapBetweenNotesInTimeline: "Show a gap between posts on the timeline"
|
||||||
duplicate: "Duplicate"
|
duplicate: "Duplicate"
|
||||||
left: "Left"
|
top: "Top"
|
||||||
|
bottom: "Bottom"
|
||||||
center: "Center"
|
center: "Center"
|
||||||
|
left: "Left"
|
||||||
|
right: "Right"
|
||||||
wide: "Wide"
|
wide: "Wide"
|
||||||
narrow: "Narrow"
|
narrow: "Narrow"
|
||||||
reloadToApplySetting: "This setting will only apply after a page reload. Reload now?"
|
reloadToApplySetting: "This setting will only apply after a page reload. Reload now?"
|
||||||
@ -1104,14 +1110,23 @@ editMemo: "Edit memo"
|
|||||||
reactionsList: "Reactions"
|
reactionsList: "Reactions"
|
||||||
renotesList: "Renotes"
|
renotesList: "Renotes"
|
||||||
notificationDisplay: "Notifications"
|
notificationDisplay: "Notifications"
|
||||||
|
placement: "Placement"
|
||||||
leftTop: "Top left"
|
leftTop: "Top left"
|
||||||
|
centerTop: "Top center"
|
||||||
rightTop: "Top right"
|
rightTop: "Top right"
|
||||||
|
leftCenter: "Center left"
|
||||||
|
rightCenter: "Center right"
|
||||||
leftBottom: "Bottom left"
|
leftBottom: "Bottom left"
|
||||||
|
centerBottom: "Bottom center"
|
||||||
rightBottom: "Bottom right"
|
rightBottom: "Bottom right"
|
||||||
stackAxis: "Stacking direction"
|
stackAxis: "Stacking direction"
|
||||||
vertical: "Vertical"
|
vertical: "Vertical"
|
||||||
horizontal: "Horizontal"
|
horizontal: "Horizontal"
|
||||||
position: "Position"
|
position: "Position"
|
||||||
|
repeat: "Repeat"
|
||||||
|
enlargement: "Enlargement"
|
||||||
|
rotate: "Rotate"
|
||||||
|
opacity: "Opacity"
|
||||||
serverRules: "Server rules"
|
serverRules: "Server rules"
|
||||||
pleaseConfirmBelowBeforeSignup: "To register on this server, you must review and agree to the following:"
|
pleaseConfirmBelowBeforeSignup: "To register on this server, you must review and agree to the following:"
|
||||||
pleaseAgreeAllToContinue: "You must agree to all above fields to continue."
|
pleaseAgreeAllToContinue: "You must agree to all above fields to continue."
|
||||||
@ -1811,10 +1826,11 @@ _role:
|
|||||||
descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. "
|
descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. "
|
||||||
canHideAds: "Can hide ads"
|
canHideAds: "Can hide ads"
|
||||||
canSearchNotes: "Usage of note search"
|
canSearchNotes: "Usage of note search"
|
||||||
canUseTranslator: "Can use Translator"
|
|
||||||
avatarDecorationLimit: "Maximum number of avatar decorations that can be applied"
|
|
||||||
canUseDriveFileInSoundSettings: "Can use Drive File in Sound Settings"
|
|
||||||
canUseReaction: "Can use reactions"
|
canUseReaction: "Can use reactions"
|
||||||
|
canUseTranslator: "Can use Translator"
|
||||||
|
canUseDriveFileInSoundSettings: "Can use Drive File in Sound Settings"
|
||||||
|
canUseWatermark: "Can use Watermark"
|
||||||
|
avatarDecorationLimit: "Maximum number of avatar decorations that can be applied"
|
||||||
mutualLinkSectionLimit: "Maximum number of mutual link sections"
|
mutualLinkSectionLimit: "Maximum number of mutual link sections"
|
||||||
mutualLinkLimit: "Maximum number of mutual links in a section"
|
mutualLinkLimit: "Maximum number of mutual links in a section"
|
||||||
_condition:
|
_condition:
|
||||||
|
72
locales/index.d.ts
vendored
72
locales/index.d.ts
vendored
@ -1884,6 +1884,18 @@ export interface Locale extends ILocale {
|
|||||||
* 既定アップロード先
|
* 既定アップロード先
|
||||||
*/
|
*/
|
||||||
"uploadFolder": string;
|
"uploadFolder": string;
|
||||||
|
/**
|
||||||
|
* ウォーターマーク
|
||||||
|
*/
|
||||||
|
"watermark": string;
|
||||||
|
/**
|
||||||
|
* ウォーターマークをつける
|
||||||
|
*/
|
||||||
|
"useWatermark": string;
|
||||||
|
/**
|
||||||
|
* 画像にウォーターマークを追加します
|
||||||
|
*/
|
||||||
|
"useWatermarkDescription": string;
|
||||||
/**
|
/**
|
||||||
* すべての通知を既読にする
|
* すべての通知を既読にする
|
||||||
*/
|
*/
|
||||||
@ -3137,13 +3149,25 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"duplicate": string;
|
"duplicate": string;
|
||||||
/**
|
/**
|
||||||
* 左
|
* 上
|
||||||
*/
|
*/
|
||||||
"left": string;
|
"top": string;
|
||||||
|
/**
|
||||||
|
* 下
|
||||||
|
*/
|
||||||
|
"bottom": string;
|
||||||
/**
|
/**
|
||||||
* 中央
|
* 中央
|
||||||
*/
|
*/
|
||||||
"center": string;
|
"center": string;
|
||||||
|
/**
|
||||||
|
* 左
|
||||||
|
*/
|
||||||
|
"left": string;
|
||||||
|
/**
|
||||||
|
* 右
|
||||||
|
*/
|
||||||
|
"right": string;
|
||||||
/**
|
/**
|
||||||
* 広い
|
* 広い
|
||||||
*/
|
*/
|
||||||
@ -4443,18 +4467,38 @@ export interface Locale extends ILocale {
|
|||||||
* 通知の表示
|
* 通知の表示
|
||||||
*/
|
*/
|
||||||
"notificationDisplay": string;
|
"notificationDisplay": string;
|
||||||
|
/**
|
||||||
|
* 配置
|
||||||
|
*/
|
||||||
|
"placement": string;
|
||||||
/**
|
/**
|
||||||
* 左上
|
* 左上
|
||||||
*/
|
*/
|
||||||
"leftTop": string;
|
"leftTop": string;
|
||||||
|
/**
|
||||||
|
* 中上
|
||||||
|
*/
|
||||||
|
"centerTop": string;
|
||||||
/**
|
/**
|
||||||
* 右上
|
* 右上
|
||||||
*/
|
*/
|
||||||
"rightTop": string;
|
"rightTop": string;
|
||||||
|
/**
|
||||||
|
* 左中
|
||||||
|
*/
|
||||||
|
"leftCenter": string;
|
||||||
|
/**
|
||||||
|
* 右中
|
||||||
|
*/
|
||||||
|
"rightCenter": string;
|
||||||
/**
|
/**
|
||||||
* 左下
|
* 左下
|
||||||
*/
|
*/
|
||||||
"leftBottom": string;
|
"leftBottom": string;
|
||||||
|
/**
|
||||||
|
* 中下
|
||||||
|
*/
|
||||||
|
"centerBottom": string;
|
||||||
/**
|
/**
|
||||||
* 右下
|
* 右下
|
||||||
*/
|
*/
|
||||||
@ -4475,6 +4519,22 @@ export interface Locale extends ILocale {
|
|||||||
* 位置
|
* 位置
|
||||||
*/
|
*/
|
||||||
"position": string;
|
"position": string;
|
||||||
|
/**
|
||||||
|
* 繰り返し
|
||||||
|
*/
|
||||||
|
"repeat": string;
|
||||||
|
/**
|
||||||
|
* 引き伸ばし
|
||||||
|
*/
|
||||||
|
"enlargement": string;
|
||||||
|
/**
|
||||||
|
* 回転
|
||||||
|
*/
|
||||||
|
"rotate": string;
|
||||||
|
/**
|
||||||
|
* 透明度
|
||||||
|
*/
|
||||||
|
"opacity": string;
|
||||||
/**
|
/**
|
||||||
* サーバールール
|
* サーバールール
|
||||||
*/
|
*/
|
||||||
@ -7091,6 +7151,10 @@ export interface Locale extends ILocale {
|
|||||||
* ノート検索の利用
|
* ノート検索の利用
|
||||||
*/
|
*/
|
||||||
"canSearchNotes": string;
|
"canSearchNotes": string;
|
||||||
|
/**
|
||||||
|
* リアクションの利用
|
||||||
|
*/
|
||||||
|
"canUseReaction": string;
|
||||||
/**
|
/**
|
||||||
* 翻訳機能の利用
|
* 翻訳機能の利用
|
||||||
*/
|
*/
|
||||||
@ -7100,9 +7164,9 @@ export interface Locale extends ILocale {
|
|||||||
*/
|
*/
|
||||||
"canUseDriveFileInSoundSettings": string;
|
"canUseDriveFileInSoundSettings": string;
|
||||||
/**
|
/**
|
||||||
* リアクションの利用
|
* ウォーターマークの利用
|
||||||
*/
|
*/
|
||||||
"canUseReaction": string;
|
"canUseWatermark": string;
|
||||||
/**
|
/**
|
||||||
* アイコンデコレーションの最大取付個数
|
* アイコンデコレーションの最大取付個数
|
||||||
*/
|
*/
|
||||||
|
@ -467,6 +467,9 @@ share: "共有"
|
|||||||
notFound: "見つかりません"
|
notFound: "見つかりません"
|
||||||
notFoundDescription: "指定されたURLに該当するページはありませんでした。"
|
notFoundDescription: "指定されたURLに該当するページはありませんでした。"
|
||||||
uploadFolder: "既定アップロード先"
|
uploadFolder: "既定アップロード先"
|
||||||
|
watermark: "ウォーターマーク"
|
||||||
|
useWatermark: "ウォーターマークをつける"
|
||||||
|
useWatermarkDescription: "画像にウォーターマークを追加します"
|
||||||
markAsReadAllNotifications: "すべての通知を既読にする"
|
markAsReadAllNotifications: "すべての通知を既読にする"
|
||||||
markAsReadAllUnreadNotes: "すべての投稿を既読にする"
|
markAsReadAllUnreadNotes: "すべての投稿を既読にする"
|
||||||
markAsReadAllTalkMessages: "すべてのチャットを既読にする"
|
markAsReadAllTalkMessages: "すべてのチャットを既読にする"
|
||||||
@ -780,8 +783,11 @@ makeExplorable: "アカウントを見つけやすくする"
|
|||||||
makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らなくなります。"
|
makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らなくなります。"
|
||||||
showGapBetweenNotesInTimeline: "タイムラインのノートを離して表示"
|
showGapBetweenNotesInTimeline: "タイムラインのノートを離して表示"
|
||||||
duplicate: "複製"
|
duplicate: "複製"
|
||||||
left: "左"
|
top: "上"
|
||||||
|
bottom: "下"
|
||||||
center: "中央"
|
center: "中央"
|
||||||
|
left: "左"
|
||||||
|
right: "右"
|
||||||
wide: "広い"
|
wide: "広い"
|
||||||
narrow: "狭い"
|
narrow: "狭い"
|
||||||
reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
|
reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
|
||||||
@ -1106,14 +1112,23 @@ editMemo: "メモを編集"
|
|||||||
reactionsList: "リアクション一覧"
|
reactionsList: "リアクション一覧"
|
||||||
renotesList: "リノート一覧"
|
renotesList: "リノート一覧"
|
||||||
notificationDisplay: "通知の表示"
|
notificationDisplay: "通知の表示"
|
||||||
|
placement: "配置"
|
||||||
leftTop: "左上"
|
leftTop: "左上"
|
||||||
|
centerTop: "中上"
|
||||||
rightTop: "右上"
|
rightTop: "右上"
|
||||||
|
leftCenter: "左中"
|
||||||
|
rightCenter: "右中"
|
||||||
leftBottom: "左下"
|
leftBottom: "左下"
|
||||||
|
centerBottom: "中下"
|
||||||
rightBottom: "右下"
|
rightBottom: "右下"
|
||||||
stackAxis: "スタック方向"
|
stackAxis: "スタック方向"
|
||||||
vertical: "縦"
|
vertical: "縦"
|
||||||
horizontal: "横"
|
horizontal: "横"
|
||||||
position: "位置"
|
position: "位置"
|
||||||
|
repeat: "繰り返し"
|
||||||
|
enlargement: "引き伸ばし"
|
||||||
|
rotate: "回転"
|
||||||
|
opacity: "透明度"
|
||||||
serverRules: "サーバールール"
|
serverRules: "サーバールール"
|
||||||
pleaseConfirmBelowBeforeSignup: "このサーバーに登録するには、以下の内容を確認し同意する必要があります。"
|
pleaseConfirmBelowBeforeSignup: "このサーバーに登録するには、以下の内容を確認し同意する必要があります。"
|
||||||
pleaseAgreeAllToContinue: "続けるには、全ての「同意する」にチェックが入っている必要があります。"
|
pleaseAgreeAllToContinue: "続けるには、全ての「同意する」にチェックが入っている必要があります。"
|
||||||
@ -1826,9 +1841,10 @@ _role:
|
|||||||
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
|
descriptionOfRateLimitFactor: "小さいほど制限が緩和され、大きいほど制限が強化されます。"
|
||||||
canHideAds: "広告の非表示"
|
canHideAds: "広告の非表示"
|
||||||
canSearchNotes: "ノート検索の利用"
|
canSearchNotes: "ノート検索の利用"
|
||||||
|
canUseReaction: "リアクションの利用"
|
||||||
canUseTranslator: "翻訳機能の利用"
|
canUseTranslator: "翻訳機能の利用"
|
||||||
canUseDriveFileInSoundSettings: "サウンド設定でドライブのファイルを利用"
|
canUseDriveFileInSoundSettings: "サウンド設定でドライブのファイルを利用"
|
||||||
canUseReaction: "リアクションの利用"
|
canUseWatermark: "ウォーターマークの利用"
|
||||||
avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
|
avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
|
||||||
mutualLinkSectionLimit: "相互リンクのセクションの最大数"
|
mutualLinkSectionLimit: "相互リンクのセクションの最大数"
|
||||||
mutualLinkLimit: "セクション内の相互リンクの最大数"
|
mutualLinkLimit: "セクション内の相互リンクの最大数"
|
||||||
|
@ -463,6 +463,9 @@ share: "공유"
|
|||||||
notFound: "찾을 수 없습니다"
|
notFound: "찾을 수 없습니다"
|
||||||
notFoundDescription: "지정한 URL에 해당하는 페이지가 존재하지 않습니다."
|
notFoundDescription: "지정한 URL에 해당하는 페이지가 존재하지 않습니다."
|
||||||
uploadFolder: "기본 업로드 위치"
|
uploadFolder: "기본 업로드 위치"
|
||||||
|
watermark: "워터마크"
|
||||||
|
useWatermark: "워터마크 사용"
|
||||||
|
useWatermarkDescription: "이미지에 워터마크를 추가합니다"
|
||||||
markAsReadAllNotifications: "모든 알림을 읽은 상태로 표시"
|
markAsReadAllNotifications: "모든 알림을 읽은 상태로 표시"
|
||||||
markAsReadAllUnreadNotes: "모든 글을 읽은 상태로 표시"
|
markAsReadAllUnreadNotes: "모든 글을 읽은 상태로 표시"
|
||||||
markAsReadAllTalkMessages: "모든 대화를 읽은 상태로 표시"
|
markAsReadAllTalkMessages: "모든 대화를 읽은 상태로 표시"
|
||||||
@ -777,8 +780,11 @@ makeExplorable: "\"발견하기\"에 내 계정 보이기"
|
|||||||
makeExplorableDescription: "비활성화하면 \"발견하기\"에 나의 계정을 표시하지 않습니다."
|
makeExplorableDescription: "비활성화하면 \"발견하기\"에 나의 계정을 표시하지 않습니다."
|
||||||
showGapBetweenNotesInTimeline: "타임라인의 노트 사이를 띄워서 표시"
|
showGapBetweenNotesInTimeline: "타임라인의 노트 사이를 띄워서 표시"
|
||||||
duplicate: "복제"
|
duplicate: "복제"
|
||||||
left: "왼쪽"
|
top: "상단"
|
||||||
|
bottom: "하단"
|
||||||
center: "가운데"
|
center: "가운데"
|
||||||
|
left: "왼쪽"
|
||||||
|
right: "오른쪽"
|
||||||
wide: "넓게"
|
wide: "넓게"
|
||||||
narrow: "좁게"
|
narrow: "좁게"
|
||||||
reloadToApplySetting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
|
reloadToApplySetting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
|
||||||
@ -1103,14 +1109,23 @@ editMemo: "메모 편집"
|
|||||||
reactionsList: "리액션 목록"
|
reactionsList: "리액션 목록"
|
||||||
renotesList: "리노트 목록"
|
renotesList: "리노트 목록"
|
||||||
notificationDisplay: "알림 표시"
|
notificationDisplay: "알림 표시"
|
||||||
|
placement: "위치"
|
||||||
leftTop: "왼쪽 상단"
|
leftTop: "왼쪽 상단"
|
||||||
|
centerTop: "중앙 상단"
|
||||||
rightTop: "오른쪽 상단"
|
rightTop: "오른쪽 상단"
|
||||||
|
leftCenter: "왼쪽 중앙"
|
||||||
|
rightCenter: "오른쪽 중앙"
|
||||||
leftBottom: "왼쪽 하단"
|
leftBottom: "왼쪽 하단"
|
||||||
|
centerBottom: "중앙 하단"
|
||||||
rightBottom: "오른쪽 하단"
|
rightBottom: "오른쪽 하단"
|
||||||
stackAxis: "나열 방향"
|
stackAxis: "나열 방향"
|
||||||
vertical: "세로"
|
vertical: "세로"
|
||||||
horizontal: "가로"
|
horizontal: "가로"
|
||||||
position: "위치"
|
position: "위치"
|
||||||
|
repeat: "반복"
|
||||||
|
enlargement: "확대"
|
||||||
|
rotate: "회전"
|
||||||
|
opacity: "불투명도"
|
||||||
serverRules: "서버 규칙"
|
serverRules: "서버 규칙"
|
||||||
pleaseConfirmBelowBeforeSignup: "이 서버에 가입하기 전에 아래 사항을 확인하여 주십시오."
|
pleaseConfirmBelowBeforeSignup: "이 서버에 가입하기 전에 아래 사항을 확인하여 주십시오."
|
||||||
pleaseAgreeAllToContinue: "계속하시려면 모든 항목에 동의하십시오."
|
pleaseAgreeAllToContinue: "계속하시려면 모든 항목에 동의하십시오."
|
||||||
@ -1805,10 +1820,11 @@ _role:
|
|||||||
descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화됩니다."
|
descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화됩니다."
|
||||||
canHideAds: "광고 숨기기"
|
canHideAds: "광고 숨기기"
|
||||||
canSearchNotes: "노트 검색 이용 가능 여부"
|
canSearchNotes: "노트 검색 이용 가능 여부"
|
||||||
|
canUseReaction: "리액션 사용"
|
||||||
canUseTranslator: "번역 기능의 사용"
|
canUseTranslator: "번역 기능의 사용"
|
||||||
canUseDriveFileInSoundSettings: "사운드 설정에서 드라이브의 파일 사용 가능 여부"
|
canUseDriveFileInSoundSettings: "사운드 설정에서 드라이브의 파일 사용 가능 여부"
|
||||||
|
canUseWatermark: "워터마크 기능의 사용"
|
||||||
avatarDecorationLimit: "아바타 장식의 최대 붙임 개수"
|
avatarDecorationLimit: "아바타 장식의 최대 붙임 개수"
|
||||||
canUseReaction: "리액션 사용"
|
|
||||||
mutualLinkSectionLimit: "서로링크 섹션의 최대 수"
|
mutualLinkSectionLimit: "서로링크 섹션의 최대 수"
|
||||||
mutualLinkLimit: "섹션 내의 서로링크의 최대 수"
|
mutualLinkLimit: "섹션 내의 서로링크의 최대 수"
|
||||||
_condition:
|
_condition:
|
||||||
|
@ -51,9 +51,10 @@ export type RolePolicies = {
|
|||||||
canManageCustomEmojis: boolean;
|
canManageCustomEmojis: boolean;
|
||||||
canManageAvatarDecorations: boolean;
|
canManageAvatarDecorations: boolean;
|
||||||
canSearchNotes: boolean;
|
canSearchNotes: boolean;
|
||||||
|
canUseReaction: boolean;
|
||||||
canUseTranslator: boolean;
|
canUseTranslator: boolean;
|
||||||
canUseDriveFileInSoundSettings: boolean;
|
canUseDriveFileInSoundSettings: boolean;
|
||||||
canUseReaction: boolean;
|
canUseWatermark: boolean;
|
||||||
canHideAds: boolean;
|
canHideAds: boolean;
|
||||||
driveCapacityMb: number;
|
driveCapacityMb: number;
|
||||||
alwaysMarkNsfw: boolean;
|
alwaysMarkNsfw: boolean;
|
||||||
@ -92,9 +93,10 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
|||||||
canManageCustomEmojis: false,
|
canManageCustomEmojis: false,
|
||||||
canManageAvatarDecorations: false,
|
canManageAvatarDecorations: false,
|
||||||
canSearchNotes: false,
|
canSearchNotes: false,
|
||||||
|
canUseReaction: true,
|
||||||
canUseTranslator: true,
|
canUseTranslator: true,
|
||||||
canUseDriveFileInSoundSettings: false,
|
canUseDriveFileInSoundSettings: false,
|
||||||
canUseReaction: true,
|
canUseWatermark: false,
|
||||||
canHideAds: false,
|
canHideAds: false,
|
||||||
driveCapacityMb: 100,
|
driveCapacityMb: 100,
|
||||||
alwaysMarkNsfw: false,
|
alwaysMarkNsfw: false,
|
||||||
@ -406,9 +408,10 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||||||
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
|
canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)),
|
||||||
canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)),
|
canManageAvatarDecorations: calc('canManageAvatarDecorations', vs => vs.some(v => v === true)),
|
||||||
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
|
canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)),
|
||||||
|
canUseReaction: calc('canUseReaction', vs => vs.some(v => v === true)),
|
||||||
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
|
canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)),
|
||||||
canUseDriveFileInSoundSettings: calc('canUseDriveFileInSoundSettings', vs => vs.some(v => v === true)),
|
canUseDriveFileInSoundSettings: calc('canUseDriveFileInSoundSettings', vs => vs.some(v => v === true)),
|
||||||
canUseReaction: calc('canUseReaction', vs => vs.some(v => v === true)),
|
canUseWatermark: calc('canUseWatermark', vs => vs.some(v => v === true)),
|
||||||
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
|
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
|
||||||
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
|
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
|
||||||
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
|
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
|
||||||
|
@ -240,6 +240,10 @@ export const packedRolePoliciesSchema = {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
canUseReaction: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
canUseTranslator: {
|
canUseTranslator: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
@ -248,7 +252,7 @@ export const packedRolePoliciesSchema = {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
canUseReaction: {
|
canUseWatermark: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
@ -396,6 +396,7 @@ import * as ep___reversi_invitations from './endpoints/reversi/invitations.js';
|
|||||||
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
||||||
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
||||||
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
||||||
|
import * as ep___watermark_update from './endpoints/watermark/update.js';
|
||||||
import { GetterService } from './GetterService.js';
|
import { GetterService } from './GetterService.js';
|
||||||
import { ApiLoggerService } from './ApiLoggerService.js';
|
import { ApiLoggerService } from './ApiLoggerService.js';
|
||||||
import type { Provider } from '@nestjs/common';
|
import type { Provider } from '@nestjs/common';
|
||||||
@ -790,6 +791,7 @@ const $reversi_invitations: Provider = { provide: 'ep:reversi/invitations', useC
|
|||||||
const $reversi_showGame: Provider = { provide: 'ep:reversi/show-game', useClass: ep___reversi_showGame.default };
|
const $reversi_showGame: Provider = { provide: 'ep:reversi/show-game', useClass: ep___reversi_showGame.default };
|
||||||
const $reversi_surrender: Provider = { provide: 'ep:reversi/surrender', useClass: ep___reversi_surrender.default };
|
const $reversi_surrender: Provider = { provide: 'ep:reversi/surrender', useClass: ep___reversi_surrender.default };
|
||||||
const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep___reversi_verify.default };
|
const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep___reversi_verify.default };
|
||||||
|
const $watermark_update: Provider = { provide: 'ep:watermark/update', useClass: ep___watermark_update.default };
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -1188,6 +1190,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
|||||||
$reversi_showGame,
|
$reversi_showGame,
|
||||||
$reversi_surrender,
|
$reversi_surrender,
|
||||||
$reversi_verify,
|
$reversi_verify,
|
||||||
|
$watermark_update,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
$admin_meta,
|
$admin_meta,
|
||||||
@ -1578,6 +1581,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
|||||||
$reversi_showGame,
|
$reversi_showGame,
|
||||||
$reversi_surrender,
|
$reversi_surrender,
|
||||||
$reversi_verify,
|
$reversi_verify,
|
||||||
|
$watermark_update,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EndpointsModule {}
|
export class EndpointsModule {}
|
||||||
|
@ -396,6 +396,7 @@ import * as ep___reversi_invitations from './endpoints/reversi/invitations.js';
|
|||||||
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
import * as ep___reversi_showGame from './endpoints/reversi/show-game.js';
|
||||||
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
import * as ep___reversi_surrender from './endpoints/reversi/surrender.js';
|
||||||
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
import * as ep___reversi_verify from './endpoints/reversi/verify.js';
|
||||||
|
import * as ep___watermark_update from './endpoints/watermark/update.js';
|
||||||
|
|
||||||
const eps = [
|
const eps = [
|
||||||
['admin/meta', ep___admin_meta],
|
['admin/meta', ep___admin_meta],
|
||||||
@ -788,6 +789,7 @@ const eps = [
|
|||||||
['reversi/show-game', ep___reversi_showGame],
|
['reversi/show-game', ep___reversi_showGame],
|
||||||
['reversi/surrender', ep___reversi_surrender],
|
['reversi/surrender', ep___reversi_surrender],
|
||||||
['reversi/verify', ep___reversi_verify],
|
['reversi/verify', ep___reversi_verify],
|
||||||
|
['watermark/update', ep___watermark_update],
|
||||||
];
|
];
|
||||||
|
|
||||||
interface IEndpointMetaBase {
|
interface IEndpointMetaBase {
|
||||||
|
@ -86,6 +86,7 @@ export const paramDef = {
|
|||||||
comment: { type: 'string', nullable: true, maxLength: DB_MAX_IMAGE_COMMENT_LENGTH, default: null },
|
comment: { type: 'string', nullable: true, maxLength: DB_MAX_IMAGE_COMMENT_LENGTH, default: null },
|
||||||
isSensitive: { type: 'boolean', default: false },
|
isSensitive: { type: 'boolean', default: false },
|
||||||
force: { type: 'boolean', default: false },
|
force: { type: 'boolean', default: false },
|
||||||
|
watermark: { type: 'boolean' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['account'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireRolePolicy: 'canUpdateContent',
|
||||||
|
|
||||||
|
kind: 'write:account',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
width: { type: 'integer' },
|
||||||
|
height: { type: 'integer' },
|
||||||
|
fit: { type: 'string', enum: ['scale-down', 'contain', 'cover', 'crop', 'pad'] },
|
||||||
|
gravity: { type: 'string', enum: ['auto', 'left', 'right', 'top', 'bottom'] },
|
||||||
|
opacity: { type: 'number' },
|
||||||
|
repeat: { type: 'string', enum: ['true', 'x', 'y'] },
|
||||||
|
top: { type: 'number' },
|
||||||
|
left: { type: 'number' },
|
||||||
|
bottom: { type: 'number' },
|
||||||
|
right: { type: 'number' },
|
||||||
|
background: { type: 'string' },
|
||||||
|
rotate: { type: 'number' },
|
||||||
|
},
|
||||||
|
required: ['fileId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// THIS ENDPOINT IS STUB
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
BIN
packages/frontend/assets/default-watermark.png
Normal file
BIN
packages/frontend/assets/default-watermark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
@ -105,6 +105,7 @@ import XFile from '@/components/MkDrive.file.vue';
|
|||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
|
import { $i } from '@/account.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { uploadFile, uploads } from '@/scripts/upload.js';
|
import { uploadFile, uploads } from '@/scripts/upload.js';
|
||||||
@ -142,6 +143,7 @@ const selectedFolders = ref<Misskey.entities.DriveFolder[]>([]);
|
|||||||
const uploadings = uploads;
|
const uploadings = uploads;
|
||||||
const connection = useStream().useChannel('drive');
|
const connection = useStream().useChannel('drive');
|
||||||
const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい
|
const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい
|
||||||
|
const useWatermark = ref<boolean>(defaultStore.state.useWatermark);
|
||||||
|
|
||||||
// ドロップされようとしているか
|
// ドロップされようとしているか
|
||||||
const draghover = ref(false);
|
const draghover = ref(false);
|
||||||
@ -385,7 +387,7 @@ function onChangeFileInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function upload(file: File, folderToUpload?: Misskey.entities.DriveFolder | null) {
|
function upload(file: File, folderToUpload?: Misskey.entities.DriveFolder | null) {
|
||||||
uploadFile(file, (folderToUpload && typeof folderToUpload === 'object') ? folderToUpload.id : null, undefined, keepOriginal.value).then(res => {
|
uploadFile(file, (folderToUpload && typeof folderToUpload === 'object') ? folderToUpload.id : null, undefined, keepOriginal.value, useWatermark.value).then(res => {
|
||||||
addFile(res, true);
|
addFile(res, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -618,7 +620,12 @@ function getMenu() {
|
|||||||
type: 'switch',
|
type: 'switch',
|
||||||
text: i18n.ts.keepOriginalUploading,
|
text: i18n.ts.keepOriginalUploading,
|
||||||
ref: keepOriginal,
|
ref: keepOriginal,
|
||||||
}, { type: 'divider' }, {
|
}, ...($i?.policies.canUseWatermark ? [{
|
||||||
|
type: 'switch',
|
||||||
|
text: i18n.ts.useWatermark,
|
||||||
|
ref: useWatermark,
|
||||||
|
}] as MenuItem[] : []
|
||||||
|
), { type: 'divider' }, {
|
||||||
text: i18n.ts.addFile,
|
text: i18n.ts.addFile,
|
||||||
type: 'label',
|
type: 'label',
|
||||||
}, {
|
}, {
|
||||||
|
@ -90,9 +90,10 @@ export const ROLE_POLICIES = [
|
|||||||
'canManageCustomEmojis',
|
'canManageCustomEmojis',
|
||||||
'canManageAvatarDecorations',
|
'canManageAvatarDecorations',
|
||||||
'canSearchNotes',
|
'canSearchNotes',
|
||||||
|
'canUseReaction',
|
||||||
'canUseTranslator',
|
'canUseTranslator',
|
||||||
'canUseDriveFileInSoundSettings',
|
'canUseDriveFileInSoundSettings',
|
||||||
'canUseReaction',
|
'canUseWatermark',
|
||||||
'canHideAds',
|
'canHideAds',
|
||||||
'driveCapacityMb',
|
'driveCapacityMb',
|
||||||
'alwaysMarkNsfw',
|
'alwaysMarkNsfw',
|
||||||
|
@ -523,6 +523,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseWatermark, 'canUseWatermark'])">
|
||||||
|
<template #label>{{ i18n.ts._role._options.canUseWatermark }}</template>
|
||||||
|
<template #suffix>
|
||||||
|
<span v-if="role.policies.canUseWatermark.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
||||||
|
<span v-else>{{ role.policies.canUseReaction.value ? i18n.ts.yes : i18n.ts.no }}</span>
|
||||||
|
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canUseWatermark)"></i></span>
|
||||||
|
</template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<MkSwitch v-model="role.policies.canUseWatermark.useDefault" :readonly="readonly">
|
||||||
|
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="role.policies.canUseWatermark.value" :disabled="role.policies.canUseWatermark.useDefault" :readonly="readonly">
|
||||||
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkRange v-model="role.policies.canUseWatermark.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
|
</MkRange>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
|
||||||
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
|
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
|
@ -142,14 +142,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkInput>
|
</MkInput>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])">
|
|
||||||
<template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template>
|
|
||||||
<template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template>
|
|
||||||
<MkSwitch v-model="policies.canManageAvatarDecorations">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])">
|
||||||
<template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template>
|
<template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template>
|
||||||
<template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template>
|
<template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||||
@ -158,6 +150,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])">
|
||||||
|
<template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template>
|
||||||
|
<template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||||
|
<MkSwitch v-model="policies.canManageAvatarDecorations">
|
||||||
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])">
|
||||||
<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
|
<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
|
||||||
<template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template>
|
<template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||||
@ -190,6 +190,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUseWatermark, 'canUseWatermark'])">
|
||||||
|
<template #label>{{ i18n.ts._role._options.canUseWatermark }}</template>
|
||||||
|
<template #suffix>{{ policies.canUseWatermark ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||||
|
<MkSwitch v-model="policies.canUseWatermark">
|
||||||
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])">
|
||||||
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
|
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
|
||||||
<template #suffix>{{ policies.driveCapacityMb }}MB</template>
|
<template #suffix>{{ policies.driveCapacityMb }}MB</template>
|
||||||
|
@ -33,11 +33,36 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<FormSection>
|
<FormSection>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<FormLink @click="chooseUploadFolder()">
|
<FormLink @click="chooseUploadFolder()">
|
||||||
|
<template #icon><i class="ti ti-folder"/></template>
|
||||||
{{ i18n.ts.uploadFolder }}
|
{{ i18n.ts.uploadFolder }}
|
||||||
<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
|
<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
|
||||||
<template #suffixIcon><i class="ti ti-folder"></i></template>
|
|
||||||
</FormLink>
|
</FormLink>
|
||||||
|
<MkFolder v-if="$i?.policies.canUseWatermark">
|
||||||
|
<template #icon><i class="ti ti-ripple"></i></template>
|
||||||
|
<template #label>{{ i18n.ts.watermark }}</template>
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<MkSwitch v-model="useWatermark">
|
||||||
|
<template #label>{{ i18n.ts.useWatermark }}</template>
|
||||||
|
<template #caption>{{ i18n.ts.useWatermarkDescription }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<FormSection>
|
||||||
|
<template #label>{{ i18n.ts.preview }}</template>
|
||||||
|
<div style="display: flex; justify-content: center; align-items: center;">
|
||||||
|
<div style="width: 80%; height: 400px; border: 1px solid #ccc; background-image: url('/client-assets/tutorial/natto_failed.webp'); background-size: cover;">
|
||||||
|
<img src="/client-assets/default-watermark.png" style="width: 100%; height: 100%; object-fit: contain; opacity: 0.5;"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FormSplit>
|
||||||
|
<div class="_buttons">
|
||||||
|
<MkButton primary rounded><i class="ti ti-photo"/>{{ i18n.ts.selectFile }}</MkButton>
|
||||||
|
<MkButton danger rounded><i class="ti ti-trash"></i> {{ i18n.ts.defa }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</FormSplit>
|
||||||
|
</FormSection>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
<FormLink to="/settings/drive/cleaner">
|
<FormLink to="/settings/drive/cleaner">
|
||||||
|
<template #icon><i class="ti ti-file-shredder"/></template>
|
||||||
{{ i18n.ts.drivecleaner }}
|
{{ i18n.ts.drivecleaner }}
|
||||||
</FormLink>
|
</FormLink>
|
||||||
<MkSwitch v-model="keepOriginalUploading">
|
<MkSwitch v-model="keepOriginalUploading">
|
||||||
@ -77,6 +102,9 @@ import MkChart from '@/components/MkChart.vue';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { signinRequired } from '@/account.js';
|
import { signinRequired } from '@/account.js';
|
||||||
|
import MkInfo from "@/components/MkInfo.vue";
|
||||||
|
import MkButton from "@/components/MkButton.vue";
|
||||||
|
import MkFolder from "@/components/MkFolder.vue";
|
||||||
|
|
||||||
const $i = signinRequired();
|
const $i = signinRequired();
|
||||||
|
|
||||||
@ -102,6 +130,9 @@ const meterStyle = computed(() => {
|
|||||||
const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
|
const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
|
||||||
const keepOriginalFilename = computed(defaultStore.makeGetterSetter('keepOriginalFilename'));
|
const keepOriginalFilename = computed(defaultStore.makeGetterSetter('keepOriginalFilename'));
|
||||||
|
|
||||||
|
const useWatermark = computed(defaultStore.makeGetterSetter('useWatermark'));
|
||||||
|
const watermarkConfig = computed(defaultStore.makeGetterSetter('watermarkConfig'));
|
||||||
|
|
||||||
misskeyApi('drive').then(info => {
|
misskeyApi('drive').then(info => {
|
||||||
capacity.value = info.capacity;
|
capacity.value = info.capacity;
|
||||||
usage.value = info.usage;
|
usage.value = info.usage;
|
||||||
|
@ -120,7 +120,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
|
|||||||
'sound_notification',
|
'sound_notification',
|
||||||
'sound_antenna',
|
'sound_antenna',
|
||||||
'sound_channel',
|
'sound_channel',
|
||||||
'mutedReactions',
|
'useWatermark',
|
||||||
];
|
];
|
||||||
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
|
||||||
'lightTheme',
|
'lightTheme',
|
||||||
|
@ -9,17 +9,19 @@ import * as os from '@/os.js';
|
|||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { $i } from '@/account.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { uploadFile } from '@/scripts/upload.js';
|
import { uploadFile } from '@/scripts/upload.js';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
|
|
||||||
export function chooseFileFromPc(multiple: boolean, keepOriginal = false): Promise<Misskey.entities.DriveFile[]> {
|
export function chooseFileFromPc(multiple: boolean, keepOriginal = false, useWatermark = false): Promise<Misskey.entities.DriveFile[]> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.multiple = multiple;
|
input.multiple = multiple;
|
||||||
input.onchange = () => {
|
input.onchange = () => {
|
||||||
if (!input.files) return res([]);
|
if (!input.files) return res([]);
|
||||||
const promises = Array.from(input.files, file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal));
|
const promises = Array.from(input.files, file => uploadFile(file, defaultStore.state.uploadFolder, undefined, keepOriginal, useWatermark));
|
||||||
|
|
||||||
Promise.all(promises).then(driveFiles => {
|
Promise.all(promises).then(driveFiles => {
|
||||||
res(driveFiles);
|
res(driveFiles);
|
||||||
@ -83,6 +85,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
|
|||||||
function select(src: any, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
|
function select(src: any, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
|
const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
|
||||||
|
const useWatermark = ref(defaultStore.state.useWatermark);
|
||||||
|
|
||||||
os.popupMenu([label ? {
|
os.popupMenu([label ? {
|
||||||
text: label,
|
text: label,
|
||||||
@ -91,10 +94,15 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Miss
|
|||||||
type: 'switch',
|
type: 'switch',
|
||||||
text: i18n.ts.keepOriginalUploading,
|
text: i18n.ts.keepOriginalUploading,
|
||||||
ref: keepOriginal,
|
ref: keepOriginal,
|
||||||
}, {
|
}, ...($i?.policies.canUseWatermark ? [{
|
||||||
|
type: 'switch',
|
||||||
|
text: i18n.ts.useWatermark,
|
||||||
|
ref: useWatermark,
|
||||||
|
}] as MenuItem[] : []
|
||||||
|
), {
|
||||||
text: i18n.ts.upload,
|
text: i18n.ts.upload,
|
||||||
icon: 'ti ti-upload',
|
icon: 'ti ti-upload',
|
||||||
action: () => chooseFileFromPc(multiple, keepOriginal.value).then(files => res(files)),
|
action: () => chooseFileFromPc(multiple, keepOriginal.value, useWatermark.value).then(files => res(files)),
|
||||||
}, {
|
}, {
|
||||||
text: i18n.ts.fromDrive,
|
text: i18n.ts.fromDrive,
|
||||||
icon: 'ti ti-cloud',
|
icon: 'ti ti-cloud',
|
||||||
|
@ -34,6 +34,7 @@ export function uploadFile(
|
|||||||
folder?: any,
|
folder?: any,
|
||||||
name?: string,
|
name?: string,
|
||||||
keepOriginal: boolean = defaultStore.state.keepOriginalUploading,
|
keepOriginal: boolean = defaultStore.state.keepOriginalUploading,
|
||||||
|
watermark: boolean = defaultStore.state.useWatermark,
|
||||||
): Promise<Misskey.entities.DriveFile> {
|
): Promise<Misskey.entities.DriveFile> {
|
||||||
if ($i == null) throw new Error('Not logged in');
|
if ($i == null) throw new Error('Not logged in');
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ export function uploadFile(
|
|||||||
formData.append('force', 'true');
|
formData.append('force', 'true');
|
||||||
formData.append('file', resizedImage ?? file);
|
formData.append('file', resizedImage ?? file);
|
||||||
formData.append('name', ctx.name);
|
formData.append('name', ctx.name);
|
||||||
|
if (watermark) formData.append('watermark', 'true');
|
||||||
if (folder) formData.append('folderId', folder);
|
if (folder) formData.append('folderId', folder);
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
@ -507,10 +507,36 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||||||
where: 'device',
|
where: 'device',
|
||||||
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
|
default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
|
||||||
},
|
},
|
||||||
|
|
||||||
mutedReactions: {
|
mutedReactions: {
|
||||||
where: 'account',
|
where: 'account',
|
||||||
default: [] as string[],
|
default: [] as string[],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
useWatermark: {
|
||||||
|
where: 'device',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
watermarkConfig: {
|
||||||
|
where: 'account',
|
||||||
|
default: null as {
|
||||||
|
fileId: string | null;
|
||||||
|
fileUrl: string | null;
|
||||||
|
width: number | null;
|
||||||
|
height: number | null;
|
||||||
|
enlargement: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
|
||||||
|
gravity: 'auto' | 'left' | 'right' | 'top' | 'bottom';
|
||||||
|
opacity: number;
|
||||||
|
repeat: true | false | 'x' | 'y';
|
||||||
|
anchor: 'center' | 'top' | 'left' | 'bottom' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
||||||
|
offsetTop: number | null;
|
||||||
|
offsetLeft: number | null;
|
||||||
|
offsetBottom: number | null;
|
||||||
|
offsetRight: number | null;
|
||||||
|
backgroundColor: string | null;
|
||||||
|
rotate: number | null;
|
||||||
|
} | null,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
@ -1800,6 +1800,7 @@ declare namespace entities {
|
|||||||
ReversiSurrenderRequest,
|
ReversiSurrenderRequest,
|
||||||
ReversiVerifyRequest,
|
ReversiVerifyRequest,
|
||||||
ReversiVerifyResponse,
|
ReversiVerifyResponse,
|
||||||
|
WatermarkUpdateRequest,
|
||||||
Error_2 as Error,
|
Error_2 as Error,
|
||||||
UserLite,
|
UserLite,
|
||||||
UserDetailedNotMeOnly,
|
UserDetailedNotMeOnly,
|
||||||
@ -3292,6 +3293,9 @@ type UsersStatsResponse = operations['users___stats']['responses']['200']['conte
|
|||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
|
type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type WatermarkUpdateRequest = operations['watermark___update']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// Warnings were encountered during analysis:
|
// Warnings were encountered during analysis:
|
||||||
//
|
//
|
||||||
// src/entities.ts:25:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
|
// src/entities.ts:25:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
|
||||||
|
@ -4332,5 +4332,16 @@ declare module '../api.js' {
|
|||||||
params: P,
|
params: P,
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
request<E extends 'watermark/update', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -586,6 +586,7 @@ import type {
|
|||||||
ReversiSurrenderRequest,
|
ReversiSurrenderRequest,
|
||||||
ReversiVerifyRequest,
|
ReversiVerifyRequest,
|
||||||
ReversiVerifyResponse,
|
ReversiVerifyResponse,
|
||||||
|
WatermarkUpdateRequest,
|
||||||
} from './entities.js';
|
} from './entities.js';
|
||||||
|
|
||||||
export type Endpoints = {
|
export type Endpoints = {
|
||||||
@ -979,4 +980,5 @@ export type Endpoints = {
|
|||||||
'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse };
|
'reversi/show-game': { req: ReversiShowGameRequest; res: ReversiShowGameResponse };
|
||||||
'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse };
|
'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse };
|
||||||
'reversi/verify': { req: ReversiVerifyRequest; res: ReversiVerifyResponse };
|
'reversi/verify': { req: ReversiVerifyRequest; res: ReversiVerifyResponse };
|
||||||
|
'watermark/update': { req: WatermarkUpdateRequest; res: EmptyResponse };
|
||||||
}
|
}
|
||||||
|
@ -589,3 +589,4 @@ export type ReversiShowGameResponse = operations['reversi___show-game']['respons
|
|||||||
export type ReversiSurrenderRequest = operations['reversi___surrender']['requestBody']['content']['application/json'];
|
export type ReversiSurrenderRequest = operations['reversi___surrender']['requestBody']['content']['application/json'];
|
||||||
export type ReversiVerifyRequest = operations['reversi___verify']['requestBody']['content']['application/json'];
|
export type ReversiVerifyRequest = operations['reversi___verify']['requestBody']['content']['application/json'];
|
||||||
export type ReversiVerifyResponse = operations['reversi___verify']['responses']['200']['content']['application/json'];
|
export type ReversiVerifyResponse = operations['reversi___verify']['responses']['200']['content']['application/json'];
|
||||||
|
export type WatermarkUpdateRequest = operations['watermark___update']['requestBody']['content']['application/json'];
|
||||||
|
@ -3744,6 +3744,15 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations['reversi___verify'];
|
post: operations['reversi___verify'];
|
||||||
};
|
};
|
||||||
|
'/watermark/update': {
|
||||||
|
/**
|
||||||
|
* watermark/update
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
post: operations['watermark___update'];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type webhooks = Record<string, never>;
|
export type webhooks = Record<string, never>;
|
||||||
@ -4973,9 +4982,10 @@ export type components = {
|
|||||||
canManageCustomEmojis: boolean;
|
canManageCustomEmojis: boolean;
|
||||||
canManageAvatarDecorations: boolean;
|
canManageAvatarDecorations: boolean;
|
||||||
canSearchNotes: boolean;
|
canSearchNotes: boolean;
|
||||||
|
canUseReaction: boolean;
|
||||||
canUseTranslator: boolean;
|
canUseTranslator: boolean;
|
||||||
canUseDriveFileInSoundSettings: boolean;
|
canUseDriveFileInSoundSettings: boolean;
|
||||||
canUseReaction: boolean;
|
canUseWatermark: boolean;
|
||||||
canHideAds: boolean;
|
canHideAds: boolean;
|
||||||
driveCapacityMb: number;
|
driveCapacityMb: number;
|
||||||
alwaysMarkNsfw: boolean;
|
alwaysMarkNsfw: boolean;
|
||||||
@ -15541,6 +15551,7 @@ export type operations = {
|
|||||||
isSensitive?: boolean;
|
isSensitive?: boolean;
|
||||||
/** @default false */
|
/** @default false */
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
|
watermark?: boolean;
|
||||||
/**
|
/**
|
||||||
* Format: binary
|
* Format: binary
|
||||||
* @description The file contents.
|
* @description The file contents.
|
||||||
@ -30286,5 +30297,72 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* watermark/update
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:account*
|
||||||
|
*/
|
||||||
|
watermark___update: {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
fileId: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
/** @enum {string} */
|
||||||
|
fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
|
||||||
|
/** @enum {string} */
|
||||||
|
gravity?: 'auto' | 'left' | 'right' | 'top' | 'bottom';
|
||||||
|
opacity?: number;
|
||||||
|
/** @enum {string} */
|
||||||
|
repeat?: 'true' | 'x' | 'y';
|
||||||
|
top?: number;
|
||||||
|
left?: number;
|
||||||
|
bottom?: number;
|
||||||
|
right?: number;
|
||||||
|
background?: string;
|
||||||
|
rotate?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (without any results) */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user