mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-13 07:49:29 +00:00
Compare commits
33 Commits
thread-mod
...
fix-async
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35be02f21d | ||
|
|
90e568413b | ||
|
|
ef0b7d1e76 | ||
|
|
65986b6f0b | ||
|
|
2dc4fbbd1a | ||
|
|
f839ac694c | ||
|
|
dbda87c31f | ||
|
|
722b3f567f | ||
|
|
e4a241abef | ||
|
|
93555182c3 | ||
|
|
0eff42d688 | ||
|
|
f7c4d4464b | ||
|
|
70c99a9f34 | ||
|
|
c2e1bfd9ae | ||
|
|
1d92b90be9 | ||
|
|
da809f9eec | ||
|
|
c4d36d024c | ||
|
|
5083311d64 | ||
|
|
2af307bce4 | ||
|
|
bcbdd4f88d | ||
|
|
9e97fbf0af | ||
|
|
b5874c1428 | ||
|
|
61ef8d643e | ||
|
|
9f29fd31ba | ||
|
|
53caab0c0b | ||
|
|
b75a1ce326 | ||
|
|
d442cfa65c | ||
|
|
f5a4201ad8 | ||
|
|
a251c42192 | ||
|
|
2ec9a75a1d | ||
|
|
fa92e88fb2 | ||
|
|
da98c33161 | ||
|
|
2eed4ace11 |
@@ -1,22 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FollowsController < Api::BaseController
|
||||
include SignatureVerification
|
||||
|
||||
def show
|
||||
render json: follow_request,
|
||||
serializer: ActivityPub::FollowSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def follow_request
|
||||
FollowRequest.includes(:account).references(:account).find_by!(
|
||||
id: params.require(:id),
|
||||
accounts: { domain: nil, username: params.require(:account_username) },
|
||||
target_account: signed_request_account
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -31,7 +31,7 @@ class ApplicationController < ActionController::Base
|
||||
private
|
||||
|
||||
def https_enabled?
|
||||
Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'
|
||||
Rails.env.production?
|
||||
end
|
||||
|
||||
def store_current_location
|
||||
@@ -192,6 +192,7 @@ class ApplicationController < ActionController::Base
|
||||
format.any { head code }
|
||||
format.html do
|
||||
set_locale
|
||||
use_pack 'error'
|
||||
render "errors/#{code}", layout: 'error', status: code
|
||||
end
|
||||
end
|
||||
@@ -199,15 +200,15 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
def render_cached_json(cache_key, **options)
|
||||
options[:expires_in] ||= 3.minutes
|
||||
options[:public] ||= true
|
||||
cache_key = cache_key.join(':') if cache_key.is_a?(Enumerable)
|
||||
cache_public = options.key?(:public) ? options.delete(:public) : true
|
||||
content_type = options.delete(:content_type) || 'application/json'
|
||||
|
||||
data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do
|
||||
yield.to_json
|
||||
end
|
||||
|
||||
expires_in options[:expires_in], public: options[:public]
|
||||
expires_in options[:expires_in], public: cache_public
|
||||
render json: data, content_type: content_type
|
||||
end
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ module JsonLdHelper
|
||||
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
|
||||
end
|
||||
|
||||
def unsupported_uri_scheme?(uri)
|
||||
!uri.start_with?('http://', 'https://')
|
||||
end
|
||||
|
||||
def canonicalize(json)
|
||||
graph = RDF::Graph.new << JSON::LD::API.toRdf(json)
|
||||
graph.dump(:normalize)
|
||||
|
||||
@@ -128,6 +128,11 @@ const handlers = {
|
||||
return;
|
||||
}
|
||||
|
||||
// We submit the status on control/meta + enter.
|
||||
if (onSubmit && e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
onSubmit();
|
||||
}
|
||||
|
||||
// Switches over the pressed key.
|
||||
switch(e.key) {
|
||||
|
||||
@@ -157,11 +162,6 @@ const handlers = {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We submit the status on control/meta + enter.
|
||||
if (onSubmit && e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
onSubmit();
|
||||
}
|
||||
},
|
||||
|
||||
// When the escape key is released, we either close the suggestions
|
||||
|
||||
@@ -45,10 +45,10 @@ const handlers = {
|
||||
const {
|
||||
onClear,
|
||||
submitted,
|
||||
value: { length },
|
||||
value,
|
||||
} = this.props;
|
||||
e.preventDefault(); // Prevents focus change ??
|
||||
if (onClear && (submitted || length)) {
|
||||
if (onClear && (submitted || value && value.length)) {
|
||||
onClear();
|
||||
}
|
||||
},
|
||||
@@ -100,7 +100,8 @@ export default class DrawerSearch extends React.PureComponent {
|
||||
value,
|
||||
} = this.props;
|
||||
const { expanded } = this.state;
|
||||
const computedClass = classNames('drawer--search', { active: value.length || submitted });
|
||||
const active = value && value.length || submitted;
|
||||
const computedClass = classNames('drawer--search', { active });
|
||||
|
||||
return (
|
||||
<div className={computedClass}>
|
||||
@@ -126,11 +127,11 @@ export default class DrawerSearch extends React.PureComponent {
|
||||
tabIndex='0'
|
||||
>
|
||||
<Icon icon='search' />
|
||||
<Icon icon='fa-times-circle' />
|
||||
<Icon icon='times-circle' />
|
||||
</div>
|
||||
<Overlay
|
||||
placement='bottom'
|
||||
show={expanded && !(value || '').length && !submitted}
|
||||
show={expanded && !active}
|
||||
target={this}
|
||||
><DrawerSearchPopout /></Overlay>
|
||||
</div>
|
||||
|
||||
@@ -42,56 +42,61 @@ export default function DrawerSearchPopout ({ style }) {
|
||||
|
||||
// The result.
|
||||
return (
|
||||
<Motion
|
||||
defaultStyle={{
|
||||
opacity: 0,
|
||||
scaleX: 0.85,
|
||||
scaleY: 0.75,
|
||||
}}
|
||||
<div
|
||||
className='drawer--search--popout'
|
||||
style={{
|
||||
opacity: motionSpring,
|
||||
scaleX: motionSpring,
|
||||
scaleY: motionSpring,
|
||||
...style,
|
||||
position: 'absolute',
|
||||
width: 285,
|
||||
}}
|
||||
>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div
|
||||
className='drawer--search--popout'
|
||||
style={{
|
||||
...style,
|
||||
position: 'absolute',
|
||||
width: 285,
|
||||
opacity: opacity,
|
||||
transform: `scale(${scaleX}, ${scaleY})`,
|
||||
}}
|
||||
>
|
||||
<h4><FormattedMessage {...messages.format} /></h4>
|
||||
<ul>
|
||||
<li>
|
||||
<em>#example</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.hashtag} />
|
||||
</li>
|
||||
<li>
|
||||
<em>@username@domain</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.user} />
|
||||
</li>
|
||||
<li>
|
||||
<em>URL</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.user} />
|
||||
</li>
|
||||
<li>
|
||||
<em>URL</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.status} />
|
||||
</li>
|
||||
</ul>
|
||||
<FormattedMessage {...messages.text} />
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
<Motion
|
||||
defaultStyle={{
|
||||
opacity: 0,
|
||||
scaleX: 0.85,
|
||||
scaleY: 0.75,
|
||||
}}
|
||||
style={{
|
||||
opacity: motionSpring,
|
||||
scaleX: motionSpring,
|
||||
scaleY: motionSpring,
|
||||
}}
|
||||
>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div
|
||||
style={{
|
||||
opacity: opacity,
|
||||
transform: `scale(${scaleX}, ${scaleY})`,
|
||||
}}
|
||||
>
|
||||
<h4><FormattedMessage {...messages.format} /></h4>
|
||||
<ul>
|
||||
<li>
|
||||
<em>#example</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.hashtag} />
|
||||
</li>
|
||||
<li>
|
||||
<em>@username@domain</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.user} />
|
||||
</li>
|
||||
<li>
|
||||
<em>URL</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.user} />
|
||||
</li>
|
||||
<li>
|
||||
<em>URL</em>
|
||||
{' '}
|
||||
<FormattedMessage {...messages.status} />
|
||||
</li>
|
||||
</ul>
|
||||
<FormattedMessage {...messages.text} />
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,11 @@ const messages = {
|
||||
|
||||
'advanced_options.local-only.short': 'ローカル限定',
|
||||
'advanced_options.local-only.long': '他のインスタンスには投稿されません',
|
||||
'advanced_options.local-only.tooltip': 'この投稿はローカル限定投稿です',
|
||||
'advanced_options.icon_title': '高度な設定',
|
||||
'advanced_options.threaded_mode.short': 'スレッドモード',
|
||||
'advanced_options.threaded_mode.long': '投稿時に自動的に返信するように設定します',
|
||||
'advanced_options.threaded_mode.tooltip': 'スレッドモードを有効にする',
|
||||
};
|
||||
|
||||
export default Object.assign({}, inherited, messages);
|
||||
|
||||
@@ -298,7 +298,7 @@ export default function compose(state = initialState, action) {
|
||||
case COMPOSE_UPLOAD_CHANGE_REQUEST:
|
||||
return state.set('is_submitting', true);
|
||||
case COMPOSE_SUBMIT_SUCCESS:
|
||||
return action.status && state.get('advanced_options', 'threaded_mode') ? continueThread(state, action.status) : clearAll(state);
|
||||
return action.status && state.getIn(['advanced_options', 'threaded_mode']) ? continueThread(state, action.status) : clearAll(state);
|
||||
case COMPOSE_SUBMIT_FAIL:
|
||||
case COMPOSE_UPLOAD_CHANGE_FAIL:
|
||||
return state.set('is_submitting', false);
|
||||
|
||||
@@ -114,19 +114,27 @@
|
||||
}
|
||||
|
||||
& > .icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: $ui-secondary-color;
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
z-index: 2;
|
||||
|
||||
.fa {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: $ui-secondary-color;
|
||||
font-size: 18px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
transition: all 100ms linear;
|
||||
}
|
||||
|
||||
@@ -136,14 +144,15 @@
|
||||
}
|
||||
|
||||
.fa-times-circle {
|
||||
top: 11px;
|
||||
transform: rotate(-90deg);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover { color: $primary-text-color }
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
&.active {
|
||||
& > .icon {
|
||||
.fa-search {
|
||||
opacity: 0;
|
||||
transform: rotate(90deg);
|
||||
@@ -158,6 +167,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
.drawer--search--popout {
|
||||
box-sizing: border-box;
|
||||
margin-top: 10px;
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px 14px 14px;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
color: $ui-primary-color;
|
||||
background: $simple-background-color;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 10px;
|
||||
color: $ui-primary-color;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
ul { margin-bottom: 10px }
|
||||
li { padding: 4px 0 }
|
||||
|
||||
em {
|
||||
color: $ui-base-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer--account {
|
||||
padding: 10px;
|
||||
color: $ui-primary-color;
|
||||
|
||||
@@ -1568,6 +1568,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
.drawer__pager {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.drawer__inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: lighten($ui-base-color, 13%) url('~images/wave-drawer.png') no-repeat bottom / 100% auto;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&.darker {
|
||||
background: $ui-base-color;
|
||||
}
|
||||
|
||||
> .mastodon {
|
||||
background: url('~images/mastodon-ui.png') no-repeat left bottom / contain;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.pseudo-drawer {
|
||||
background: lighten($ui-base-color, 13%);
|
||||
font-size: 13px;
|
||||
@@ -2781,6 +2814,112 @@
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.search {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search__input {
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
padding-right: 30px;
|
||||
font-family: inherit;
|
||||
background: $ui-base-color;
|
||||
color: $ui-primary-color;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.search__icon {
|
||||
.fa {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 2;
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
transition: all 100ms linear;
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: $ui-secondary-color;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
|
||||
&.active {
|
||||
pointer-events: auto;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-search {
|
||||
transform: rotate(90deg);
|
||||
|
||||
&.active {
|
||||
pointer-events: none;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.fa-times-circle {
|
||||
top: 11px;
|
||||
transform: rotate(0deg);
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-results__header {
|
||||
color: $ui-base-lighter-color;
|
||||
background: lighten($ui-base-color, 2%);
|
||||
border-bottom: 1px solid darken($ui-base-color, 4%);
|
||||
padding: 15px 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.search-results__hashtag {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
color: $ui-secondary-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: lighten($ui-secondary-color, 4%);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-root {
|
||||
transition: opacity 0.3s linear;
|
||||
will-change: opacity;
|
||||
@@ -3918,37 +4057,6 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.search-popout {
|
||||
background: $simple-background-color;
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
padding-bottom: 14px;
|
||||
margin-top: 10px;
|
||||
color: $ui-primary-color;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: $ui-primary-color;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: 500;
|
||||
color: $ui-base-color;
|
||||
}
|
||||
}
|
||||
|
||||
noscript {
|
||||
text-align: center;
|
||||
|
||||
|
||||
@@ -27,15 +27,15 @@ export function HashtagTimeline () {
|
||||
}
|
||||
|
||||
export function ListTimeline () {
|
||||
return import(/* webpackChunkName: "features/list_timeline" */'flavours/glitch/features/list_timeline');
|
||||
return import(/* webpackChunkName: "flavours/glitch/async/list_timeline" */'flavours/glitch/features/list_timeline');
|
||||
}
|
||||
|
||||
export function Lists () {
|
||||
return import(/* webpackChunkName: "features/lists" */'flavours/glitch/features/lists');
|
||||
return import(/* webpackChunkName: "flavours/glitch/async/lists" */'flavours/glitch/features/lists');
|
||||
}
|
||||
|
||||
export function ListEditor () {
|
||||
return import(/* webpackChunkName: "features/list_editor" */'flavours/glitch/features/list_editor');
|
||||
return import(/* webpackChunkName: "flavours/glitch/async/list_editor" */'flavours/glitch/features/list_editor');
|
||||
}
|
||||
|
||||
export function DirectTimeline() {
|
||||
@@ -51,7 +51,7 @@ export function GettingStarted () {
|
||||
}
|
||||
|
||||
export function KeyboardShortcuts () {
|
||||
return import(/* webpackChunkName: "features/keyboard_shortcuts" */'flavours/glitch/features/keyboard_shortcuts');
|
||||
return import(/* webpackChunkName: "flavours/glitch/async/keyboard_shortcuts" */'flavours/glitch/features/keyboard_shortcuts');
|
||||
}
|
||||
|
||||
export function PinnedStatuses () {
|
||||
|
||||
@@ -18,6 +18,6 @@ export const boostModal = getMeta('boost_modal');
|
||||
export const favouriteModal = getMeta('favourite_modal');
|
||||
export const deleteModal = getMeta('delete_modal');
|
||||
export const me = getMeta('me');
|
||||
export const maxChars = getMeta('max_toot_chars') || 500;
|
||||
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
|
||||
|
||||
export default initialState;
|
||||
|
||||
@@ -6,7 +6,7 @@ en:
|
||||
skins:
|
||||
vanilla:
|
||||
default: Default
|
||||
en:
|
||||
pl:
|
||||
flavours:
|
||||
vanilla:
|
||||
description: Motyw używany przez instancje czystego Mastodona. Może nie obsługiwać wszystkich funkcji GlitchSoc.
|
||||
|
||||
@@ -94,7 +94,7 @@ export default class Compose extends React.PureComponent {
|
||||
<div className='drawer__inner' onFocus={this.onFocus}>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
<ComposeFormContainer />
|
||||
<div className='mastodon' />
|
||||
{multiColumn && <div className='mastodon' />}
|
||||
</div>
|
||||
|
||||
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
"account.unmute_notifications": "@{name}의 알림 뮤트 해제",
|
||||
"account.view_full_profile": "전체 프로필 보기",
|
||||
"boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
|
||||
"bundle_column_error.retry": "다시 시도",
|
||||
"bundle_column_error.title": "네트워크 에러",
|
||||
"bundle_modal_error.close": "닫기",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.message": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
|
||||
"bundle_modal_error.retry": "다시 시도",
|
||||
"column.blocks": "차단 중인 사용자",
|
||||
"column.community": "로컬 타임라인",
|
||||
@@ -50,7 +50,7 @@
|
||||
"column_header.unpin": "고정 해제",
|
||||
"column_subheading.navigation": "내비게이션",
|
||||
"column_subheading.settings": "설정",
|
||||
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
|
||||
"compose_form.hashtag_warning": "이 툿은 어떤 해시태그로도 검색 되지 않습니다. 전체공개로 게시 된 툿만이 해시태그로 검색 될 수 있습니다.",
|
||||
"compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.",
|
||||
"compose_form.lock_disclaimer.lock": "비공개",
|
||||
"compose_form.placeholder": "지금 무엇을 하고 있나요?",
|
||||
@@ -135,7 +135,7 @@
|
||||
"lists.new.create": "리스트 추가",
|
||||
"lists.new.title_placeholder": "새 리스트의 이름",
|
||||
"lists.search": "팔로우 중인 사람들 중에서 찾기",
|
||||
"lists.subheading": "Your lists",
|
||||
"lists.subheading": "당신의 리스트",
|
||||
"loading_indicator.label": "불러오는 중...",
|
||||
"media_gallery.toggle_visible": "표시 전환",
|
||||
"missing_indicator.label": "찾을 수 없습니다",
|
||||
@@ -178,7 +178,7 @@
|
||||
"onboarding.page_one.welcome": "Mastodon에 어서 오세요!",
|
||||
"onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.",
|
||||
"onboarding.page_six.almost_done": "이상입니다.",
|
||||
"onboarding.page_six.appetoot": "Bon Appetoot!",
|
||||
"onboarding.page_six.appetoot": "본 아페툿!",
|
||||
"onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.",
|
||||
"onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.",
|
||||
"onboarding.page_six.guidelines": "커뮤니티 가이드라인",
|
||||
@@ -213,7 +213,7 @@
|
||||
"search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다",
|
||||
"search_popout.tips.user": "유저",
|
||||
"search_results.total": "{count, number}건의 결과",
|
||||
"standalone.public_title": "A look inside...",
|
||||
"standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
|
||||
"status.block": "@{name} 차단",
|
||||
"status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
|
||||
"status.delete": "삭제",
|
||||
@@ -247,7 +247,7 @@
|
||||
"ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
|
||||
"upload_area.title": "드래그 & 드롭으로 업로드",
|
||||
"upload_button.label": "미디어 추가",
|
||||
"upload_form.description": "Describe for the visually impaired",
|
||||
"upload_form.description": "시각장애인을 위한 설명",
|
||||
"upload_form.undo": "재시도",
|
||||
"upload_progress.label": "업로드 중...",
|
||||
"video.close": "동영상 닫기",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"column_header.unpin": "取消固定",
|
||||
"column_subheading.navigation": "导航",
|
||||
"column_subheading.settings": "设置",
|
||||
"compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
|
||||
"compose_form.hashtag_warning": "这条嘟文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的嘟文才能通过话题标签进行搜索。",
|
||||
"compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。",
|
||||
"compose_form.lock_disclaimer.lock": "开启保护",
|
||||
"compose_form.placeholder": "在想啥?",
|
||||
@@ -214,7 +214,7 @@
|
||||
"search_popout.tips.user": "用户",
|
||||
"search_results.total": "共 {count, number} 个结果",
|
||||
"standalone.public_title": "大家都在干啥?",
|
||||
"status.block": "Block @{name}",
|
||||
"status.block": "屏蔽 @{name}",
|
||||
"status.cannot_reblog": "无法转嘟这条嘟文",
|
||||
"status.delete": "删除",
|
||||
"status.embed": "嵌入",
|
||||
@@ -223,7 +223,7 @@
|
||||
"status.media_hidden": "隐藏媒体内容",
|
||||
"status.mention": "提及 @{name}",
|
||||
"status.more": "更多",
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute": "隐藏 @{name}",
|
||||
"status.mute_conversation": "隐藏此对话",
|
||||
"status.open": "展开嘟文",
|
||||
"status.pin": "在个人资料页面置顶",
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
|
||||
class ActivityPub::Activity::Accept < ActivityPub::Activity
|
||||
def perform
|
||||
if @object.respond_to?(:[]) &&
|
||||
@object['type'] == 'Follow' && @object['actor'].present?
|
||||
accept_follow_from @object['actor']
|
||||
else
|
||||
accept_follow_object @object
|
||||
case @object['type']
|
||||
when 'Follow'
|
||||
accept_follow
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def accept_follow_from(actor)
|
||||
target_account = account_from_uri(value_or_id(actor))
|
||||
def accept_follow
|
||||
target_account = account_from_uri(target_uri)
|
||||
|
||||
return if target_account.nil? || !target_account.local?
|
||||
|
||||
@@ -21,8 +19,7 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
|
||||
follow_request&.authorize!
|
||||
end
|
||||
|
||||
def accept_follow_object(object)
|
||||
follow_request = ActivityPub::TagManager.instance.uri_to_resource(value_or_id(object), FollowRequest)
|
||||
follow_request&.authorize!
|
||||
def target_uri
|
||||
@target_uri ||= value_or_id(@object['actor'])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
SUPPORTED_TYPES = %w(Article Note).freeze
|
||||
CONVERTED_TYPES = %w(Image Video).freeze
|
||||
SUPPORTED_TYPES = %w(Note).freeze
|
||||
CONVERTED_TYPES = %w(Image Video Article).freeze
|
||||
|
||||
def perform
|
||||
return if delete_arrived_first?(object_uri) || unsupported_object_type?
|
||||
return if delete_arrived_first?(object_uri) || unsupported_object_type? || invalid_origin?(@object['id'])
|
||||
|
||||
RedisLock.acquire(lock_options) do |lock|
|
||||
if lock.acquired?
|
||||
@@ -213,7 +213,14 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
|
||||
def object_url
|
||||
return if @object['url'].blank?
|
||||
url_to_href(@object['url'], 'text/html')
|
||||
|
||||
url_candidate = url_to_href(@object['url'], 'text/html')
|
||||
|
||||
if invalid_origin?(url_candidate)
|
||||
nil
|
||||
else
|
||||
url_candidate
|
||||
end
|
||||
end
|
||||
|
||||
def content_language_map?
|
||||
@@ -245,6 +252,15 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
@skip_download ||= DomainBlock.find_by(domain: @account.domain)&.reject_media?
|
||||
end
|
||||
|
||||
def invalid_origin?(url)
|
||||
return true if unsupported_uri_scheme?(url)
|
||||
|
||||
needle = Addressable::URI.parse(url).host
|
||||
haystack = Addressable::URI.parse(@account.uri).host
|
||||
|
||||
!haystack.casecmp(needle).zero?
|
||||
end
|
||||
|
||||
def reply_to_local?
|
||||
!replied_to_status.nil? && replied_to_status.account.local?
|
||||
end
|
||||
|
||||
@@ -28,8 +28,6 @@ class ActivityPub::TagManager
|
||||
return target.uri if target.respond_to?(:local?) && !target.local?
|
||||
|
||||
case target.object_type
|
||||
when :follow
|
||||
account_follow_url(target.account.username, target)
|
||||
when :person
|
||||
account_url(target)
|
||||
when :note, :comment, :activity
|
||||
@@ -99,12 +97,6 @@ class ActivityPub::TagManager
|
||||
case klass.name
|
||||
when 'Account'
|
||||
klass.find_local(uri_to_local_id(uri, :username))
|
||||
when 'FollowRequest'
|
||||
params = Rails.application.routes.recognize_path(uri)
|
||||
klass.joins(:account).find_by!(
|
||||
accounts: { domain: nil, username: params[:account_username] },
|
||||
id: params[:id]
|
||||
)
|
||||
else
|
||||
StatusFinder.new(uri).status
|
||||
end
|
||||
|
||||
@@ -21,10 +21,6 @@ class FollowRequest < ApplicationRecord
|
||||
|
||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||
|
||||
def object_type
|
||||
:follow
|
||||
end
|
||||
|
||||
def authorize!
|
||||
account.follow!(target_account, reblogs: show_reblogs)
|
||||
MergeWorker.perform_async(target_account.id, account.id)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FollowSerializer < ActiveModel::Serializer
|
||||
attributes :type, :actor
|
||||
attribute :id, if: :dereferencable?
|
||||
attributes :id, :type, :actor
|
||||
attribute :virtual_object, key: :object
|
||||
|
||||
def id
|
||||
ActivityPub::TagManager.instance.uri_for(object)
|
||||
[ActivityPub::TagManager.instance.uri_for(object.account), '#follows/', object.id].join
|
||||
end
|
||||
|
||||
def type
|
||||
@@ -20,8 +19,4 @@ class ActivityPub::FollowSerializer < ActiveModel::Serializer
|
||||
def virtual_object
|
||||
ActivityPub::TagManager.instance.uri_for(object.target_account)
|
||||
end
|
||||
|
||||
def dereferencable?
|
||||
object.respond_to?(:object_type)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
# Should be called with confirmed valid JSON
|
||||
# and WebFinger-resolved username and domain
|
||||
def call(username, domain, json)
|
||||
return if json['inbox'].blank?
|
||||
return if json['inbox'].blank? || unsupported_uri_scheme?(json['id'])
|
||||
|
||||
@json = json
|
||||
@uri = @json['id']
|
||||
@@ -107,7 +107,21 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||
|
||||
def url
|
||||
return if @json['url'].blank?
|
||||
url_to_href(@json['url'], 'text/html')
|
||||
|
||||
url_candidate = url_to_href(@json['url'], 'text/html')
|
||||
|
||||
if unsupported_uri_scheme?(url_candidate) || mismatching_origin?(url_candidate)
|
||||
nil
|
||||
else
|
||||
url_candidate
|
||||
end
|
||||
end
|
||||
|
||||
def mismatching_origin?(url)
|
||||
needle = Addressable::URI.parse(url).host
|
||||
haystack = Addressable::URI.parse(@uri).host
|
||||
|
||||
!haystack.casecmp(needle).zero?
|
||||
end
|
||||
|
||||
def outbox_total_items
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
= fa_icon 'user-times'
|
||||
= t('accounts.unfollow')
|
||||
- else
|
||||
= link_to account_follows_path(account), data: { method: :post }, class: 'icon-button' do
|
||||
= link_to account_follow_path(account), data: { method: :post }, class: 'icon-button' do
|
||||
= fa_icon 'user-plus'
|
||||
= t('accounts.follow')
|
||||
- elsif !user_signed_in?
|
||||
|
||||
13
app/views/user_mailer/email_changed.zh-cn.html.erb
Normal file
13
app/views/user_mailer/email_changed.zh-cn.html.erb
Normal file
@@ -0,0 +1,13 @@
|
||||
<p><%= @resource.email %>,你好呀!</p>
|
||||
|
||||
<% if @resource&.unconfirmed_email? %>
|
||||
<p>我们发送这封邮件是为了提醒你,你在 <%= @instance %> 上使用的电子邮件地址即将变更为 <%= @resource.unconfirmed_email %>。</p>
|
||||
<% else %>
|
||||
<p>我们发送这封邮件是为了提醒你,你在 <%= @instance %> 上使用的电子邮件地址已经变更为 <%= @resource.unconfirmed_email %>。</p>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
如果你并没有请求更改你的电子邮件地址,则他人很有可能已经入侵你的帐户。请立即更改你的密码;如果你已经无法访问你的帐户,请联系实例的管理员请求协助。
|
||||
</p>
|
||||
|
||||
<p>来自 <%= @instance %> 管理团队</p>
|
||||
11
app/views/user_mailer/email_changed.zh-cn.text.erb
Normal file
11
app/views/user_mailer/email_changed.zh-cn.text.erb
Normal file
@@ -0,0 +1,11 @@
|
||||
<%= @resource.email %>,你好呀!
|
||||
|
||||
<% if @resource&.unconfirmed_email? %>
|
||||
我们发送这封邮件是为了提醒你,你在 <%= @instance %> 上使用的电子邮件地址即将变更为 <%= @resource.unconfirmed_email %>。
|
||||
<% else %>
|
||||
我们发送这封邮件是为了提醒你,你在 <%= @instance %> 上使用的电子邮件地址已经变更为 <%= @resource.unconfirmed_email %>。
|
||||
<% end %>
|
||||
|
||||
如果你并没有请求更改你的电子邮件地址,则他人很有可能已经入侵你的帐户。请立即更改你的密码;如果你已经无法访问你的帐户,请联系实例的管理员请求协助。
|
||||
|
||||
来自 <%= @instance %> 管理团队
|
||||
@@ -0,0 +1,13 @@
|
||||
<p><%= @resource.email %>,你好呀!</p>
|
||||
|
||||
<p>你正在更改你在 <%= @instance %> 使用的电子邮件地址。</p>
|
||||
|
||||
<p>点击下面的链接以确认操作:<br>
|
||||
<%= link_to '确认我的电子邮件地址', confirmation_url(@resource, confirmation_token: @token) %></p>
|
||||
|
||||
<p>上面的链接按不动?把下面的链接复制到地址栏再试试:<br>
|
||||
<span><%= confirmation_url(@resource, confirmation_token: @token) %></span>
|
||||
|
||||
<p>记得读一读我们的<%= link_to '使用条款', terms_url %>哦。</p>
|
||||
|
||||
<p>来自 <%= @instance %> 管理团队</p>
|
||||
@@ -0,0 +1,10 @@
|
||||
<%= @resource.email %>,你好呀!
|
||||
|
||||
你正在更改你在 <%= @instance %> 使用的电子邮件地址。
|
||||
|
||||
点击下面的链接以确认操作:
|
||||
<%= confirmation_url(@resource, confirmation_token: @token) %>
|
||||
|
||||
记得读一读我们的使用条款哦:<%= terms_url %>
|
||||
|
||||
来自 <%= @instance %> 管理团队
|
||||
@@ -95,7 +95,7 @@ Rails.application.configure do
|
||||
'X-Frame-Options' => 'DENY',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'Content-Security-Policy' => "frame-ancestors 'none'; object-src 'none'; script-src 'self' https://dev-static.glitch.social 'unsafe-inline'; base-uri 'none';" ,
|
||||
'Content-Security-Policy' => "frame-ancestors 'none'; object-src 'none'; script-src 'self' https://dev-static.glitch.social ; base-uri 'none';" ,
|
||||
'Referrer-Policy' => 'no-referrer, strict-origin-when-cross-origin',
|
||||
'Strict-Transport-Security' => 'max-age=63072000; includeSubDomains; preload',
|
||||
'X-Clacks-Overhead' => 'GNU Natalie Nguyen'
|
||||
|
||||
@@ -17,15 +17,17 @@ zh-CN:
|
||||
unconfirmed: 继续操作前请先确认你的帐户。
|
||||
mailer:
|
||||
confirmation_instructions:
|
||||
subject: Mastodon 帐户确认信息
|
||||
subject: Mastodon:确认 %{instance} 帐户信息
|
||||
email_changed:
|
||||
subject: Mastodon 电子邮件地址已被修改
|
||||
subject: Mastodon:电子邮件地址已被修改
|
||||
password_change:
|
||||
subject: Mastodon 密码已被重置
|
||||
subject: Mastodon:密码已被重置
|
||||
reconfirmation_instructions:
|
||||
subject: Mastodon:确认 %{instance} 电子邮件地址
|
||||
reset_password_instructions:
|
||||
subject: Mastodon 重置密码信息
|
||||
subject: Mastodon:重置密码信息
|
||||
unlock_instructions:
|
||||
subject: Mastodon 帐户解锁信息
|
||||
subject: Mastodon:帐户解锁信息
|
||||
omniauth_callbacks:
|
||||
failure: 由于%{reason},无法从%{kind}获得授权。
|
||||
success: 成功地从%{kind}获得授权。
|
||||
|
||||
@@ -263,12 +263,18 @@ zh-CN:
|
||||
unresolved: 未处理
|
||||
view: 查看
|
||||
settings:
|
||||
activity_api_enabled:
|
||||
desc_html: 本站用户发布的嘟文数,以及本站的活跃用户数和一周内新用户数
|
||||
title: 公开用户活跃度的统计数据
|
||||
bootstrap_timeline_accounts:
|
||||
desc_html: 用半角逗号分隔多个用户名。只能添加来自本站且未开启保护的帐户。如果留空,则默认关注本站所有的管理员。
|
||||
title: 新用户默认关注
|
||||
contact_information:
|
||||
email: 用于联系的公开电子邮件地址
|
||||
username: 用于联系的公开用户名
|
||||
peers_api_enabled:
|
||||
desc_html: 截至目前本实例在网络中已发现的域名
|
||||
title: 公开已知实例的列表
|
||||
registrations:
|
||||
closed_message:
|
||||
desc_html: 本站关闭注册期间的提示信息。可以使用 HTML 标签
|
||||
|
||||
@@ -54,8 +54,7 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :followers, only: [:index], controller: :follower_accounts
|
||||
resources :following, only: [:index], controller: :following_accounts
|
||||
resources :follows, only: [:show], module: :activitypub
|
||||
resource :follow, only: [:create], controller: :account_follow, as: :follows
|
||||
resource :follow, only: [:create], controller: :account_follow
|
||||
resource :unfollow, only: [:create], controller: :account_unfollow
|
||||
resource :outbox, only: [:show], module: :activitypub
|
||||
resource :inbox, only: [:create], module: :activitypub
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe ActivityPub::FollowsController, type: :controller do
|
||||
let(:follow_request) { Fabricate(:follow_request, account: account) }
|
||||
|
||||
render_views
|
||||
|
||||
context 'with local account' do
|
||||
let(:account) { Fabricate(:account, domain: nil) }
|
||||
|
||||
it 'returns follow request' do
|
||||
signed_request = Request.new(:get, account_follow_url(account, follow_request))
|
||||
signed_request.on_behalf_of(follow_request.target_account)
|
||||
request.headers.merge! signed_request.headers
|
||||
|
||||
get :show, params: { id: follow_request, account_username: account.username }
|
||||
|
||||
expect(body_as_json[:id]).to eq ActivityPub::TagManager.instance.uri_for(follow_request)
|
||||
expect(response).to have_http_status :success
|
||||
end
|
||||
|
||||
it 'returns http 404 without signature' do
|
||||
get :show, params: { id: follow_request, account_username: account.username }
|
||||
expect(response).to have_http_status 404
|
||||
end
|
||||
end
|
||||
|
||||
context 'with remote account' do
|
||||
let(:account) { Fabricate(:account, domain: Faker::Internet.domain_name) }
|
||||
|
||||
it 'returns http 404' do
|
||||
signed_request = Request.new(:get, account_follow_url(account, follow_request))
|
||||
signed_request.on_behalf_of(follow_request.target_account)
|
||||
request.headers.merge! signed_request.headers
|
||||
|
||||
get :show, params: { id: follow_request, account_username: account.username }
|
||||
|
||||
expect(response).to have_http_status 404
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -47,22 +47,18 @@ describe ApplicationController, type: :controller do
|
||||
include_examples 'respond_with_error', 422
|
||||
end
|
||||
|
||||
it "does not force ssl if LOCAL_HTTPS is not 'true'" do
|
||||
it "does not force ssl if Rails.env.production? is not 'true'" do
|
||||
routes.draw { get 'success' => 'anonymous#success' }
|
||||
ClimateControl.modify LOCAL_HTTPS: '' do
|
||||
allow(Rails.env).to receive(:production?).and_return(true)
|
||||
get 'success'
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
allow(Rails.env).to receive(:production?).and_return(false)
|
||||
get 'success'
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it "forces ssl if LOCAL_HTTPS is 'true'" do
|
||||
it "forces ssl if Rails.env.production? is 'true'" do
|
||||
routes.draw { get 'success' => 'anonymous#success' }
|
||||
ClimateControl.modify LOCAL_HTTPS: 'true' do
|
||||
allow(Rails.env).to receive(:production?).and_return(true)
|
||||
get 'success'
|
||||
expect(response).to redirect_to('https://test.host/success')
|
||||
end
|
||||
allow(Rails.env).to receive(:production?).and_return(true)
|
||||
get 'success'
|
||||
expect(response).to redirect_to('https://test.host/success')
|
||||
end
|
||||
|
||||
describe 'helper_method :current_account' do
|
||||
|
||||
@@ -3,49 +3,36 @@ require 'rails_helper'
|
||||
RSpec.describe ActivityPub::Activity::Accept do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
let!(:follow_request) { Fabricate(:follow_request, account: recipient, target_account: sender) }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Accept',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: {
|
||||
id: 'bar',
|
||||
type: 'Follow',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
object: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
},
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
Fabricate(:follow_request, account: recipient, target_account: sender)
|
||||
subject.perform
|
||||
end
|
||||
|
||||
context 'with concerete object representation' do
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Accept',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: {
|
||||
type: 'Follow',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
object: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
},
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
it 'creates a follow relationship' do
|
||||
expect(recipient.following?(sender)).to be true
|
||||
end
|
||||
it 'creates a follow relationship' do
|
||||
expect(recipient.following?(sender)).to be true
|
||||
end
|
||||
|
||||
context 'with object represented by id' do
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Accept',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(follow_request),
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
it 'creates a follow relationship' do
|
||||
expect(recipient.following?(sender)).to be true
|
||||
end
|
||||
it 'removes the follow request' do
|
||||
expect(recipient.requested?(sender)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
let(:json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#foo'].join,
|
||||
type: 'Create',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: object_json,
|
||||
@@ -16,6 +16,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
|
||||
|
||||
stub_request(:get, 'http://example.com/attachment.png').to_return(request_fixture('avatar.txt'))
|
||||
stub_request(:get, 'http://example.com/emoji.png').to_return(body: attachment_fixture('emojo.png'))
|
||||
end
|
||||
@@ -28,7 +30,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'standalone' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
@@ -52,7 +54,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
@@ -70,7 +72,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'unlisted' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
@@ -88,7 +90,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'private' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
@@ -108,7 +110,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
@@ -128,7 +130,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
@@ -151,7 +153,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
@@ -174,7 +176,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with mentions missing href' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
@@ -194,7 +196,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with media attachments' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
@@ -218,7 +220,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with media attachments missing url' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
@@ -239,7 +241,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with hashtags' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
@@ -263,7 +265,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with hashtags missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
@@ -284,7 +286,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with emojis' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
@@ -310,7 +312,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with emojis missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
@@ -333,7 +335,7 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
context 'with emojis missing icon' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'bar',
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
|
||||
@@ -34,12 +34,4 @@ RSpec.describe FollowRequest, type: :model do
|
||||
expect(follow_request.account.muting_reblogs?(target)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
let(:follow_request) { Fabricate(:follow_request) }
|
||||
|
||||
it 'equals to :follow' do
|
||||
expect(follow_request.object_type).to eq :follow
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,7 +46,7 @@ RSpec.configure do |config|
|
||||
config.include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
config.before :each, type: :feature do
|
||||
https = Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true'
|
||||
https = ENV['LOCAL_HTTPS'] == 'true'
|
||||
Capybara.app_host = "http#{https ? 's' : ''}://#{ENV.fetch('LOCAL_DOMAIN')}"
|
||||
end
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do
|
||||
|
||||
describe '#call' do
|
||||
before do
|
||||
sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
|
||||
|
||||
stub_request(:head, 'https://example.com/watch?v=12345').to_return(status: 404, body: '')
|
||||
subject.call(object[:id], prefetched_body: Oj.dump(object))
|
||||
end
|
||||
@@ -48,13 +50,13 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do
|
||||
{
|
||||
type: 'Link',
|
||||
mimeType: 'application/x-bittorrent',
|
||||
href: 'https://example.com/12345.torrent',
|
||||
href: "https://#{valid_domain}/12345.torrent",
|
||||
},
|
||||
|
||||
{
|
||||
type: 'Link',
|
||||
mimeType: 'text/html',
|
||||
href: 'https://example.com/watch?v=12345',
|
||||
href: "https://#{valid_domain}/watch?v=12345",
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -64,8 +66,8 @@ RSpec.describe ActivityPub::FetchRemoteStatusService do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.url).to eq 'https://example.com/watch?v=12345'
|
||||
expect(strip_tags(status.text)).to eq 'Nyan Cat 10 hours remix https://example.com/watch?v=12345'
|
||||
expect(status.url).to eq "https://#{valid_domain}/watch?v=12345"
|
||||
expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remix https://#{valid_domain}/watch?v=12345"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user