parent
aaaaf2681a
commit
bceb02d760
15 changed files with 139 additions and 11 deletions
|
@ -96,6 +96,9 @@ common:
|
|||
specified: "ダイレクト"
|
||||
specified-desc: "指定したユーザーにのみ公開"
|
||||
private: "非公開"
|
||||
local-public: "公開(ローカルのみ)"
|
||||
local-home: "ホーム(ローカルのみ)"
|
||||
local-followers: "フォロワー(ローカルのみ)"
|
||||
|
||||
note-placeholders:
|
||||
a: "今どうしてる?"
|
||||
|
@ -471,6 +474,9 @@ common/views/components/visibility-chooser.vue:
|
|||
specified: "ダイレクト"
|
||||
specified-desc: "指定したユーザーにのみ公開"
|
||||
private: "非公開"
|
||||
local-public: "公開(ローカルのみ)"
|
||||
local-home: "ホーム(ローカルのみ)"
|
||||
local-followers: "フォロワー(ローカルのみ)"
|
||||
|
||||
common/views/components/trends.vue:
|
||||
count: "{}人が投稿"
|
||||
|
@ -761,6 +767,7 @@ desktop/views/components/post-form.vue:
|
|||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
recent-tags: "最近"
|
||||
local-only-message: "この投稿はローカルにのみ公開されます"
|
||||
click-to-tagging: "クリックでタグ付け"
|
||||
visibility: "公開範囲"
|
||||
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
|
||||
<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
|
||||
</span>
|
||||
<span class="localOnly" v-if="note.localOnly == true">
|
||||
<template><fa icon="heart"/></template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
@ -115,4 +118,7 @@ export default Vue.extend({
|
|||
> .visibility
|
||||
margin-left 8px
|
||||
|
||||
> .localOnly
|
||||
margin-left 4px
|
||||
|
||||
</style>
|
||||
|
|
|
@ -35,6 +35,24 @@
|
|||
<span>{{ $t('private') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('local-public')" :class="{ active: v == 'local-public' }">
|
||||
<div><fa icon="globe"/></div>
|
||||
<div>
|
||||
<span>{{ $t('local-public') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('local-home')" :class="{ active: v == 'local-home' }">
|
||||
<div><fa icon="home"/></div>
|
||||
<div>
|
||||
<span>{{ $t('local-home') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('local-followers')" :class="{ active: v == 'local-followers' }">
|
||||
<div><fa icon="unlock"/></div>
|
||||
<div>
|
||||
<span>{{ $t('local-followers') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -20,6 +20,15 @@
|
|||
<router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
|
||||
<span>{{ this.$t('reposted-by').substr(this.$t('reposted-by').indexOf('}') + 1) }}</span>
|
||||
<mk-time :time="note.createdAt"/>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'"><fa icon="home"/></template>
|
||||
<template v-if="note.visibility == 'followers'"><fa icon="unlock"/></template>
|
||||
<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
|
||||
<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
|
||||
</span>
|
||||
<span class="localOnly" v-if="note.localOnly == true">
|
||||
<template><fa icon="heart"/></template>
|
||||
</span>
|
||||
</div>
|
||||
<article>
|
||||
<mk-avatar class="avatar" :user="appearNote.user"/>
|
||||
|
@ -199,9 +208,6 @@ export default Vue.extend({
|
|||
> span
|
||||
flex-shrink 0
|
||||
|
||||
&:last-of-type
|
||||
margin-right 8px
|
||||
|
||||
.name
|
||||
overflow hidden
|
||||
flex-shrink 1
|
||||
|
@ -215,6 +221,18 @@ export default Vue.extend({
|
|||
flex-shrink 0
|
||||
font-size 0.9em
|
||||
|
||||
> .visibility
|
||||
margin-left 8px
|
||||
|
||||
[data-icon]
|
||||
margin-right 0
|
||||
|
||||
> .localOnly
|
||||
margin-left 4px
|
||||
|
||||
[data-icon]
|
||||
margin-right 0
|
||||
|
||||
& + article
|
||||
padding-top 8px
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<b>{{ $t('recent-tags') }}:</b>
|
||||
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
||||
</div>
|
||||
<div class="local-only" v-if="this.localOnly == true">{{ $t('local-only-message') }}</div>
|
||||
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
||||
<div class="textarea">
|
||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||
|
@ -112,6 +113,7 @@ export default Vue.extend({
|
|||
geo: null,
|
||||
visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
|
||||
visibleUsers: [],
|
||||
localOnly: false,
|
||||
autocomplete: null,
|
||||
draghover: false,
|
||||
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
||||
|
@ -363,7 +365,14 @@ export default Vue.extend({
|
|||
source: this.$refs.visibilityButton
|
||||
});
|
||||
w.$once('chosen', v => {
|
||||
const m = v.match(/^local-(.+)/);
|
||||
if (m) {
|
||||
this.localOnly = true;
|
||||
this.visibility = m[1];
|
||||
} else {
|
||||
this.localOnly = false;
|
||||
this.visibility = v;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -407,6 +416,7 @@ export default Vue.extend({
|
|||
cw: this.useCw ? this.cw || '' : undefined,
|
||||
visibility: this.visibility,
|
||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||
localOnly: this.localOnly,
|
||||
geo: this.geo ? {
|
||||
coordinates: [this.geo.longitude, this.geo.latitude],
|
||||
altitude: this.geo.altitude,
|
||||
|
@ -640,6 +650,10 @@ export default Vue.extend({
|
|||
margin-right 8px
|
||||
white-space nowrap
|
||||
|
||||
> .local-only
|
||||
margin 0 0 8px 0
|
||||
color var(--primary)
|
||||
|
||||
> .mk-uploader
|
||||
margin 8px 0 0 0
|
||||
padding 8px
|
||||
|
|
|
@ -16,6 +16,15 @@
|
|||
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
|
||||
<span>{{ this.$t('reposted-by').substr(this.$t('reposted-by').indexOf('}') + 1) }}</span>
|
||||
<mk-time :time="note.createdAt"/>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'"><fa icon="home"/></template>
|
||||
<template v-if="note.visibility == 'followers'"><fa icon="unlock"/></template>
|
||||
<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
|
||||
<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
|
||||
</span>
|
||||
<span class="localOnly" v-if="note.localOnly == true">
|
||||
<template><fa icon="heart"/></template>
|
||||
</span>
|
||||
</div>
|
||||
<article>
|
||||
<mk-avatar class="avatar" :user="appearNote.user" v-if="$store.state.device.postStyle != 'smart'"/>
|
||||
|
@ -163,9 +172,6 @@ export default Vue.extend({
|
|||
> span
|
||||
flex-shrink 0
|
||||
|
||||
&:last-of-type
|
||||
margin-right 8px
|
||||
|
||||
.name
|
||||
overflow hidden
|
||||
flex-shrink 1
|
||||
|
@ -179,6 +185,18 @@ export default Vue.extend({
|
|||
flex-shrink 0
|
||||
font-size 0.9em
|
||||
|
||||
> .visibility
|
||||
margin-left 8px
|
||||
|
||||
[data-icon]
|
||||
margin-right 0
|
||||
|
||||
> .localOnly
|
||||
margin-left 4px
|
||||
|
||||
[data-icon]
|
||||
margin-right 0
|
||||
|
||||
& + article
|
||||
padding-top 8px
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ export default Vue.extend({
|
|||
geo: null,
|
||||
visibility: this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility,
|
||||
visibleUsers: [],
|
||||
localOnly: false,
|
||||
useCw: false,
|
||||
cw: null,
|
||||
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
||||
|
@ -274,7 +275,14 @@ export default Vue.extend({
|
|||
compact: true
|
||||
});
|
||||
w.$once('chosen', v => {
|
||||
const m = v.match(/^local-(.+)/);
|
||||
if (m) {
|
||||
this.localOnly = true;
|
||||
this.visibility = m[1];
|
||||
} else {
|
||||
this.localOnly = false;
|
||||
this.visibility = v;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -320,6 +328,7 @@ export default Vue.extend({
|
|||
} : null,
|
||||
visibility: this.visibility,
|
||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||
localOnly: this.localOnly,
|
||||
viaMobile: viaMobile
|
||||
}).then(data => {
|
||||
this.$emit('posted');
|
||||
|
|
|
@ -26,6 +26,13 @@ props:
|
|||
ja-JP: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
||||
en-US: "Whether this note sent via a mobile device"
|
||||
|
||||
localOnly:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "ローカルのみに公開する投稿か否か"
|
||||
en-US: "Whether this note is no federation"
|
||||
|
||||
text:
|
||||
type: "string"
|
||||
optional: true
|
||||
|
|
|
@ -50,6 +50,7 @@ export type INote = {
|
|||
userId: mongo.ObjectID;
|
||||
appId: mongo.ObjectID;
|
||||
viaMobile: boolean;
|
||||
localOnly: boolean;
|
||||
renoteCount: number;
|
||||
repliesCount: number;
|
||||
reactionCounts: any;
|
||||
|
|
|
@ -6,6 +6,8 @@ export function createHttpJob(data: any) {
|
|||
}
|
||||
|
||||
export function deliver(user: ILocalUser, content: any, to: any) {
|
||||
if (content == null) return;
|
||||
|
||||
createHttpJob({
|
||||
type: 'deliver',
|
||||
user,
|
||||
|
|
|
@ -116,6 +116,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
|
|||
cw: note.summary,
|
||||
text: text,
|
||||
viaMobile: false,
|
||||
localOnly: false,
|
||||
geo: undefined,
|
||||
visibility,
|
||||
visibleUsers,
|
||||
|
|
|
@ -66,7 +66,8 @@ router.get('/notes/:note', async (ctx, next) => {
|
|||
|
||||
const note = await Note.findOne({
|
||||
_id: new mongo.ObjectID(ctx.params.note),
|
||||
visibility: { $in: ['public', 'home'] }
|
||||
visibility: { $in: ['public', 'home'] },
|
||||
localOnly: { $ne: true }
|
||||
});
|
||||
|
||||
if (note === null) {
|
||||
|
@ -83,7 +84,8 @@ router.get('/notes/:note', async (ctx, next) => {
|
|||
router.get('/notes/:note/activity', async ctx => {
|
||||
const note = await Note.findOne({
|
||||
_id: new mongo.ObjectID(ctx.params.note),
|
||||
visibility: { $in: ['public', 'home'] }
|
||||
visibility: { $in: ['public', 'home'] },
|
||||
localOnly: { $ne: true }
|
||||
});
|
||||
|
||||
if (note === null) {
|
||||
|
|
|
@ -55,7 +55,8 @@ export default async (ctx: Router.IRouterContext) => {
|
|||
|
||||
const query = {
|
||||
userId: user._id,
|
||||
visibility: { $in: ['public', 'home'] }
|
||||
visibility: { $in: ['public', 'home'] },
|
||||
localOnly: { $ne: true }
|
||||
} as any;
|
||||
|
||||
if (sinceId) {
|
||||
|
|
|
@ -74,6 +74,14 @@ export const meta = {
|
|||
}
|
||||
},
|
||||
|
||||
localOnly: {
|
||||
validator: $.bool.optional,
|
||||
default: false,
|
||||
desc: {
|
||||
'ja-JP': 'ローカルのみに投稿か否か。'
|
||||
}
|
||||
},
|
||||
|
||||
geo: {
|
||||
validator: $.obj({
|
||||
coordinates: $.arr().length(2)
|
||||
|
@ -226,6 +234,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
|
|||
cw: ps.cw,
|
||||
app,
|
||||
viaMobile: ps.viaMobile,
|
||||
localOnly: ps.localOnly,
|
||||
visibility: ps.visibility,
|
||||
visibleUsers,
|
||||
geo: ps.geo
|
||||
|
|
|
@ -95,6 +95,7 @@ type Option = {
|
|||
geo?: any;
|
||||
poll?: any;
|
||||
viaMobile?: boolean;
|
||||
localOnly?: boolean;
|
||||
cw?: string;
|
||||
visibility?: string;
|
||||
visibleUsers?: IUser[];
|
||||
|
@ -109,6 +110,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||
if (data.createdAt == null) data.createdAt = new Date();
|
||||
if (data.visibility == null) data.visibility = 'public';
|
||||
if (data.viaMobile == null) data.viaMobile = false;
|
||||
if (data.localOnly == null) data.localOnly = false;
|
||||
|
||||
if (data.visibleUsers) {
|
||||
data.visibleUsers = erase(null, data.visibleUsers);
|
||||
|
@ -139,6 +141,16 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||
return rej('Renote target is private of others');
|
||||
}
|
||||
|
||||
// ローカルのみをRenoteしたらローカルのみにする
|
||||
if (data.renote && data.renote.localOnly) {
|
||||
data.localOnly = true;
|
||||
}
|
||||
|
||||
// ローカルのみにリプライしたらローカルのみにする
|
||||
if (data.reply && data.reply.localOnly) {
|
||||
data.localOnly = true;
|
||||
}
|
||||
|
||||
if (data.text) {
|
||||
data.text = data.text.trim();
|
||||
}
|
||||
|
@ -308,6 +320,8 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||
});
|
||||
|
||||
async function renderActivity(data: Option, note: INote) {
|
||||
if (data.localOnly) return null;
|
||||
|
||||
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length == 0)
|
||||
? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote._id}`, note)
|
||||
: renderCreate(await renderNote(note, false), note);
|
||||
|
@ -389,6 +403,7 @@ async function insertNote(user: IUser, data: Option, tags: string[], emojis: str
|
|||
emojis,
|
||||
userId: user._id,
|
||||
viaMobile: data.viaMobile,
|
||||
localOnly: data.localOnly,
|
||||
geo: data.geo || null,
|
||||
appId: data.app ? data.app._id : null,
|
||||
visibility: data.visibility,
|
||||
|
|
Loading…
Reference in a new issue