From 7457c6559965812f4a8f721654e700632eca1243 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 21 Nov 2023 15:12:51 +0900 Subject: [PATCH 01/15] Add verification link validation --- app/Http/Controllers/AccountController.php | 2 +- app/Libraries/UserVerificationLink.php | 40 ++++++++++++++++++++++ app/Libraries/UserVerificationState.php | 6 +++- app/helpers.php | 22 ++++++++---- 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 app/Libraries/UserVerificationLink.php diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index eb5b75ce9ee..07406ac9546 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -308,7 +308,7 @@ public function verify() public function verifyLink() { - $state = UserVerificationState::fromVerifyLink(request('key')); + $state = UserVerificationState::fromVerifyLink(get_string(request('key')) ?? ''); if ($state === null) { UserVerification::logAttempt('link', 'fail', 'incorrect_key'); diff --git a/app/Libraries/UserVerificationLink.php b/app/Libraries/UserVerificationLink.php new file mode 100644 index 00000000000..a6598ba0866 --- /dev/null +++ b/app/Libraries/UserVerificationLink.php @@ -0,0 +1,40 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Libraries; + +class UserVerificationLink +{ + private const KEY_SIZE = 32; + + public static function create(): string + { + $key = random_bytes(static::KEY_SIZE); + $hmac = static::hmac($key); + + return base64url_encode($key.$hmac); + } + + public static function isValid(string $link): bool + { + $linkBin = base64url_decode($link); + if ($linkBin === null) { + return false; + } + + $key = substr($linkBin, 0, static::KEY_SIZE); + $hmac = substr($linkBin, static::KEY_SIZE); + $expectedHmac = static::hmac($key); + + return hash_equals($expectedHmac, $hmac); + } + + private static function hmac(string $key): string + { + return hash_hmac('sha1', $key, \Crypt::getKey(), true); + } +} diff --git a/app/Libraries/UserVerificationState.php b/app/Libraries/UserVerificationState.php index 266ff6abe11..5ec90d94fed 100644 --- a/app/Libraries/UserVerificationState.php +++ b/app/Libraries/UserVerificationState.php @@ -23,6 +23,10 @@ public static function fromCurrentRequest() public static function fromVerifyLink($linkKey) { + if (!UserVerificationLink::isValid($linkKey)) { + return null; + } + $params = cache()->get("verification:{$linkKey}"); if ($params !== null) { @@ -76,7 +80,7 @@ public function issue() // 1 byte = 2^8 bits = 16^2 bits = 2 hex characters $key = bin2hex(random_bytes(config('osu.user.verification_key_length_hex') / 2)); - $linkKey = bin2hex(random_bytes(32)); + $linkKey = UserVerificationLink::create(); $expires = now()->addHours(5); $this->session->put('verification_key', $key); diff --git a/app/helpers.php b/app/helpers.php index 6e22360737c..4f20c03cdf3 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -60,6 +60,18 @@ function background_image($url, $proxy = true) return sprintf(' style="background-image:url(\'%s\');" ', e($url)); } +function base64url_decode(string $value): ?string +{ + return null_if_false(base64_decode(strtr($value, '-_', '+/'), true)); +} + +function base64url_encode(string $value): string +{ + // url safe base64 + // reference: https://datatracker.ietf.org/doc/html/rfc4648#section-5 + return rtrim(strtr(base64_encode($value), '+/', '-_'), '='); +} + function beatmap_timestamp_format($ms) { $s = $ms / 1000; @@ -310,13 +322,9 @@ function cursor_decode($cursorString): ?array function cursor_encode(?array $cursor): ?string { - if ($cursor === null) { - return null; - } - - // url safe base64 - // reference: https://datatracker.ietf.org/doc/html/rfc4648#section-5 - return rtrim(strtr(base64_encode(json_encode($cursor)), '+/', '-_'), '='); + return $cursor === null + ? null + : base64url_encode(json_encode($cursor)); } function cursor_for_response(?array $cursor): array From 738b90939ae13112b6d723e9ec84ce8a54c31f3a Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 21 Nov 2023 17:23:54 +0900 Subject: [PATCH 02/15] Add tests --- tests/Libraries/UserVerificationLinkTest.php | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/Libraries/UserVerificationLinkTest.php diff --git a/tests/Libraries/UserVerificationLinkTest.php b/tests/Libraries/UserVerificationLinkTest.php new file mode 100644 index 00000000000..319839ea417 --- /dev/null +++ b/tests/Libraries/UserVerificationLinkTest.php @@ -0,0 +1,36 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace Tests\Libraries; + +use App\Libraries\UserVerificationLink; +use Tests\TestCase; + +class UserVerificationLinkTest extends TestCase +{ + public function testIsValid(): void + { + $this->assertTrue(UserVerificationLink::isValid(UserVerificationLink::create())); + } + + /** + * @dataProvider dataProviderForTestIsValidInvalid + */ + public function testIsValidInvalid(string $value): void + { + $this->assertFalse(UserVerificationLink::isValid($value)); + } + + public function dataProviderForTestIsValidInvalid(): array + { + return [ + ['invalid'], + [''], + [base64url_encode('')], + ]; + } +} From 720bc038a4e959e8976a9cd0403ccf6ef67e8b0f Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 22 Nov 2023 14:59:11 +0900 Subject: [PATCH 03/15] Use correct int size for multiplayer attempts column --- ...ultiplayer_scores_high_attempts_to_int.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php diff --git a/database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php b/database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php new file mode 100644 index 00000000000..fc174c84ae7 --- /dev/null +++ b/database/migrations/2023_11_22_055714_change_multiplayer_scores_high_attempts_to_int.php @@ -0,0 +1,27 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + public function up(): void + { + Schema::table('multiplayer_scores_high', function (Blueprint $table) { + $table->unsignedInteger('attempts')->default(0)->change(); + }); + } + + public function down(): void + { + Schema::table('multiplayer_scores_high', function (Blueprint $table) { + $table->unsignedTinyInteger('attempts')->default(0)->change(); + }); + } +}; From f5480c696f1856f0aa1e27ea496f9f2ac06091c2 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 22 Nov 2023 18:46:05 +0900 Subject: [PATCH 04/15] fix chat input box going into wrong flow context on safari --- resources/css/bem/chat-conversation-panel.less | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/css/bem/chat-conversation-panel.less b/resources/css/bem/chat-conversation-panel.less index de9dabc370e..27d373d547f 100644 --- a/resources/css/bem/chat-conversation-panel.less +++ b/resources/css/bem/chat-conversation-panel.less @@ -5,9 +5,15 @@ display: flex; flex-direction: column; flex: 1; - overflow-y: auto; // without this, firefox scroll breaks position: relative; + @media @mobile { + overflow-y: hidden; // Needed to limit the height of the panel so chat-conversation is scrollable and + // chat-conversation-list doesn't become 0 height on mobile. + // height: 100% alone doesn't work. + // The panel itself should not be scrollable. + } + &__instructions { margin-top: 10px; } From 233a12013374b7d5c7a7fc9fe1a511a7860eff84 Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 24 Nov 2023 15:27:55 +0900 Subject: [PATCH 05/15] More generic class name --- ...icationLink.php => SignedRandomString.php} | 21 +++++++++---------- app/Libraries/UserVerificationState.php | 4 ++-- ...inkTest.php => SignedRandomStringTest.php} | 10 ++++----- 3 files changed, 17 insertions(+), 18 deletions(-) rename app/Libraries/{UserVerificationLink.php => SignedRandomString.php} (56%) rename tests/Libraries/{UserVerificationLinkTest.php => SignedRandomStringTest.php} (68%) diff --git a/app/Libraries/UserVerificationLink.php b/app/Libraries/SignedRandomString.php similarity index 56% rename from app/Libraries/UserVerificationLink.php rename to app/Libraries/SignedRandomString.php index a6598ba0866..ef4f339f3e1 100644 --- a/app/Libraries/UserVerificationLink.php +++ b/app/Libraries/SignedRandomString.php @@ -7,27 +7,26 @@ namespace App\Libraries; -class UserVerificationLink +class SignedRandomString { - private const KEY_SIZE = 32; - - public static function create(): string + public static function create(int $randomSize): string { - $key = random_bytes(static::KEY_SIZE); + $key = random_bytes($randomSize); $hmac = static::hmac($key); - return base64url_encode($key.$hmac); + return base64url_encode($hmac.$key); } - public static function isValid(string $link): bool + public static function isValid(string $input): bool { - $linkBin = base64url_decode($link); - if ($linkBin === null) { + $bin = base64url_decode($input); + if ($bin === null) { return false; } - $key = substr($linkBin, 0, static::KEY_SIZE); - $hmac = substr($linkBin, static::KEY_SIZE); + // hmac size for sha1 is 20 + $hmac = substr($bin, 0, 20); + $key = substr($bin, 20); $expectedHmac = static::hmac($key); return hash_equals($expectedHmac, $hmac); diff --git a/app/Libraries/UserVerificationState.php b/app/Libraries/UserVerificationState.php index 5ec90d94fed..61c336ee09a 100644 --- a/app/Libraries/UserVerificationState.php +++ b/app/Libraries/UserVerificationState.php @@ -23,7 +23,7 @@ public static function fromCurrentRequest() public static function fromVerifyLink($linkKey) { - if (!UserVerificationLink::isValid($linkKey)) { + if (!SignedRandomString::isValid($linkKey)) { return null; } @@ -80,7 +80,7 @@ public function issue() // 1 byte = 2^8 bits = 16^2 bits = 2 hex characters $key = bin2hex(random_bytes(config('osu.user.verification_key_length_hex') / 2)); - $linkKey = UserVerificationLink::create(); + $linkKey = SignedRandomString::create(32); $expires = now()->addHours(5); $this->session->put('verification_key', $key); diff --git a/tests/Libraries/UserVerificationLinkTest.php b/tests/Libraries/SignedRandomStringTest.php similarity index 68% rename from tests/Libraries/UserVerificationLinkTest.php rename to tests/Libraries/SignedRandomStringTest.php index 319839ea417..b917154a0f6 100644 --- a/tests/Libraries/UserVerificationLinkTest.php +++ b/tests/Libraries/SignedRandomStringTest.php @@ -7,14 +7,14 @@ namespace Tests\Libraries; -use App\Libraries\UserVerificationLink; +use App\Libraries\SignedRandomString; use Tests\TestCase; -class UserVerificationLinkTest extends TestCase +class SignedRandomStringTest extends TestCase { public function testIsValid(): void { - $this->assertTrue(UserVerificationLink::isValid(UserVerificationLink::create())); + $this->assertTrue(SignedRandomString::isValid(SignedRandomString::create(40))); } /** @@ -22,7 +22,7 @@ public function testIsValid(): void */ public function testIsValidInvalid(string $value): void { - $this->assertFalse(UserVerificationLink::isValid($value)); + $this->assertFalse(SignedRandomString::isValid($value)); } public function dataProviderForTestIsValidInvalid(): array @@ -30,7 +30,7 @@ public function dataProviderForTestIsValidInvalid(): array return [ ['invalid'], [''], - [base64url_encode('')], + [base64url_encode('test')], ]; } } From c182cdc8da956a7bfa7ed23d35b8707af803e635 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 27 Nov 2023 16:44:37 +0900 Subject: [PATCH 06/15] don't update autosize on component update --- resources/js/components/textarea-autosize.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/js/components/textarea-autosize.tsx b/resources/js/components/textarea-autosize.tsx index b47d3da401a..4b120e5d856 100644 --- a/resources/js/components/textarea-autosize.tsx +++ b/resources/js/components/textarea-autosize.tsx @@ -61,8 +61,6 @@ export default class TextareaAutosize extends React.Component { if (this.ref.current.style.overflowX !== 'hidden') { this.ref.current.style.overflowX = 'hidden'; } - - autosize.update(this.ref.current); } componentWillUnmount() { From 9f62566b0722753d5783ee7f64c31cea9cb9e53c Mon Sep 17 00:00:00 2001 From: nanaya Date: Mon, 27 Nov 2023 21:11:44 +0900 Subject: [PATCH 07/15] Simplify laravel provider list --- config/app.php | 70 ++++++++++---------------------------------------- 1 file changed, 14 insertions(+), 56 deletions(-) diff --git a/config/app.php b/config/app.php index 1c483773537..d39297d1714 100644 --- a/config/app.php +++ b/config/app.php @@ -1,6 +1,7 @@ [ - - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - // Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - // Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - // We're using our own SessionServiceProvider so we can override the session id naming (for redis key namespacing) - App\Providers\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - - /* - * Package Service Providers... - */ - GrahamCampbell\GitHub\GitHubServiceProvider::class, - Mariuzzo\LaravelJsLocalization\LaravelJsLocalizationServiceProvider::class, - Laravel\Tinker\TinkerServiceProvider::class, - - /* - * Application Service Providers... - */ + 'providers' => ServiceProvider::defaultProviders()->except([ + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + ])->merge([ App\Providers\AppServiceProvider::class, + App\Providers\AuthServiceProvider::class, App\Providers\EventServiceProvider::class, + // Override default migrate:fresh + App\Providers\MigrationServiceProvider::class, App\Providers\RouteServiceProvider::class, - - /* - * After DB transaction commit support - */ + // Override the session id naming (for redis key namespacing) + App\Providers\SessionServiceProvider::class, + // After DB transaction commit support App\Providers\TransactionStateServiceProvider::class, - /* - * OAuth2 Setup - */ - - App\Providers\AuthServiceProvider::class, - Laravel\Passport\PassportServiceProvider::class, - - /* Datadog Metrics */ - ChaseConey\LaravelDatadogHelper\LaravelDatadogHelperServiceProvider::class, - - /* Override default migrate:fresh */ - App\Providers\MigrationServiceProvider::class, - ], + Mariuzzo\LaravelJsLocalization\LaravelJsLocalizationServiceProvider::class, + ])->toArray(), /* |-------------------------------------------------------------------------- From 33c2c2fefdc5dd6249a257f4a009a028521b13aa Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 28 Nov 2023 19:19:39 +0900 Subject: [PATCH 08/15] Test correct set of OAuth routes registered --- app/Providers/AuthServiceProvider.php | 3 +- tests/Providers/AuthServiceProviderTest.php | 58 +++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/Providers/AuthServiceProviderTest.php diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index bcb8f9ea713..719bcb38c75 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -12,6 +12,7 @@ use Carbon\Carbon; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; +use Laravel\Passport\Http\Controllers\AccessTokenController; use Laravel\Passport\Http\Controllers\ApproveAuthorizationController; use Laravel\Passport\Http\Controllers\DenyAuthorizationController; use Laravel\Passport\Passport; @@ -52,7 +53,7 @@ public function boot() // RouteServiceProvider current runs before our provider, so Passport's default routes will override // those set in routes/web.php. Route::group(['prefix' => 'oauth', 'as' => 'oauth.'], function () { - Route::post('token', '\Laravel\Passport\Http\Controllers\AccessTokenController@issueToken')->middleware('throttle')->name('passport.token'); + Route::post('token', AccessTokenController::class.'@issueToken')->middleware('throttle')->name('passport.token'); Route::get('authorize', AuthorizationController::class.'@authorize') ->middleware(['web', 'verify-user']) ->name('authorizations.authorize'); diff --git a/tests/Providers/AuthServiceProviderTest.php b/tests/Providers/AuthServiceProviderTest.php new file mode 100644 index 00000000000..1528663567f --- /dev/null +++ b/tests/Providers/AuthServiceProviderTest.php @@ -0,0 +1,58 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace Tests\Providers; + +use App\Http\Controllers\OAuth\AuthorizedClientsController; +use App\Http\Controllers\OAuth\ClientsController; +use App\Http\Controllers\Passport\AuthorizationController; +use Laravel\Passport\Http\Controllers\AccessTokenController; +use Laravel\Passport\Http\Controllers\ApproveAuthorizationController; +use Laravel\Passport\Http\Controllers\DenyAuthorizationController; +use Request; +use Route; +use Tests\TestCase; + +class AuthServiceProviderTest extends TestCase +{ + /** + * @dataProvider oauthRoutesRegisteredDataProvider + */ + public function testOAuthRoutesRegistered($url, $method, $uses) + { + $route = Route::getRoutes()->match(Request::create($url, $method)); + $this->assertSame($uses, $route->action['uses']); + } + + public function testPassportDefaultRoutesNotRegistered() + { + $routeNames = array_keys(Route::getRoutes()->getRoutesByName()); + + foreach ($routeNames as $routeName) { + $this->assertStringStartsNotWith('passport.', $routeName); + } + } + + public function oauthRoutesRegisteredDataProvider() + { + return [ + ['oauth/authorize', 'GET', AuthorizationController::class.'@authorize'], + ['oauth/authorize', 'POST', ApproveAuthorizationController::class.'@approve'], + ['oauth/authorize', 'DELETE', DenyAuthorizationController::class.'@deny'], + + ['oauth/authorized-clients/1', 'DELETE', AuthorizedClientsController::class.'@destroy'], + + ['oauth/clients', 'GET', ClientsController::class.'@index'], + ['oauth/clients', 'POST', ClientsController::class.'@store'], + ['oauth/clients/1', 'PUT', ClientsController::class.'@update'], + ['oauth/clients/1', 'DELETE', ClientsController::class.'@destroy'], + ['oauth/clients/1/reset-secret', 'POST', ClientsController::class.'@resetSecret'], + + ['oauth/token', 'POST', AccessTokenController::class.'@issueToken'], + ]; + } +} From a898df0d5264bf899acd68fb9361b6ac95954a8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:21:58 +0000 Subject: [PATCH 09/15] Bump phpseclib/phpseclib from 3.0.20 to 3.0.34 Bumps [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib) from 3.0.20 to 3.0.34. - [Release notes](https://github.com/phpseclib/phpseclib/releases) - [Changelog](https://github.com/phpseclib/phpseclib/blob/master/CHANGELOG.md) - [Commits](https://github.com/phpseclib/phpseclib/compare/3.0.20...3.0.34) --- updated-dependencies: - dependency-name: phpseclib/phpseclib dependency-type: indirect ... Signed-off-by: dependabot[bot] --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 91ddc6d9a0e..e01bc972f15 100644 --- a/composer.lock +++ b/composer.lock @@ -6434,16 +6434,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.20", + "version": "3.0.34", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67" + "reference": "56c79f16a6ae17e42089c06a2144467acc35348a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67", - "reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a", + "reference": "56c79f16a6ae17e42089c06a2144467acc35348a", "shasum": "" }, "require": { @@ -6524,7 +6524,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.20" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.34" }, "funding": [ { @@ -6540,7 +6540,7 @@ "type": "tidelift" } ], - "time": "2023-06-13T06:30:34+00:00" + "time": "2023-11-27T11:13:31+00:00" }, { "name": "psr/cache", From eabfa609762b1a8d130fd97b45780ee9f0254909 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 29 Nov 2023 15:26:01 +0900 Subject: [PATCH 10/15] Enforce native redis and ds extensions --- composer.json | 3 ++- composer.lock | 62 +++++---------------------------------------------- 2 files changed, 7 insertions(+), 58 deletions(-) diff --git a/composer.json b/composer.json index fdad702a921..a77c864bf5a 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,8 @@ } ], "require": { + "ext-ds": "*", + "ext-redis": "*", "anhskohbo/no-captcha": "^3.2", "chaseconey/laravel-datadog-helper": ">=1.2.0", "egulias/email-validator": "*", @@ -40,7 +42,6 @@ "maennchen/zipstream-php": "^2.1", "mariuzzo/laravel-js-localization": "*", "paypal/paypal-checkout-sdk": "*", - "php-ds/php-ds": "^1.3", "sentry/sentry-laravel": "*", "symfony/yaml": "*", "tightenco/ziggy": ">=0.8.1", diff --git a/composer.lock b/composer.lock index e01bc972f15..45ae87b2800 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cc91b3026bfa983837950f0e96fb7c8a", + "content-hash": "c748b63fdc90f32f3850b615cb80eb80", "packages": [ { "name": "anhskohbo/no-captcha", @@ -5807,61 +5807,6 @@ }, "time": "2021-09-14T21:35:26+00:00" }, - { - "name": "php-ds/php-ds", - "version": "v1.4.1", - "source": { - "type": "git", - "url": "https://github.com/php-ds/polyfill.git", - "reference": "43d2df301a9e2017f67b8c11d94a5222f9c00fd1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-ds/polyfill/zipball/43d2df301a9e2017f67b8c11d94a5222f9c00fd1", - "reference": "43d2df301a9e2017f67b8c11d94a5222f9c00fd1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=7.0" - }, - "provide": { - "ext-ds": "1.3.0" - }, - "require-dev": { - "php-ds/tests": "^1.3" - }, - "suggest": { - "ext-ds": "to improve performance and reduce memory usage" - }, - "type": "library", - "autoload": { - "psr-4": { - "Ds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Rudi Theunissen", - "email": "rudolf.theunissen@gmail.com" - } - ], - "keywords": [ - "data structures", - "ds", - "php", - "polyfill" - ], - "support": { - "issues": "https://github.com/php-ds/polyfill/issues", - "source": "https://github.com/php-ds/polyfill/tree/v1.4.1" - }, - "time": "2022-03-09T20:39:30+00:00" - }, { "name": "php-http/cache-plugin", "version": "1.8.0", @@ -13882,7 +13827,10 @@ }, "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "ext-ds": "*", + "ext-redis": "*" + }, "platform-dev": [], "platform-overrides": { "php": "8.2.0" From 11e8d902cb083f2e3b60505c71c2f16279731124 Mon Sep 17 00:00:00 2001 From: Taevas <67872932+TTTaevas@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:10:19 +0100 Subject: [PATCH 11/15] Fix some typos in the API's documentation --- resources/views/docs/_using_chat.md | 6 +++--- resources/views/docs/_websocket_commands.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/views/docs/_using_chat.md b/resources/views/docs/_using_chat.md index 57a0bf82078..bd50c344efb 100644 --- a/resources/views/docs/_using_chat.md +++ b/resources/views/docs/_using_chat.md @@ -33,7 +33,7 @@ e.g. `GET` `/chat/updates?includes[]=presence` ## Creating a channel -Make a request to the [Create Channel](#create-channel) endpoint +Make a request to the [Create Channel](#create-channel) endpoint. Only `PM` and `ANNOUNCE` type channels may be created. Creating a channel will automatically join it. Re-creating a `PM` channel will simply rejoin the existing channel. @@ -42,7 +42,7 @@ Re-creating a `PM` channel will simply rejoin the existing channel. Make a request to the [Join Channel](#join-channel) endpoint where `channel` is the `channel_id`. -A [chat.channel.join](#chatchanneljoin) event is sent when the over the websocket when the user joins a channel. +A [chat.channel.join](#chatchanneljoin) event is sent over the websocket when the user joins a channel. ## Leaving a channel @@ -55,7 +55,7 @@ A [chat.channel.part](#chatchannelpart) event is sent over the websocket when th ## Sending messages Channels should be [joined](#joining-a-channel) or [created](#creating-a-channel) before messages are sent to them. -To send a message a channel, make a request to the [Send Message to Channel](#send-message-to-channel) endpoint. +To send a message to a channel, make a request to the [Send Message to Channel](#send-message-to-channel) endpoint. A [chat.message.new](#chatmessagenew) event is sent over the websocket when the user receives a message. diff --git a/resources/views/docs/_websocket_commands.md b/resources/views/docs/_websocket_commands.md index 701d2ffa8de..78eeb395f7d 100644 --- a/resources/views/docs/_websocket_commands.md +++ b/resources/views/docs/_websocket_commands.md @@ -27,5 +27,5 @@ webSocket.send(JSON.stringify({ event: 'chat.start' })); Send to the websocket to stop receiving chat messages. ```javascript -webSocket.send(JSON.stringify({ event: 'chat.start' })); +webSocket.send(JSON.stringify({ event: 'chat.end' })); ``` From abf1c323ba8bedc6c2b819f4be6d546d0ff80af3 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 30 Nov 2023 13:07:14 +0900 Subject: [PATCH 12/15] don't renderer on same props, otherwise clicking also causes rerender --- resources/js/components/textarea-autosize.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/components/textarea-autosize.tsx b/resources/js/components/textarea-autosize.tsx index 4b120e5d856..244d7828a5c 100644 --- a/resources/js/components/textarea-autosize.tsx +++ b/resources/js/components/textarea-autosize.tsx @@ -14,7 +14,7 @@ interface State { lineHeight?: number; } -export default class TextareaAutosize extends React.Component { +export default class TextareaAutosize extends React.PureComponent { static readonly defaultProps = { async: false, rows: 1, From 437b1169a36e4e04cd0e4536822da78f78b5c980 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 30 Nov 2023 14:44:39 +0900 Subject: [PATCH 13/15] update on componentDidUpdate but don't trigger double update on input. --- resources/js/components/textarea-autosize.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/resources/js/components/textarea-autosize.tsx b/resources/js/components/textarea-autosize.tsx index 244d7828a5c..8d1cd4afdf5 100644 --- a/resources/js/components/textarea-autosize.tsx +++ b/resources/js/components/textarea-autosize.tsx @@ -21,6 +21,7 @@ export default class TextareaAutosize extends React.PureComponent }; private readonly ref = this.props.innerRef ?? React.createRef(); + private shouldUpdate = true; private get maxHeight() { return this.props.maxRows != null && this.state.lineHeight != null @@ -61,6 +62,13 @@ export default class TextareaAutosize extends React.PureComponent if (this.ref.current.style.overflowX !== 'hidden') { this.ref.current.style.overflowX = 'hidden'; } + + // Avoid double updating since autosize automatically triggers update on input. + if (this.shouldUpdate) { + autosize.update(this.ref.current); + } else { + this.shouldUpdate = true; + } } componentWillUnmount() { @@ -69,16 +77,22 @@ export default class TextareaAutosize extends React.PureComponent } render() { - const { async, innerRef, maxRows, style, ...otherProps } = this.props; + const { async, innerRef, onInput, maxRows, style, ...otherProps } = this.props; const maxHeight = this.maxHeight; return (