mirror of https://github.com/pixelfed/pixelfed.git
commit
aad77239a9
|
@ -49,6 +49,13 @@
|
|||
- Updated MediaStorageService, improve head checks to fix failed jobs. ([1769cdfd](https://github.com/pixelfed/pixelfed/commit/1769cdfd))
|
||||
- Updated user admin, remove expensive db query and add search. ([8feeadbf](https://github.com/pixelfed/pixelfed/commit/8feeadbf))
|
||||
- Updated Compose apis, prevent private accounts from posting public or unlisted scopes. ([f53bfa6f](https://github.com/pixelfed/pixelfed/commit/f53bfa6f))
|
||||
- Updated font icons, use font-display:swap. ([77d4353a](https://github.com/pixelfed/pixelfed/commit/77d4353a))
|
||||
- Updated ComposeModal, limit visibility scope for private accounts. ([001d4105](https://github.com/pixelfed/pixelfed/commit/001d4105))
|
||||
- Updated ComposeController, add autocomplete apis for hashtags and mentions. ([f0e48a09](https://github.com/pixelfed/pixelfed/commit/f0e48a09))
|
||||
- Updated StatusController, invalidate profile embed cache on status delete. ([9c8a87c3](https://github.com/pixelfed/pixelfed/commit/9c8a87c3))
|
||||
- Updated moderation api, invalidate profile embed. ([b2501bfc](https://github.com/pixelfed/pixelfed/commit/b2501bfc))
|
||||
- Updated Nodeinfo util, use last_active_at for monthly active user count. ([d200c12c](https://github.com/pixelfed/pixelfed/commit/d200c12c))
|
||||
- Updated PhotoPresenter, add width and height to images. ([3f8202e2](https://github.com/pixelfed/pixelfed/commit/3f8202e2))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.10.10 (2021-01-28)](https://github.com/pixelfed/pixelfed/compare/v0.10.9...v0.10.10)
|
||||
|
|
|
@ -7,6 +7,7 @@ use Auth, Cache, Storage, URL;
|
|||
use Carbon\Carbon;
|
||||
use App\{
|
||||
Avatar,
|
||||
Hashtag,
|
||||
Like,
|
||||
Media,
|
||||
MediaTag,
|
||||
|
@ -304,6 +305,72 @@ class ComposeController extends Controller
|
|||
return $places;
|
||||
}
|
||||
|
||||
public function searchMentionAutocomplete(Request $request)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'q' => 'required|string|min:2|max:50'
|
||||
]);
|
||||
|
||||
$q = $request->input('q');
|
||||
|
||||
if(Str::of($q)->startsWith('@')) {
|
||||
if(strlen($q) < 3) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
$blocked = UserFilter::whereFilterableType('App\Profile')
|
||||
->whereFilterType('block')
|
||||
->whereFilterableId($request->user()->profile_id)
|
||||
->pluck('user_id');
|
||||
|
||||
$blocked->push($request->user()->profile_id);
|
||||
|
||||
$results = Profile::select('id','domain','username')
|
||||
->whereNotIn('id', $blocked)
|
||||
->where('username','like','%'.$q.'%')
|
||||
->groupBy('domain')
|
||||
->limit(15)
|
||||
->get()
|
||||
->map(function($profile) {
|
||||
$username = $profile->domain ? substr($profile->username, 1) : $profile->username;
|
||||
return [
|
||||
'key' => '@' . str_limit($username, 30),
|
||||
'value' => $username,
|
||||
];
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function searchHashtagAutocomplete(Request $request)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'q' => 'required|string|min:2|max:50'
|
||||
]);
|
||||
|
||||
$q = $request->input('q');
|
||||
|
||||
$results = Hashtag::select('slug')
|
||||
->where('slug', 'like', '%'.$q.'%')
|
||||
->whereIsNsfw(false)
|
||||
->whereIsBanned(false)
|
||||
->limit(5)
|
||||
->get()
|
||||
->map(function($tag) {
|
||||
return [
|
||||
'key' => '#' . $tag->slug,
|
||||
'value' => $tag->slug
|
||||
];
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
|
|
|
@ -132,13 +132,15 @@ class InternalApiController extends Controller
|
|||
|
||||
public function statusReplies(Request $request, int $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'limit' => 'nullable|int|min:1|max:6'
|
||||
]);
|
||||
$parent = Status::whereScope('public')->findOrFail($id);
|
||||
|
||||
$limit = $request->input('limit') ?? 3;
|
||||
$children = Status::whereInReplyToId($parent->id)
|
||||
->orderBy('created_at', 'desc')
|
||||
->take(3)
|
||||
->take($limit)
|
||||
->get();
|
||||
|
||||
$resource = new Fractal\Resource\Collection($children, new StatusTransformer());
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
|
||||
|
@ -310,6 +312,10 @@ class InternalApiController extends Controller
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Cache::forget('_api:statuses:recent_9:' . $status->profile_id);
|
||||
Cache::forget('profile:embed:' . $status->profile_id);
|
||||
|
||||
return ['msg' => 200];
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ class PublicApiController extends Controller
|
|||
->whereNull('reblog_of_id')
|
||||
->whereIn('scope', $scope)
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->select('id', 'caption', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
|
||||
->select('id', 'caption', 'local', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
|
||||
->where('id', '>=', $request->min_id)
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($limit);
|
||||
|
@ -176,7 +176,7 @@ class PublicApiController extends Controller
|
|||
->whereNull('reblog_of_id')
|
||||
->whereIn('scope', $scope)
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->select('id', 'caption', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
|
||||
->select('id', 'caption', 'local', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
|
||||
->where('id', '<=', $request->max_id)
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($limit);
|
||||
|
@ -186,7 +186,7 @@ class PublicApiController extends Controller
|
|||
->whereNull('reblog_of_id')
|
||||
->whereIn('scope', $scope)
|
||||
->whereNotIn('profile_id', $filtered)
|
||||
->select('id', 'caption', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
|
||||
->select('id', 'caption', 'local', 'is_nsfw', 'rendered', 'profile_id', 'in_reply_to_id', 'type', 'reply_count', 'created_at')
|
||||
->orderBy('id', 'desc')
|
||||
->paginate($limit);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,12 @@ class StatusController extends Controller
|
|||
}
|
||||
|
||||
$template = $status->in_reply_to_id ? 'status.reply' : 'status.show';
|
||||
// $template = $status->type === 'video' &&
|
||||
// $request->has('video_beta') &&
|
||||
// $request->video_beta == 1 &&
|
||||
// $request->user() ?
|
||||
// 'status.show_video' : 'status.show';
|
||||
|
||||
return view($template, compact('user', 'status'));
|
||||
}
|
||||
|
||||
|
@ -212,6 +218,7 @@ class StatusController extends Controller
|
|||
|
||||
Cache::forget('_api:statuses:recent_9:' . $status->profile_id);
|
||||
Cache::forget('profile:status_count:' . $status->profile_id);
|
||||
Cache::forget('profile:embed:' . $status->profile_id);
|
||||
StatusService::del($status->id);
|
||||
if ($status->profile_id == $user->profile->id || $user->is_admin == true) {
|
||||
Cache::forget('profile:status_count:'.$status->profile_id);
|
||||
|
|
|
@ -12,24 +12,31 @@ class Nodeinfo {
|
|||
{
|
||||
$res = Cache::remember('api:nodeinfo', now()->addMinutes(15), function () {
|
||||
$activeHalfYear = Cache::remember('api:nodeinfo:ahy', now()->addHours(12), function() {
|
||||
// todo: replace with last_active_at after July 9, 2021 (96afc3e781)
|
||||
$count = collect([]);
|
||||
$likes = Like::select('profile_id')->with('actor')->where('created_at', '>', now()->subMonths(6)->toDateTimeString())->groupBy('profile_id')->get()->filter(function($like) {return $like->actor && $like->actor->domain == null;})->pluck('profile_id')->toArray();
|
||||
$count = $count->merge($likes);
|
||||
$statuses = Status::select('profile_id')->whereLocal(true)->where('created_at', '>', now()->subMonths(6)->toDateTimeString())->groupBy('profile_id')->pluck('profile_id')->toArray();
|
||||
$count = $count->merge($statuses);
|
||||
$profiles = Profile::select('id')->whereNull('domain')->where('created_at', '>', now()->subMonths(6)->toDateTimeString())->groupBy('id')->pluck('id')->toArray();
|
||||
$profiles = User::select('profile_id', 'last_active_at')
|
||||
->whereNotNull('last_active_at')
|
||||
->where('last_active_at', '>', now()->subMonths(6))
|
||||
->pluck('profile_id')
|
||||
->toArray();
|
||||
$newProfiles = User::select('profile_id', 'last_active_at', 'created_at')
|
||||
->whereNull('last_active_at')
|
||||
->where('created_at', '>', now()->subMonths(6))
|
||||
->pluck('profile_id')
|
||||
->toArray();
|
||||
$count = $count->merge($newProfiles);
|
||||
$count = $count->merge($profiles);
|
||||
return $count->unique()->count();
|
||||
});
|
||||
$activeMonth = Cache::remember('api:nodeinfo:am', now()->addHours(12), function() {
|
||||
$count = collect([]);
|
||||
$likes = Like::select('profile_id')->where('created_at', '>', now()->subMonths(1)->toDateTimeString())->groupBy('profile_id')->get()->filter(function($like) {return $like->actor && $like->actor->domain == null;})->pluck('profile_id')->toArray();
|
||||
$count = $count->merge($likes);
|
||||
$statuses = Status::select('profile_id')->whereLocal(true)->where('created_at', '>', now()->subMonths(1)->toDateTimeString())->groupBy('profile_id')->pluck('profile_id')->toArray();
|
||||
$count = $count->merge($statuses);
|
||||
$profiles = Profile::select('id')->whereNull('domain')->where('created_at', '>', now()->subMonths(1)->toDateTimeString())->groupBy('id')->pluck('id')->toArray();
|
||||
$count = $count->merge($profiles);
|
||||
return $count->unique()->count();
|
||||
$activeMonth = Cache::remember('api:nodeinfo:am', now()->addHours(2), function() {
|
||||
return User::select('last_active_at')
|
||||
->where('last_active_at', '>', now()->subMonths(1))
|
||||
->orWhere('created_at', '>', now()->subMonths(1))
|
||||
->count();
|
||||
});
|
||||
return [
|
||||
'metadata' => [
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,14 +4,14 @@
|
|||
"/js/ace.js": "/js/ace.js?id=11e5550a450fece75c33",
|
||||
"/js/activity.js": "/js/activity.js?id=64252d7f9c17e958b8d2",
|
||||
"/js/app.js": "/js/app.js?id=fdbdd51482b98e1324e8",
|
||||
"/css/app.css": "/css/app.css?id=9a0aaaef301793358e3a",
|
||||
"/css/appdark.css": "/css/appdark.css?id=d582a698993f108ec182",
|
||||
"/css/landing.css": "/css/landing.css?id=135e3d12b7cb15e0228b",
|
||||
"/css/app.css": "/css/app.css?id=9768353d7525582a1f55",
|
||||
"/css/appdark.css": "/css/appdark.css?id=17a3687db2a463c6ffe8",
|
||||
"/css/landing.css": "/css/landing.css?id=34928de21eeb3ae5f506",
|
||||
"/css/quill.css": "/css/quill.css?id=711b2150d518816d6112",
|
||||
"/js/collectioncompose.js": "/js/collectioncompose.js?id=37ac6f2e9cbcd035704f",
|
||||
"/js/collections.js": "/js/collections.js?id=be6208c4ab7909ad8ebe",
|
||||
"/js/components.js": "/js/components.js?id=56aa48f8042553148a78",
|
||||
"/js/compose.js": "/js/compose.js?id=c493ce1400063b8ab861",
|
||||
"/js/compose.js": "/js/compose.js?id=afb87c46a823d90cd3a7",
|
||||
"/js/compose-classic.js": "/js/compose-classic.js?id=ee4ad4759a55261c429c",
|
||||
"/js/developers.js": "/js/developers.js?id=f8efa9cb9101d403d6c2",
|
||||
"/js/direct.js": "/js/direct.js?id=735c52376bc4f3ec102e",
|
||||
|
@ -21,14 +21,14 @@
|
|||
"/js/memoryprofile.js": "/js/memoryprofile.js?id=1dbacb8b611b63cc22f2",
|
||||
"/js/mode-dot.js": "/js/mode-dot.js?id=dd9c87024fbaa8e75ac4",
|
||||
"/js/my2020.js": "/js/my2020.js?id=4a9d534053da8e6467c0",
|
||||
"/js/profile.js": "/js/profile.js?id=ac2fa43d195f02c0225e",
|
||||
"/js/profile.js": "/js/profile.js?id=acdc921fc2ea7b476a8d",
|
||||
"/js/profile-directory.js": "/js/profile-directory.js?id=e63d5f2c6f2d5710a8bd",
|
||||
"/js/quill.js": "/js/quill.js?id=4769f11fc9a6c32dde50",
|
||||
"/js/rempos.js": "/js/rempos.js?id=680df036dc36c4c7e3da",
|
||||
"/js/rempos.js": "/js/rempos.js?id=69bf33a25900894c2f50",
|
||||
"/js/rempro.js": "/js/rempro.js?id=6cca99808897aaf5acf5",
|
||||
"/js/search.js": "/js/search.js?id=f4319adfd5750db3be3f",
|
||||
"/js/status.js": "/js/status.js?id=2336a391ee6dcfb3403a",
|
||||
"/js/status.js": "/js/status.js?id=90f26b7d00eacb70d88e",
|
||||
"/js/story-compose.js": "/js/story-compose.js?id=99dc1cd352d71e41843d",
|
||||
"/js/theme-monokai.js": "/js/theme-monokai.js?id=85f0af57479412548223",
|
||||
"/js/timeline.js": "/js/timeline.js?id=3f189a30f1c6c99c1c5e"
|
||||
"/js/timeline.js": "/js/timeline.js?id=63d4e29e4adf3b384f87"
|
||||
}
|
||||
|
|
|
@ -479,9 +479,26 @@
|
|||
|
||||
<div v-if="page == 'visibility'" class="w-100 h-100">
|
||||
<div class="list-group list-group-flush">
|
||||
<div :class="'list-group-item lead cursor-pointer ' + [visibility == 'public'?'text-primary':'']" @click="toggleVisibility('public')">Public</div>
|
||||
<div :class="'list-group-item lead cursor-pointer ' + [visibility == 'unlisted'?'text-primary':'']" @click="toggleVisibility('unlisted')">Unlisted</div>
|
||||
<div :class="'list-group-item lead cursor-pointer ' + [visibility == 'private'?'text-primary':'']" @click="toggleVisibility('private')">Followers Only</div>
|
||||
<div
|
||||
v-if="!profile.locked"
|
||||
class="list-group-item lead cursor-pointer"
|
||||
:class="{ 'text-primary': visibility == 'public' }"
|
||||
@click="toggleVisibility('public')">
|
||||
Public
|
||||
</div>
|
||||
<div
|
||||
v-if="!profile.locked"
|
||||
class="list-group-item lead cursor-pointer"
|
||||
:class="{ 'text-primary': visibility == 'unlisted' }"
|
||||
@click="toggleVisibility('unlisted')">
|
||||
Unlisted
|
||||
</div>
|
||||
<div
|
||||
class="list-group-item lead cursor-pointer"
|
||||
:class="{ 'text-primary': visibility == 'private' }"
|
||||
@click="toggleVisibility('private')">
|
||||
Followers Only
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -641,7 +658,7 @@ export default {
|
|||
return {
|
||||
config: window.App.config,
|
||||
pageLoading: false,
|
||||
profile: {},
|
||||
profile: window._sharedData.curUser,
|
||||
composeText: '',
|
||||
composeTextLength: 0,
|
||||
nsfw: false,
|
||||
|
@ -708,20 +725,19 @@ export default {
|
|||
|
||||
methods: {
|
||||
fetchProfile() {
|
||||
let self = this;
|
||||
if(window._sharedData.curUser) {
|
||||
self.profile = window._sharedData.curUser;
|
||||
if(self.profile.locked == true) {
|
||||
self.visibility = 'private';
|
||||
self.visibilityTag = 'Followers Only';
|
||||
if(window._sharedData.curUser.id) {
|
||||
this.profile = window._sharedData.curUser;
|
||||
if(this.profile.locked == true) {
|
||||
this.visibility = 'private';
|
||||
this.visibilityTag = 'Followers Only';
|
||||
}
|
||||
} else {
|
||||
axios.get('/api/pixelfed/v1/accounts/verify_credentials').then(res => {
|
||||
self.profile = res.data;
|
||||
window.pixelfed.currentUser = res.data;
|
||||
if(res.data.locked == true) {
|
||||
self.visibility = 'private';
|
||||
self.visibilityTag = 'Followers Only';
|
||||
window._sharedData.currentUser = res.data;
|
||||
this.profile = res.data;
|
||||
if(this.profile.locked == true) {
|
||||
this.visibility = 'private';
|
||||
this.visibilityTag = 'Followers Only';
|
||||
}
|
||||
}).catch(err => {
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,7 +23,13 @@
|
|||
</div>
|
||||
<div v-else>
|
||||
<div :title="status.media_attachments[0].description">
|
||||
<img :class="status.media_attachments[0].filter_class + ' card-img-top'" :src="status.media_attachments[0].url" loading="lazy" :alt="altText(status)" onerror="this.onerror=null;this.src='/storage/no-preview.png'">
|
||||
<img class="card-img-top"
|
||||
:src="status.media_attachments[0].url"
|
||||
loading="lazy"
|
||||
:alt="altText(status)"
|
||||
:width="width()"
|
||||
:height="height()"
|
||||
onerror="this.onerror=null;this.src='/storage/no-preview.png'">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -67,6 +73,24 @@
|
|||
|
||||
toggleContentWarning(status) {
|
||||
this.$emit('togglecw');
|
||||
},
|
||||
|
||||
width() {
|
||||
if( !this.status.media_attachments[0].meta ||
|
||||
!this.status.media_attachments[0].meta.original ||
|
||||
!this.status.media_attachments[0].meta.original.width ) {
|
||||
return;
|
||||
}
|
||||
return this.status.media_attachments[0].meta.original.width;
|
||||
},
|
||||
|
||||
height() {
|
||||
if( !this.status.media_attachments[0].meta ||
|
||||
!this.status.media_attachments[0].meta.original ||
|
||||
!this.status.media_attachments[0].meta.original.height ) {
|
||||
return;
|
||||
}
|
||||
return this.status.media_attachments[0].meta.original.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4255,7 +4255,7 @@ readers do not read off random characters that represent icons */
|
|||
font-family: 'Font Awesome 5 Brands';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-display: auto;
|
||||
font-display: swap;
|
||||
src: url("/fonts/fa-brands-400.eot");
|
||||
src: url("/fonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("/fonts/fa-brands-400.woff2") format("woff2"), url("/fonts/fa-brands-400.woff") format("woff"), url("/fonts/fa-brands-400.ttf") format("truetype"), url("/fonts/fa-brands-400.svg#fontawesome") format("svg"); }
|
||||
|
||||
|
@ -4265,7 +4265,7 @@ readers do not read off random characters that represent icons */
|
|||
font-family: 'Font Awesome 5 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: auto;
|
||||
font-display: swap;
|
||||
src: url("/fonts/fa-regular-400.eot");
|
||||
src: url("/fonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("/fonts/fa-regular-400.woff2") format("woff2"), url("/fonts/fa-regular-400.woff") format("woff"), url("/fonts/fa-regular-400.ttf") format("truetype"), url("/fonts/fa-regular-400.svg#fontawesome") format("svg"); }
|
||||
|
||||
|
@ -4276,7 +4276,7 @@ readers do not read off random characters that represent icons */
|
|||
font-family: 'Font Awesome 5 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: auto;
|
||||
font-display: swap;
|
||||
src: url("/fonts/fa-solid-900.eot");
|
||||
src: url("/fonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("/fonts/fa-solid-900.woff2") format("woff2"), url("/fonts/fa-solid-900.woff") format("woff"), url("/fonts/fa-solid-900.ttf") format("truetype"), url("/fonts/fa-solid-900.svg#fontawesome") format("svg"); }
|
||||
|
||||
|
|
|
@ -113,6 +113,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::delete('/media/delete', 'ComposeController@mediaDelete');
|
||||
Route::get('/search/tag', 'ComposeController@searchTag');
|
||||
Route::get('/search/location', 'ComposeController@searchLocation');
|
||||
Route::get('/search/mention', 'ComposeController@searchMentionAutocomplete');
|
||||
Route::get('/search/hashtag', 'ComposeController@searchHashtagAutocomplete');
|
||||
|
||||
Route::post('/publish', 'ComposeController@store')
|
||||
->middleware('throttle:maxPostsPerHour,60')
|
||||
|
|
Loading…
Reference in New Issue