소스 검색

Merge pull request #2690 from pixelfed/staging

Staging
daniel 4 년 전
부모
커밋
aad77239a9

+ 7 - 0
CHANGELOG.md

@@ -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)

+ 67 - 0
app/Http/Controllers/ComposeController.php

@@ -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, [

+ 9 - 3
app/Http/Controllers/InternalApiController.php

@@ -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];
     }
 

+ 3 - 3
app/Http/Controllers/PublicApiController.php

@@ -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);
         }

+ 7 - 0
app/Http/Controllers/StatusController.php

@@ -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);

+ 17 - 10
app/Util/Site/Nodeinfo.php

@@ -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' => [

BIN
public/css/app.css


BIN
public/css/appdark.css


BIN
public/css/landing.css


BIN
public/css/quill.css


BIN
public/js/compose.js


BIN
public/js/profile.js


BIN
public/js/rempos.js


BIN
public/js/status.js


BIN
public/js/timeline.js


BIN
public/mix-manifest.json


+ 31 - 15
resources/assets/js/components/ComposeModal.vue

@@ -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 => {
 				});

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 715 - 700
resources/assets/js/components/Timeline.vue


+ 25 - 1
resources/assets/js/components/presenter/PhotoPresenter.vue

@@ -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;
 			}
 		}
 	}

+ 3 - 3
resources/assets/sass/lib/fontawesome.scss

@@ -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"); }
 

+ 2 - 0
routes/web.php

@@ -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')

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.