瀏覽代碼

Merge pull request #5828 from pixelfed/staging

[Bug Fix] Fix IG import
daniel 4 月之前
父節點
當前提交
d23ffac21e
共有 60 個文件被更改,包括 1703 次插入1735 次删除
  1. 1 0
      CHANGELOG.md
  2. 3 3
      app/Console/Commands/TransformImports.php
  3. 3 1
      app/Http/Controllers/Admin/AdminSettingsController.php
  4. 4 0
      app/Http/Controllers/Api/ApiV1Controller.php
  5. 1 0
      app/Http/Controllers/ComposeController.php
  6. 2 0
      app/Http/Controllers/ImportPostController.php
  7. 57 56
      app/Jobs/ImageOptimizePipeline/ImageUpdate.php
  8. 2 1
      app/Util/Media/Image.php
  9. 3 0
      config/import.php
  10. 二進制
      public/_lang/bn.json
  11. 二進制
      public/_lang/de.json
  12. 二進制
      public/_lang/en.json
  13. 二進制
      public/_lang/es.json
  14. 二進制
      public/_lang/fi.json
  15. 二進制
      public/_lang/fr.json
  16. 二進制
      public/_lang/hi.json
  17. 二進制
      public/_lang/hu.json
  18. 二進制
      public/_lang/it.json
  19. 二進制
      public/_lang/pt.json
  20. 二進制
      public/_lang/ru.json
  21. 二進制
      public/_lang/sk.json
  22. 二進制
      public/js/account-import.js
  23. 二進制
      public/js/admin.js
  24. 二進制
      public/js/app.js
  25. 二進制
      public/js/compose.chunk.a98574fc749d74ec.js
  26. 0 0
      public/js/compose.chunk.a98574fc749d74ec.js.LICENSE.txt
  27. 二進制
      public/js/compose.chunk.b06beb250e24db17.js
  28. 二進制
      public/js/compose.js
  29. 二進制
      public/js/dms.chunk.1a2a644df5c78346.js
  30. 二進制
      public/js/dms.chunk.e7e428d4df2a00a6.js
  31. 二進制
      public/js/landing.js
  32. 二進制
      public/js/manifest.js
  33. 二進制
      public/js/post.chunk.0757a6f1391ea041.js
  34. 二進制
      public/js/post.chunk.70d3758a59ef3742.js
  35. 0 0
      public/js/post.chunk.70d3758a59ef3742.js.LICENSE.txt
  36. 二進制
      public/js/profile.chunk.5a5ea597e5286899.js
  37. 二進制
      public/js/profile.chunk.5c642d2321d67652.js
  38. 二進制
      public/js/spa.js
  39. 二進制
      public/js/story-compose.js
  40. 二進制
      public/js/timeline.js
  41. 二進制
      public/mix-manifest.json
  42. 10 7
      resources/assets/components/AccountImport.vue
  43. 434 433
      resources/assets/components/Post.vue
  44. 5 1
      resources/assets/components/admin/AdminSettings.vue
  45. 879 977
      resources/assets/components/partials/navbar.vue
  46. 34 25
      resources/assets/js/app.js
  47. 13 13
      resources/assets/js/i18n/bn.json
  48. 4 4
      resources/assets/js/i18n/de.json
  49. 14 0
      resources/assets/js/i18n/en.json
  50. 32 18
      resources/assets/js/i18n/es.json
  51. 68 68
      resources/assets/js/i18n/fi.json
  52. 8 8
      resources/assets/js/i18n/fr.json
  53. 32 32
      resources/assets/js/i18n/hi.json
  54. 4 4
      resources/assets/js/i18n/hu.json
  55. 2 2
      resources/assets/js/i18n/it.json
  56. 44 44
      resources/assets/js/i18n/pt.json
  57. 21 21
      resources/assets/js/i18n/ru.json
  58. 2 2
      resources/assets/js/i18n/sk.json
  59. 17 11
      resources/assets/js/spa.js
  60. 4 4
      resources/views/status/reply.blade.php

+ 1 - 0
CHANGELOG.md

@@ -4,6 +4,7 @@
 
 ### Added
 - Add app register email verify resends ([dbd1e17](https://github.com/pixelfed/pixelfed/commit/dbd1e17))
+- Add AVIF support ([7ddbe0c47](https://github.com/pixelfed/pixelfed/commit/7ddbe0c47))
 
 ### Features
 - WebGL photo filters ([#5374](https://github.com/pixelfed/pixelfed/pull/5374))

+ 3 - 3
app/Console/Commands/TransformImports.php

@@ -109,11 +109,11 @@ class TransformImports extends Command
             $status->caption = $caption;
             $status->type = $ip->post_type;
 
-            $status->scope = 'unlisted';
-            $status->visibility = 'unlisted';
+            $status->scope = 'public';
+            $status->visibility = 'public';
             $status->id = $idk['id'];
             $status->created_at = now()->parse($ip->creation_date);
-            $status->save();
+            $status->saveQuietly();
 
             foreach ($ip->media as $ipm) {
                 $fileName = last(explode('/', $ipm['uri']));

+ 3 - 1
app/Http/Controllers/Admin/AdminSettingsController.php

@@ -70,6 +70,7 @@ trait AdminSettingsController
             'type_gif' => 'nullable',
             'type_mp4' => 'nullable',
             'type_webp' => 'nullable',
+            'type_avif' => 'nullable',
             'admin_account_id' => 'nullable',
             'regs' => 'required|in:open,filtered,closed',
             'account_migration' => 'nullable',
@@ -128,6 +129,7 @@ trait AdminSettingsController
             'type_gif' => 'image/gif',
             'type_mp4' => 'video/mp4',
             'type_webp' => 'image/webp',
+            'type_avif' => 'image/avif',
         ];
 
         foreach ($mimes as $key => $value) {
@@ -609,7 +611,7 @@ trait AdminSettingsController
         $mediaTypes = $request->input('media_types');
         $mediaArray = explode(',', $mediaTypes);
         foreach ($mediaArray as $mediaType) {
-            if (! in_array($mediaType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4'])) {
+            if (! in_array($mediaType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4', 'image/avif'])) {
                 return redirect()->back()->withErrors(['media_types' => 'Invalid media type']);
             }
         }

+ 4 - 0
app/Http/Controllers/Api/ApiV1Controller.php

@@ -1897,6 +1897,8 @@ class ApiV1Controller extends Controller
         switch ($media->mime) {
             case 'image/jpeg':
             case 'image/png':
+            case 'image/webp':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 
@@ -2125,6 +2127,8 @@ class ApiV1Controller extends Controller
         switch ($media->mime) {
             case 'image/jpeg':
             case 'image/png':
+            case 'image/webp':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 

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

@@ -133,6 +133,7 @@ class ComposeController extends Controller
             case 'image/jpeg':
             case 'image/png':
             case 'image/webp':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 

+ 2 - 0
app/Http/Controllers/ImportPostController.php

@@ -29,6 +29,8 @@ class ImportPostController extends Controller
 
             'allow_video_posts' => config('import.instagram.allow_video_posts'),
 
+            'allow_image_webp' => config('import.instagram.allow_image_webp') && str_contains(config_cache('pixelfed.media_types'), 'image/webp'),
+
             'permissions' => [
                 'admins_only' => config('import.instagram.permissions.admins_only'),
                 'admin_follows_only' => config('import.instagram.permissions.admin_follows_only'),

+ 57 - 56
app/Jobs/ImageOptimizePipeline/ImageUpdate.php

@@ -16,70 +16,71 @@ use App\Jobs\MediaPipeline\MediaStoragePipeline;
 
 class ImageUpdate implements ShouldQueue
 {
-	use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
-	protected $media;
+    protected $media;
 
-	protected $protectedMimes = [
-		'image/jpeg',
-		'image/png',
-		'image/webp'
-	];
+    protected $protectedMimes = [
+        'image/jpeg',
+        'image/png',
+        'image/webp',
+        'image/avif'
+    ];
 
-	/**
-	 * Delete the job if its models no longer exist.
-	 *
-	 * @var bool
-	 */
-	public $deleteWhenMissingModels = true;
+    /**
+     * Delete the job if its models no longer exist.
+     *
+     * @var bool
+     */
+    public $deleteWhenMissingModels = true;
 
-	/**
-	 * Create a new job instance.
-	 *
-	 * @return void
-	 */
-	public function __construct(Media $media)
-	{
-		$this->media = $media;
-	}
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct(Media $media)
+    {
+        $this->media = $media;
+    }
 
-	/**
-	 * Execute the job.
-	 *
-	 * @return void
-	 */
-	public function handle()
-	{
-		$media = $this->media;
-		if(!$media) {
-			return;
-		}
-		$path = storage_path('app/'.$media->media_path);
-		$thumb = storage_path('app/'.$media->thumbnail_path);
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $media = $this->media;
+        if(!$media) {
+            return;
+        }
+        $path = storage_path('app/'.$media->media_path);
+        $thumb = storage_path('app/'.$media->thumbnail_path);
 
-		if (!is_file($path)) {
-			return;
-		}
+        if (!is_file($path)) {
+            return;
+        }
 
-		if((bool) config_cache('pixelfed.optimize_image')) {
-			if (in_array($media->mime, $this->protectedMimes) == true) {
-				ImageOptimizer::optimize($thumb);
-				if(!$media->skip_optimize) {
-					ImageOptimizer::optimize($path);
-				}
-			}
-		}
+        if((bool) config_cache('pixelfed.optimize_image')) {
+            if (in_array($media->mime, $this->protectedMimes) == true) {
+                ImageOptimizer::optimize($thumb);
+                if(!$media->skip_optimize) {
+                    ImageOptimizer::optimize($path);
+                }
+            }
+        }
 
-		if (!is_file($path) || !is_file($thumb)) {
-			return;
-		}
+        if (!is_file($path) || !is_file($thumb)) {
+            return;
+        }
 
-		$photo_size = filesize($path);
-		$thumb_size = filesize($thumb);
-		$total = ($photo_size + $thumb_size);
-		$media->size = $total;
-		$media->save();
+        $photo_size = filesize($path);
+        $thumb_size = filesize($thumb);
+        $total = ($photo_size + $thumb_size);
+        $media->size = $total;
+        $media->save();
 
-		MediaStoragePipeline::dispatch($media);
-	}
+        MediaStoragePipeline::dispatch($media);
+    }
 }

+ 2 - 1
app/Util/Media/Image.php

@@ -16,7 +16,8 @@ class Image
 	public $acceptedMimes = [
 		'image/png',
 		'image/jpeg',
-		'image/webp'
+		'image/webp',
+		'image/avif',
 	];
 
 	public function __construct()

+ 3 - 0
config/import.php

@@ -24,6 +24,9 @@ return [
         // Allow video posts to be imported
         'allow_video_posts' => env('PF_IMPORT_IG_ALLOW_VIDEO_POSTS', true),
 
+        // Allow webp posts to be imported
+        'allow_image_webp' => env('PF_IMPORT_IG_ALLOW_IMAGE_WEBP', true),
+
         'permissions' => [
             // Limit to admin accounts only
             'admins_only' => env('PF_IMPORT_IG_PERM_ADMIN_ONLY', false),

二進制
public/_lang/bn.json


二進制
public/_lang/de.json


二進制
public/_lang/en.json


二進制
public/_lang/es.json


二進制
public/_lang/fi.json


二進制
public/_lang/fr.json


二進制
public/_lang/hi.json


二進制
public/_lang/hu.json


二進制
public/_lang/it.json


二進制
public/_lang/pt.json


二進制
public/_lang/ru.json


二進制
public/_lang/sk.json


二進制
public/js/account-import.js


二進制
public/js/admin.js


二進制
public/js/app.js


二進制
public/js/compose.chunk.a98574fc749d74ec.js


+ 0 - 0
public/js/compose.chunk.b06beb250e24db17.js.LICENSE.txt → public/js/compose.chunk.a98574fc749d74ec.js.LICENSE.txt


二進制
public/js/compose.chunk.b06beb250e24db17.js


二進制
public/js/compose.js


二進制
public/js/dms.chunk.1a2a644df5c78346.js


二進制
public/js/dms.chunk.e7e428d4df2a00a6.js


二進制
public/js/landing.js


二進制
public/js/manifest.js


二進制
public/js/post.chunk.0757a6f1391ea041.js


二進制
public/js/post.chunk.70d3758a59ef3742.js


+ 0 - 0
public/js/post.chunk.0757a6f1391ea041.js.LICENSE.txt → public/js/post.chunk.70d3758a59ef3742.js.LICENSE.txt


二進制
public/js/profile.chunk.5a5ea597e5286899.js


二進制
public/js/profile.chunk.5c642d2321d67652.js


二進制
public/js/spa.js


二進制
public/js/story-compose.js


二進制
public/js/timeline.js


二進制
public/mix-manifest.json


+ 10 - 7
resources/assets/components/AccountImport.vue

@@ -107,7 +107,7 @@
                         <p v-else class="lead mb-0"><span class="font-weight-bold">{{ selectedPostsCounter }}</span> posts selected for import</p>
 
                         <button v-if="selectedMedia.length" class="btn btn-outline-danger font-weight-bold rounded-pill btn-sm my-1" @click="handleClearAll()">Clear all selected</button>
-                        <button v-else class="btn btn-outline-primary font-weight-bold rounded-pill" @click="handleSelectAll()">Select first 100 posts</button>
+                        <button v-else class="btn btn-outline-primary font-weight-bold rounded-pill" @click="handleSelectAll()">Select first {{ toggleLimit }} posts</button>
                     </div>
                 </section>
                 <section class="row mb-n5 media-selector" style="max-height: 600px;overflow-y: auto;">
@@ -233,7 +233,7 @@
             return {
                 page: 1,
                 step: 1,
-                toggleLimit: 100,
+                toggleLimit: 300,
                 config: {},
                 showDisabledWarning: false,
                 showNotAllowedWarning: false,
@@ -375,11 +375,14 @@
                 }
                 let res = json.filter(j => {
                     let ids = j.media.map(m => m.uri).filter(m => {
-                        if(this.config.allow_video_posts == true) {
-                            return m.endsWith('.png') || m.endsWith('.jpg') || m.endsWith('.mp4');
-                        } else {
-                            return m.endsWith('.png') || m.endsWith('.jpg');
+                        const supportedFormats = ['.png', '.jpg'];
+                        if (this.config.allow_video_posts) {
+                          supportedFormats.push('.mp4');
+                        }
+                        if (this.config.allow_image_webp) {
+                          supportedFormats.push('.webp');
                         }
+                        return supportedFormats.some(format => m.endsWith(format));
                     });
                     return ids.length;
                 }).filter(j => {
@@ -621,7 +624,7 @@
             },
 
             handleSelectAll() {
-                let medias = this.postMeta.slice(0, 100);
+                let medias = this.postMeta.slice(0, this.toggleLimit);
                 for (var i = medias.length - 1; i >= 0; i--) {
                     let m = medias[i];
                     this.toggleSelectedPost(m);

+ 434 - 433
resources/assets/components/Post.vue

@@ -1,443 +1,444 @@
 <template>
-	<div class="post-timeline-component web-wrapper">
-		<div v-if="isLoaded" class="container-fluid mt-3">
-			<div class="row">
-				<div class="col-md-4 col-lg-3 d-md-block">
-					<sidebar :user="user" />
-				</div>
-
-				<div class="col-md-8 col-lg-6">
-					<div v-if="isReply" class="p-3 rounded-top mb-n3" style="background-color: var(--card-header-accent)">
-						<p>
-							<i class="fal fa-reply mr-1"></i> In reply to
-
-							<a
-								:href="'/i/web/profile/' + reply.account.id"
-								class="font-weight-bold primary"
-								@click.prevent="goToProfile(reply.account)">
-								&commat;{{ reply.account.acct }}
-							</a>
-
-							<button
-								@click.prevent="goToPost(reply)"
-								class="btn btn-primary font-weight-bold btn-sm px-3 float-right rounded-pill">
-								View Post
-							</button>
-						</p>
-					</div>
-					<status
-						:key="post.id + ':fui:' + forceUpdateIdx"
-						:status="post"
-						:profile="user"
-						v-on:menu="openContextMenu()"
-						v-on:like="likeStatus()"
-						v-on:unlike="unlikeStatus()"
-						v-on:likes-modal="openLikesModal()"
-						v-on:shares-modal="openSharesModal()"
-						v-on:bookmark="handleBookmark()"
-						v-on:share="shareStatus()"
-						v-on:unshare="unshareStatus()"
-						v-on:follow="follow()"
-						v-on:unfollow="unfollow()"
-						v-on:counter-change="counterChange"
-						/>
-				</div>
-
-				<div class="d-none d-lg-block col-lg-3">
-					<rightbar />
-				</div>
-			</div>
-		</div>
-
-		<div v-if="postStateError" class="container-fluid mt-3">
-			<div class="row">
-				<div class="col-md-4 col-lg-3 d-md-block">
-					<sidebar :user="user" />
-				</div>
-				<div class="col-md-8 col-lg-6">
-					<div class="card card-body shadow-none border">
-						<div class="d-flex align-self-center flex-column" style="max-width: 500px;">
-							<p class="text-center">
-								<i class="far fa-exclamation-triangle fa-3x text-lighter"></i>
-							</p>
-							<p class="text-center lead font-weight-bold">Error displaying post</p>
-							<p class="mb-0">This can happen for a few reasons:</p>
-							<ul class="text-lighter">
-								<li>The url is invalid or has a typo</li>
-								<li>The page has been flagged for review by our automated abuse detection systems</li>
-								<li>The content may have been deleted</li>
-								<li>You do not have permission to view this content</li>
-							</ul>
-						</div>
-					</div>
-				</div>
-
-				<div class="d-none d-lg-block col-lg-3">
-					<rightbar />
-				</div>
-			</div>
-		</div>
-
-		<context-menu
-			v-if="isLoaded"
-			ref="contextMenu"
-			:status="post"
-			:profile="user"
-			@report-modal="handleReport()"
-			@delete="deletePost()"
-			v-on:edit="handleEdit"
-		/>
-
-		<likes-modal
-			v-if="showLikesModal"
-			ref="likesModal"
-			:status="post"
-			:profile="user"
-		/>
-
-		<shares-modal
-			v-if="showSharesModal"
-			ref="sharesModal"
-			:status="post"
-			:profile="profile"
-		/>
-
-		<report-modal
-			v-if="post"
-			ref="reportModal"
-			:status="post"
-		/>
-
-		<post-edit-modal
-			ref="editModal"
-			v-on:update="mergeUpdatedPost"
-		/>
-
-		<drawer />
-	</div>
+    <div class="post-timeline-component web-wrapper">
+        <div v-if="isLoaded" class="container-fluid mt-3">
+            <div class="row">
+                <div class="col-md-4 col-lg-3 d-md-block">
+                    <sidebar :user="user" />
+                </div>
+
+                <div class="col-md-8 col-lg-6">
+                    <div v-if="isReply" class="p-3 rounded-top mb-n3" style="background-color: var(--card-header-accent)">
+                        <p>
+                            <i class="fal fa-reply mr-1"></i> In reply to
+
+                            <a
+                                :href="'/i/web/profile/' + reply.account.id"
+                                class="font-weight-bold primary"
+                                @click.prevent="goToProfile(reply.account)">
+                                &commat;{{ reply.account.acct }}
+                            </a>
+
+                            <button
+                                @click.prevent="goToPost(reply)"
+                                class="btn btn-primary font-weight-bold btn-sm px-3 float-right rounded-pill">
+                                View Post
+                            </button>
+                        </p>
+                    </div>
+                    <status
+                        :key="post.id + ':fui:' + forceUpdateIdx"
+                        :status="post"
+                        :profile="user"
+                        v-on:menu="openContextMenu()"
+                        v-on:like="likeStatus()"
+                        v-on:unlike="unlikeStatus()"
+                        v-on:likes-modal="openLikesModal()"
+                        v-on:shares-modal="openSharesModal()"
+                        v-on:bookmark="handleBookmark()"
+                        v-on:share="shareStatus()"
+                        v-on:unshare="unshareStatus()"
+                        v-on:follow="follow()"
+                        v-on:unfollow="unfollow()"
+                        v-on:counter-change="counterChange"
+                        />
+                </div>
+
+                <div class="d-none d-lg-block col-lg-3">
+                    <rightbar />
+                </div>
+            </div>
+        </div>
+
+        <div v-if="postStateError" class="container-fluid mt-3">
+            <div class="row">
+                <div class="col-md-4 col-lg-3 d-md-block">
+                    <sidebar :user="user" />
+                </div>
+                <div class="col-md-8 col-lg-6">
+                    <div class="card card-body shadow-none border">
+                        <div class="d-flex align-self-center flex-column" style="max-width: 500px;">
+                            <p class="text-center">
+                                <i class="far fa-exclamation-triangle fa-3x text-lighter"></i>
+                            </p>
+                            <p class="text-center lead font-weight-bold">Error displaying post</p>
+                            <p class="mb-0">This can happen for a few reasons:</p>
+                            <ul class="text-lighter">
+                                <li>The url is invalid or has a typo</li>
+                                <li>The page has been flagged for review by our automated abuse detection systems</li>
+                                <li>The content may have been deleted</li>
+                                <li>You do not have permission to view this content</li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="d-none d-lg-block col-lg-3">
+                    <rightbar />
+                </div>
+            </div>
+        </div>
+
+        <context-menu
+            v-if="isLoaded"
+            ref="contextMenu"
+            :status="post"
+            :profile="user"
+            @report-modal="handleReport()"
+            @delete="deletePost()"
+            v-on:edit="handleEdit"
+        />
+
+        <likes-modal
+            v-if="showLikesModal"
+            ref="likesModal"
+            :status="post"
+            :profile="user"
+        />
+
+        <shares-modal
+            v-if="showSharesModal"
+            ref="sharesModal"
+            :status="post"
+            :profile="profile"
+        />
+
+        <report-modal
+            v-if="post"
+            ref="reportModal"
+            :status="post"
+        />
+
+        <post-edit-modal
+            ref="editModal"
+            v-on:update="mergeUpdatedPost"
+        />
+
+        <drawer />
+    </div>
 </template>
 
 <script type="text/javascript">
-	import Drawer from './partials/drawer.vue';
-	import Rightbar from './partials/rightbar.vue';
-	import Sidebar from './partials/sidebar.vue';
-	import Status from './partials/TimelineStatus.vue';
-	import ContextMenu from './partials/post/ContextMenu.vue';
-	import MediaContainer from './partials/post/MediaContainer.vue';
-	import LikesModal from './partials/post/LikeModal.vue';
-	import SharesModal from './partials/post/ShareModal.vue';
-	import ReportModal from './partials/modal/ReportPost.vue';
-	import PostEditModal from './partials/post/PostEditModal.vue';
-
-	export default {
-		props: {
-			cachedStatus: {
-				type: Object
-			},
-
-			cachedProfile: {
-				type: Object
-			}
-		},
-
-		components: {
-			"drawer": Drawer,
-			"sidebar": Sidebar,
-			"status": Status,
-			"context-menu": ContextMenu,
-			"media-container": MediaContainer,
-			"likes-modal": LikesModal,
-			"shares-modal": SharesModal,
-			"rightbar": Rightbar,
-			"report-modal": ReportModal,
+    import Drawer from './partials/drawer.vue';
+    import Rightbar from './partials/rightbar.vue';
+    import Sidebar from './partials/sidebar.vue';
+    import Status from './partials/TimelineStatus.vue';
+    import ContextMenu from './partials/post/ContextMenu.vue';
+    import MediaContainer from './partials/post/MediaContainer.vue';
+    import LikesModal from './partials/post/LikeModal.vue';
+    import SharesModal from './partials/post/ShareModal.vue';
+    import ReportModal from './partials/modal/ReportPost.vue';
+    import PostEditModal from './partials/post/PostEditModal.vue';
+
+    export default {
+        props: {
+            cachedStatus: {
+                type: Object
+            },
+
+            cachedProfile: {
+                type: Object
+            }
+        },
+
+        components: {
+            "drawer": Drawer,
+            "sidebar": Sidebar,
+            "status": Status,
+            "context-menu": ContextMenu,
+            "media-container": MediaContainer,
+            "likes-modal": LikesModal,
+            "shares-modal": SharesModal,
+            "rightbar": Rightbar,
+            "report-modal": ReportModal,
             "post-edit-modal": PostEditModal
-		},
-
-		data() {
-			return {
-				isLoaded: false,
-				user: undefined,
-				profile: undefined,
-				post: undefined,
-				relationship: {},
-				media: undefined,
-				mediaIndex: 0,
-				showLikesModal: false,
-				isReply: false,
-				reply: {},
-				showSharesModal: false,
-				postStateError: false,
-				forceUpdateIdx: 0
-			}
-		},
-
-		created() {
-			this.init();
-		},
-
-		watch: {
-			'$route': 'init'
-		},
-
-		methods: {
-			init() {
-				this.fetchSelf();
-			},
-
-			fetchSelf() {
-				this.user = window._sharedData.user;
-				this.fetchPost();
-			},
-
-			fetchPost() {
-				axios.get('/api/pixelfed/v1/statuses/'+this.$route.params.id)
-				.then(res => {
-					if(!res.data || !res.data.hasOwnProperty('id')) {
-						this.$router.push('/i/web/404');
-					}
-					if(!res.data.hasOwnProperty('account') || !res.data.account) {
-						this.postStateError = true;
-						return;
-					}
-					this.post = res.data;
-					this.media = this.post.media_attachments;
-					this.profile = this.post.account;
-					if(this.post.in_reply_to_id) {
-						this.fetchReply();
-					} else {
-						this.fetchRelationship();
-					}
-				}).catch(err => {
-					switch(err.response.status) {
-						case 403:
-						case 404:
-							this.$router.push('/i/web/404');
-						break;
-					}
-				})
-			},
-
-			fetchReply() {
-				axios.get('/api/pixelfed/v1/statuses/' + this.post.in_reply_to_id)
-				.then(res => {
-					this.reply = res.data;
-					this.isReply = true;
-					this.fetchRelationship();
-				})
-				.catch(err => {
-					this.fetchRelationship();
-				})
-			},
-
-			fetchRelationship() {
-				if(this.profile.id == this.user.id) {
-					this.relationship = {};
-					this.fetchState();
-					return;
-				}
-
-				axios.get('/api/pixelfed/v1/accounts/relationships', {
-					params: {
-						'id[]': this.profile.id
-					}
-				}).then(res => {
-					this.relationship = res.data[0];
-					this.fetchState();
-				});
-			},
-
-			fetchState() {
-				axios.get('/api/v2/statuses/'+this.post.id+'/state')
-				.then(res => {
-					this.post.favourited = res.data.liked;
-					this.post.reblogged = res.data.shared;
-					this.post.bookmarked = res.data.bookmarked;
-					if(!this.post.favourites_count && this.post.favourited) {
-						this.post.favourites_count = 1;
-					}
-					this.isLoaded = true;
-				}).catch(err => {
-					this.isLoaded = false;
-					this.postStateError = true;
-				})
-			},
-
-			goBack() {
-				this.$router.push('/i/web');
-			},
-
-			likeStatus() {
-				let count = this.post.favourites_count;
-				this.post.favourites_count = count + 1;
-				this.post.favourited = !this.post.favourited;
-
-				axios.post('/api/v1/statuses/' + this.post.id + '/favourite')
-				.then(res => {
-					//
-				}).catch(err => {
-					this.post.favourites_count = count;
-					this.post.favourited = false;
-				})
-			},
-
-			unlikeStatus() {
-				let count = this.post.favourites_count;
-				this.post.favourites_count = count - 1;
-				this.post.favourited = !this.post.favourited;
-
-				axios.post('/api/v1/statuses/' + this.post.id + '/unfavourite')
-				.then(res => {
-					//
-				}).catch(err => {
-					this.post.favourites_count = count;
-					this.post.favourited = false;
-				})
-			},
-
-			shareStatus() {
-				let count = this.post.reblogs_count;
-				this.post.reblogs_count = count + 1;
-				this.post.reblogged = !this.post.reblogged;
-
-				axios.post('/api/v1/statuses/' + this.post.id + '/reblog')
-				.then(res => {
-					//
-				}).catch(err => {
-					this.post.reblogs_count = count;
-					this.post.reblogged = false;
-				})
-			},
-
-			unshareStatus() {
-				let count = this.post.reblogs_count;
-				this.post.reblogs_count = count - 1;
-				this.post.reblogged = !this.post.reblogged;
-
-				axios.post('/api/v1/statuses/' + this.post.id + '/unreblog')
-				.then(res => {
-					//
-				}).catch(err => {
-					this.post.reblogs_count = count;
-					this.post.reblogged = false;
-				})
-			},
-
-			follow() {
-				axios.post('/api/v1/accounts/' + this.post.account.id + '/follow')
-				.then(res => {
-					this.$store.commit('updateRelationship', [res.data]);
-					this.user.following_count++;
-					this.post.account.followers_count++;
-				}).catch(err => {
-					swal('Oops!', 'An error occurred when attempting to follow this account.', 'error');
-					this.post.relationship.following = false;
-				});
-			},
-
-			unfollow() {
-				axios.post('/api/v1/accounts/' + this.post.account.id + '/unfollow')
-				.then(res => {
-					this.$store.commit('updateRelationship', [res.data]);
-					this.user.following_count--;
-					this.post.account.followers_count--;
-				}).catch(err => {
-					swal('Oops!', 'An error occurred when attempting to unfollow this account.', 'error');
-					this.post.relationship.following = true;
-				});
-			},
-
-			openContextMenu() {
-				this.$nextTick(() => {
-					this.$refs.contextMenu.open();
-				});
-			},
-
-			openLikesModal() {
-				this.showLikesModal = true;
-				this.$nextTick(() => {
-					this.$refs.likesModal.open();
-				});
-			},
-
-			openSharesModal() {
-				this.showSharesModal = true;
-				this.$nextTick(() => {
-					this.$refs.sharesModal.open();
-				});
-			},
-
-			deletePost() {
-				this.$router.push('/i/web');
-			},
-
-			goToPost(post) {
-				this.$router.push({
-					name: 'post',
-					path: `/i/web/post/${post.id}`,
-					params: {
-						id: post.id,
-						cachedStatus: post,
-						cachedProfile: this.user
-					}
-				})
-			},
-
-			goToProfile(account) {
-				this.$router.push({
-					name: 'profile',
-					path: `/i/web/profile/${account.id}`,
-					params: {
-						id: account.id,
-						cachedProfile: account,
-						cachedUser: this.user
-					}
-				})
-			},
-
-			handleBookmark() {
-				axios.post('/i/bookmark', {
-					item: this.post.id
-				})
-				.then(res => {
-					this.post.bookmarked = !this.post.bookmarked;
-				})
-				.catch(err => {
-					this.$bvToast.toast('Cannot bookmark post at this time.', {
-						title: 'Bookmark Error',
-						variant: 'danger',
-						autoHideDelay: 5000
-					});
-				});
-			},
-
-			handleReport() {
-				this.$nextTick(() => {
-					this.$refs.reportModal.open();
-				});
-			},
-
-			counterChange(type) {
-				switch(type) {
-					case 'comment-increment':
-						this.post.reply_count = this.post.reply_count + 1;
-					break;
-
-					case 'comment-decrement':
-						this.post.reply_count = this.post.reply_count - 1;
-					break;
-				}
-			},
-
-			handleEdit(status) {
-            	this.$refs.editModal.show(status);
+        },
+
+        data() {
+            return {
+                isLoaded: false,
+                user: undefined,
+                profile: undefined,
+                post: undefined,
+                relationship: {},
+                media: undefined,
+                mediaIndex: 0,
+                showLikesModal: false,
+                isReply: false,
+                reply: {},
+                showSharesModal: false,
+                postStateError: false,
+                forceUpdateIdx: 0
+            }
+        },
+
+        created() {
+            this.init();
+        },
+
+        watch: {
+            '$route': 'init'
+        },
+
+        methods: {
+            init() {
+                this.fetchSelf();
+            },
+
+            fetchSelf() {
+                this.user = window._sharedData.user;
+                this.isReply = false;
+                this.fetchPost();
+            },
+
+            fetchPost() {
+                axios.get('/api/pixelfed/v1/statuses/'+this.$route.params.id)
+                .then(res => {
+                    if(!res.data || !res.data.hasOwnProperty('id')) {
+                        this.$router.push('/i/web/404');
+                    }
+                    if(!res.data.hasOwnProperty('account') || !res.data.account) {
+                        this.postStateError = true;
+                        return;
+                    }
+                    this.post = res.data;
+                    this.media = this.post.media_attachments;
+                    this.profile = this.post.account;
+                    if(this.post.in_reply_to_id) {
+                        this.fetchReply();
+                    } else {
+                        this.fetchRelationship();
+                    }
+                }).catch(err => {
+                    switch(err.response.status) {
+                        case 403:
+                        case 404:
+                            this.$router.push('/i/web/404');
+                        break;
+                    }
+                })
+            },
+
+            fetchReply() {
+                axios.get('/api/pixelfed/v1/statuses/' + this.post.in_reply_to_id)
+                .then(res => {
+                    this.reply = res.data;
+                    this.isReply = true;
+                    this.fetchRelationship();
+                })
+                .catch(err => {
+                    this.fetchRelationship();
+                })
+            },
+
+            fetchRelationship() {
+                if(this.profile.id == this.user.id) {
+                    this.relationship = {};
+                    this.fetchState();
+                    return;
+                }
+
+                axios.get('/api/pixelfed/v1/accounts/relationships', {
+                    params: {
+                        'id[]': this.profile.id
+                    }
+                }).then(res => {
+                    this.relationship = res.data[0];
+                    this.fetchState();
+                });
+            },
+
+            fetchState() {
+                axios.get('/api/v2/statuses/'+this.post.id+'/state')
+                .then(res => {
+                    this.post.favourited = res.data.liked;
+                    this.post.reblogged = res.data.shared;
+                    this.post.bookmarked = res.data.bookmarked;
+                    if(!this.post.favourites_count && this.post.favourited) {
+                        this.post.favourites_count = 1;
+                    }
+                    this.isLoaded = true;
+                }).catch(err => {
+                    this.isLoaded = false;
+                    this.postStateError = true;
+                })
+            },
+
+            goBack() {
+                this.$router.push('/i/web');
+            },
+
+            likeStatus() {
+                let count = this.post.favourites_count;
+                this.post.favourites_count = count + 1;
+                this.post.favourited = !this.post.favourited;
+
+                axios.post('/api/v1/statuses/' + this.post.id + '/favourite')
+                .then(res => {
+                    //
+                }).catch(err => {
+                    this.post.favourites_count = count;
+                    this.post.favourited = false;
+                })
+            },
+
+            unlikeStatus() {
+                let count = this.post.favourites_count;
+                this.post.favourites_count = count - 1;
+                this.post.favourited = !this.post.favourited;
+
+                axios.post('/api/v1/statuses/' + this.post.id + '/unfavourite')
+                .then(res => {
+                    //
+                }).catch(err => {
+                    this.post.favourites_count = count;
+                    this.post.favourited = false;
+                })
+            },
+
+            shareStatus() {
+                let count = this.post.reblogs_count;
+                this.post.reblogs_count = count + 1;
+                this.post.reblogged = !this.post.reblogged;
+
+                axios.post('/api/v1/statuses/' + this.post.id + '/reblog')
+                .then(res => {
+                    //
+                }).catch(err => {
+                    this.post.reblogs_count = count;
+                    this.post.reblogged = false;
+                })
+            },
+
+            unshareStatus() {
+                let count = this.post.reblogs_count;
+                this.post.reblogs_count = count - 1;
+                this.post.reblogged = !this.post.reblogged;
+
+                axios.post('/api/v1/statuses/' + this.post.id + '/unreblog')
+                .then(res => {
+                    //
+                }).catch(err => {
+                    this.post.reblogs_count = count;
+                    this.post.reblogged = false;
+                })
+            },
+
+            follow() {
+                axios.post('/api/v1/accounts/' + this.post.account.id + '/follow')
+                .then(res => {
+                    this.$store.commit('updateRelationship', [res.data]);
+                    this.user.following_count++;
+                    this.post.account.followers_count++;
+                }).catch(err => {
+                    swal('Oops!', 'An error occurred when attempting to follow this account.', 'error');
+                    this.post.relationship.following = false;
+                });
+            },
+
+            unfollow() {
+                axios.post('/api/v1/accounts/' + this.post.account.id + '/unfollow')
+                .then(res => {
+                    this.$store.commit('updateRelationship', [res.data]);
+                    this.user.following_count--;
+                    this.post.account.followers_count--;
+                }).catch(err => {
+                    swal('Oops!', 'An error occurred when attempting to unfollow this account.', 'error');
+                    this.post.relationship.following = true;
+                });
+            },
+
+            openContextMenu() {
+                this.$nextTick(() => {
+                    this.$refs.contextMenu.open();
+                });
+            },
+
+            openLikesModal() {
+                this.showLikesModal = true;
+                this.$nextTick(() => {
+                    this.$refs.likesModal.open();
+                });
+            },
+
+            openSharesModal() {
+                this.showSharesModal = true;
+                this.$nextTick(() => {
+                    this.$refs.sharesModal.open();
+                });
+            },
+
+            deletePost() {
+                this.$router.push('/i/web');
+            },
+
+            goToPost(post) {
+                this.$router.push({
+                    name: 'post',
+                    path: `/i/web/post/${post.id}`,
+                    params: {
+                        id: post.id,
+                        cachedStatus: post,
+                        cachedProfile: this.user
+                    }
+                })
+            },
+
+            goToProfile(account) {
+                this.$router.push({
+                    name: 'profile',
+                    path: `/i/web/profile/${account.id}`,
+                    params: {
+                        id: account.id,
+                        cachedProfile: account,
+                        cachedUser: this.user
+                    }
+                })
+            },
+
+            handleBookmark() {
+                axios.post('/i/bookmark', {
+                    item: this.post.id
+                })
+                .then(res => {
+                    this.post.bookmarked = !this.post.bookmarked;
+                })
+                .catch(err => {
+                    this.$bvToast.toast('Cannot bookmark post at this time.', {
+                        title: 'Bookmark Error',
+                        variant: 'danger',
+                        autoHideDelay: 5000
+                    });
+                });
+            },
+
+            handleReport() {
+                this.$nextTick(() => {
+                    this.$refs.reportModal.open();
+                });
+            },
+
+            counterChange(type) {
+                switch(type) {
+                    case 'comment-increment':
+                        this.post.reply_count = this.post.reply_count + 1;
+                    break;
+
+                    case 'comment-decrement':
+                        this.post.reply_count = this.post.reply_count - 1;
+                    break;
+                }
+            },
+
+            handleEdit(status) {
+                this.$refs.editModal.show(status);
             },
 
             mergeUpdatedPost(post) {
-            	this.post = post;
-            	this.$nextTick(() => {
-            		this.forceUpdateIdx++;
-            	});
+                this.post = post;
+                this.$nextTick(() => {
+                    this.forceUpdateIdx++;
+                });
             }
-		}
-	}
+        }
+    }
 </script>

+ 5 - 1
resources/assets/components/admin/AdminSettings.vue

@@ -1025,6 +1025,10 @@
                         res += 'video/mp4'
                     }
 
+                    if(this.mediaTypes.avif) {
+                        res += 'image/avif'
+                    }
+
                     if(res.endsWith(',')) {
                         res = res.slice(0, -1);
                     }
@@ -1088,7 +1092,7 @@
                 if(types && types.length) {
                     types.forEach((type) => {
                         let mime = type.split('/')[1];
-                        if(['jpeg', 'png', 'gif', 'webp', 'mp4'].includes(mime)) {
+                        if(['jpeg', 'png', 'gif', 'webp', 'mp4', 'avif'].includes(mime)) {
                             this.mediaTypes[mime] = true;
                         }
                     })

+ 879 - 977
resources/assets/components/partials/navbar.vue

@@ -1,985 +1,887 @@
 <template>
-	<nav class="metro-nav navbar navbar-expand navbar-light navbar-laravel sticky-top shadow-none py-1">
-		<div class="container-fluid">
-				<a class="navbar-brand d-flex align-items-center" href="/i/web" title="Logo">
-					<img src="/img/pixelfed-icon-color.svg" height="30px" class="px-2" loading="eager" alt="Pixelfed logo">
-					<span class="font-weight-bold mb-0 d-none d-sm-block" style="font-size:20px;">
-						{{ brandName }}
-					</span>
-				</a>
-
-				<div class="collapse navbar-collapse">
-					<div class="navbar-nav ml-auto">
-					  <!-- <form class="form-inline search-bar" method="get" action="/i/results">
-						<input class="form-control" name="q" placeholder="Search ..." aria-label="search" autocomplete="off" required style="position: relative;line-height: 0.6;width:100%;min-width: 300px;max-width: 500px;border-radius: 8px;" role="search">
-					  </form> -->
-
-						<autocomplete
-							class="searchbox"
-							:search="autocompleteSearch"
-							:placeholder="$t('navmenu.search')"
-							aria-label="Search"
-							:get-result-value="getSearchResultValue"
-							:debounceTime="700"
-							@submit="onSearchSubmit"
-							ref="autocomplete">
-
-							<template #result="{ result, props }">
-								<li
-								v-bind="props"
-								class="autocomplete-result sr"
-								>
-									<div v-if="result.s_type === 'account'" class="media align-items-center my-0">
-										<img :src="result.avatar" width="40" height="40" class="sr-avatar" style="border-radius: 40px" onerror="this.src='/storage/avatars/default.png?v=0';this.onerror=null;">
-										<div class="media-body sr-account">
-											<div class="sr-account-acct" :class="{ compact: result.acct && result.acct.length > 24 }">
-												&commat;{{ result.acct }}
-												<b-button
-													v-if="result.locked"
-													v-b-tooltip.html
-													title="Private Account"
-													variant="link"
-													size="sm"
-													class="p-0"
-													>
-													<i class="far fa-lock fa-sm text-lighter ml-1"></i>
-												</b-button>
-											</div>
-											<template v-if="result.is_admin">
-												<div class="sr-account-stats">
-													<div class="sr-account-stats-followers text-danger font-weight-bold">
-														Admin
-													</div>
-													<div>·</div>
-													<div class="sr-account-stats-followers font-weight-bold">
-														<span>{{ formatCount(result.followers_count) }}</span>
-														<span>Followers</span>
-													</div>
-												</div>
-											</template>
-											<template v-else>
-												<template v-if="result.local">
-													<div class="sr-account-stats">
-														<div v-if="result.followers_count" class="sr-account-stats-followers font-weight-bold">
-															<span>{{ formatCount(result.followers_count) }}</span>
-															<span>Followers</span>
-														</div>
-														<div v-if="result.followers_count && result.statuses_count">·</div>
-														<div v-if="result.statuses_count" class="sr-account-stats-statuses font-weight-bold">
-															<span>{{ formatCount(result.statuses_count) }}</span>
-															<span>Posts</span>
-														</div>
-														<div v-if="!result.followers_count && result.statuses_count">·</div>
-														<div class="sr-account-stats-statuses font-weight-bold">
-															<i class="far fa-clock fa-sm"></i>
-															<span>{{ timeAgo(result.created_at) }}</span>
-														</div>
-													</div>
-												</template>
-												<template v-else>
-													<div class="sr-account-stats">
-														<div v-if="result.followers_count" class="sr-account-stats-followers font-weight-bold">
-															<span>{{ formatCount(result.followers_count) }}</span>
-															<span>Followers</span>
-														</div>
-														<div v-if="result.followers_count && result.statuses_count">·</div>
-														<div v-if="result.statuses_count" class="sr-account-stats-statuses font-weight-bold">
-															<span>{{ formatCount(result.statuses_count) }}</span>
-															<span>Posts</span>
-														</div>
-														<div v-if="!result.followers_count && result.statuses_count">·</div>
-
-														<div v-if="!result.followers_count && !result.statuses_count" class="sr-account-stats-statuses font-weight-bold">
-															Remote Account
-														</div>
-														<div v-if="!result.followers_count && !result.statuses_count">
-															·
-														</div>
-														<b-button
-															v-b-tooltip.html
-															:title="'Joined ' + timeAgo(result.created_at) + ' ago'"
-															variant="link"
-															size="sm"
-															class="sr-account-stats-statuses p-0"
-															>
-															<i class="far fa-clock fa-sm"></i>
-															<span class="font-weight-bold">{{ timeAgo(result.created_at) }}</span>
-														</b-button>
-													</div>
-												</template>
-											</template>
-										</div>
-									</div>
-
-									<div v-else-if="result.s_type === 'hashtag'" class="media align-items-center my-0">
-										<div class="media-icon">
-											<i class="far fa-hashtag fa-large"></i>
-										</div>
-										<div class="media-body sr-tag">
-											<div class="sr-tag-name" :class="{ compact: result.name && result.name.length > 26 }">
-												#{{ result.name }}
-											</div>
-											<div v-if="result.count && result.count > 100" class="sr-tag-count">
-												{{ formatCount(result.count) }} {{ result.count == 1 ? 'Post' : 'Posts' }}
-											</div>
-										</div>
-									</div>
-
-									<div v-else-if="result.s_type === 'status'" class="media align-items-center my-0">
-										<img :src="result.account.avatar"  width="40" height="40" class="sr-avatar" style="border-radius: 40px" onerror="this.src='/storage/avatars/default.png?v=0';this.onerror=null;">
-
-										<div class="media-body sr-post">
-											<div class="sr-post-acct" :class="{ compact: result.acct && result.acct.length > 26 }">
-												&commat;{{ truncate(result.account.acct, 20) }}
-												<b-button
-													v-if="result.locked"
-													v-b-tooltip.html
-													title="Private Account"
-													variant="link"
-													size="sm"
-													class="p-0"
-													>
-													<i class="far fa-lock fa-sm text-lighter ml-1"></i>
-												</b-button>
-											</div>
-											<div class="sr-post-action">
-												<div class="sr-post-action-timestamp">
-													<i class="far fa-clock fa-sm"></i>
-													{{ timeAgo(result.created_at)}}
-												</div>
-												<div>·</div>
-												<div class="sr-post-action-label">
-													Tap to view post
-												</div>
-											</div>
-										</div>
-									</div>
-								</li>
-							</template>
-						</autocomplete>
-
-					</div>
-					<div class="ml-auto">
-						<ul class="navbar-nav align-items-center">
-							<!-- <li class="nav-item px-md-2 d-none d-md-block">
-								<router-link class="nav-link font-weight-bold text-dark" to="/i/web" title="Home" data-toggle="tooltip" data-placement="bottom">
-									<i class="far fa-home fa-lg"></i>
-									<span class="sr-only">Home</span>
-								</router-link>
-							</li>
-							<li class="nav-item px-md-2 d-none d-md-block">
-								<router-link class="nav-link font-weight-bold text-dark" title="Compose" data-toggle="tooltip" data-placement="bottom" to="/i/web/compose">
-									<i class="far fa-plus-square fa-lg"></i>
-									<span class="sr-only">Compose</span>
-								</router-link>
-							</li> -->
-							<!-- <li class="nav-item px-md-2">
-								<router-link class="nav-link font-weight-bold text-dark" to="/i/web/direct" title="Direct" data-toggle="tooltip" data-placement="bottom">
-									<i class="far fa-comment-dots fa-lg"></i>
-									<span class="sr-only">Direct</span>
-								</router-link>
-							</li>
-							<li class="nav-item px-md-2 d-none d-md-block">
-								<router-link class="nav-link font-weight-bold text-dark fa-layers fa-fw" to="/i/web/notifications" title="Notifications" data-toggle="tooltip" data-placement="bottom">
-									<i class="far fa-bell fa-lg"></i>
-									<span class="fa-layers-counter" style="background:Tomato"></span>
-									<span class="sr-only">Notifications</span>
-								</router-link>
-							</li> -->
-							<li class="nav-item dropdown ml-2">
-								<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="User Menu">
-									<i class="d-none far fa-user fa-lg text-dark"></i>
-									<span class="sr-only">User Menu</span>
-									<img :src="user.avatar" class="nav-avatar rounded-circle border shadow" width="30" height="30" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
-								</a>
-
-								<div class="dropdown-menu dropdown-menu-right shadow" aria-labelledby="navbarDropdown">
-									<ul class="nav flex-column">
-										<li class="nav-item nav-icons">
-											<div class="d-flex justify-content-between align-items-center">
-												<router-link class="nav-link text-center" to="/i/web">
-													<div class="icon text-lighter"><i class="far fa-home fa-lg"></i></div>
-													<div class="small">{{ $t('navmenu.homeFeed') }}</div>
-												</router-link>
-
-												<router-link v-if="hasLocalTimeline" class="nav-link text-center" :to="{ name: 'timeline', params: { scope: 'local' } }">
-													<div class="icon text-lighter"><i class="fas fa-stream fa-lg"></i></div>
-													<div class="small">{{ $t('navmenu.localFeed') }}</div>
-												</router-link>
-
-												<router-link v-if="hasNetworkTimeline" class="nav-link text-center" :to="{ name: 'timeline', params: { scope: 'global' } }">
-													<div class="icon text-lighter"><i class="far fa-globe fa-lg"></i></div>
-													<div class="small">{{ $t('navmenu.globalFeed') }}</div>
-												</router-link>
-											</div>
-										</li>
-
-										<li class="nav-item nav-icons">
-											<div class="d-flex justify-content-between align-items-center">
-												<router-link class="nav-link text-center" to="/i/web/discover">
-													<div class="icon text-lighter"><i class="far fa-compass"></i></div>
-													<div class="small">{{ $t('navmenu.discover') }}</div>
-												</router-link>
-
-												<router-link class="nav-link text-center" to="/i/web/notifications">
-													<div class="icon text-lighter">
-														<i class="far fa-bell"></i>
-													</div>
-													<div class="small">
-														{{ $t('navmenu.notifications') }}
-													</div>
-												</router-link>
-
-												<router-link class="nav-link text-center px-3" :to="'/i/web/profile/' + user.id">
-													<div class="icon text-lighter">
-														<i class="far fa-user"></i>
-													</div>
-													<div class="small">{{ $t('navmenu.profile') }}</div>
-												</router-link>
-											</div>
-											<hr class="mb-0" style="margin-top: -5px;opacity: 0.4;" />
-										</li>
-
-										<li class="nav-item">
-											<router-link class="nav-link" to="/i/web/compose">
-												<span class="icon text-lighter"><i class="far fa-plus-square"></i></span>
-												{{ $t('navmenu.compose') }}
-											</router-link>
-										</li>
-
-										<!-- <li class="nav-item">
-											<router-link class="nav-link" to="/i/web/discover">
-												<span class="icon text-lighter"><i class="far fa-compass"></i></span>
-												{{ $t('navmenu.discover') }}
-											</router-link>
-										</li> -->
-
-										<li class="nav-item">
-											<router-link class="nav-link d-flex justify-content-between align-items-center" to="/i/web/direct">
-												<span>
-													<span class="icon text-lighter">
-														<i class="far fa-envelope"></i>
-													</span>
-													{{ $t('navmenu.directMessages') }}
-												</span>
-
-												<!-- <span class="badge badge-danger font-weight-light rounded-pill px-2" style="transform:scale(0.86)">99+</span> -->
-											</router-link>
-										</li>
-
-										<li class="nav-item">
-											<a class="nav-link" href="/i/web" @click.prevent="openUserInterfaceSettings">
-												<span class="icon text-lighter"><i class="far fa-brush"></i></span>
-												{{ $t('navmenu.appearance') }}
-											</a>
-										</li>
-
-										<li class="nav-item">
-											<router-link class="nav-link" to="/settings/home">
-												<span class="icon text-lighter"><i class="fa-cog"></i></span>
-												{{ $t('navmenu.settings') }}
-											</router-link>
-										</li>
-
-										<!-- <li class="nav-item">
-											<router-link class="nav-link d-flex justify-content-between align-items-center" to="/i/web/notifications">
-												<span>
-													<span class="icon text-lighter">
-														<i class="far fa-bell"></i>
-													</span>
-													{{ $t('navmenu.notifications') }}
-												</span>
-											</router-link>
-										</li> -->
-
-										<!-- <li class="nav-item">
-											<hr class="mt-n1" style="opacity: 0.4;margin-bottom: 0;" />
-
-											<router-link class="nav-link" :to="'/i/web/profile/' + user.id">
-												<span class="icon text-lighter">
-													<i class="far fa-user"></i>
-												</span>
-												{{ $t('navmenu.profile') }}
-											</router-link>
-										</li> -->
-										<li v-if="user.is_admin" class="nav-item">
-											<hr class="mt-n1" style="opacity: 0.4;margin-bottom: 0;" />
-											<a class="nav-link" href="/i/admin/dashboard">
-												<span class="icon text-lighter">
-													<i class="far fa-tools"></i>
-												</span>
-												{{ $t('navmenu.admin') }}
-											</a>
-										</li>
-
-										<li class="nav-item">
-											<hr class="mt-n1" style="opacity: 0.4;margin-bottom: 0;" />
-											<a class="nav-link" href="/">
-												<span class="icon text-lighter">
-													<i class="fas fa-chevron-left"></i>
-												</span>
-												{{ $t('navmenu.backToPreviousDesign') }}
-											</a>
-										</li>
-
-										<li class="nav-item">
-											<hr class="mt-n1" style="opacity: 0.4;margin-bottom: 0;" />
-											<a class="nav-link" href="/" @click.prevent="logout()">
-												<span class="icon text-lighter">
-													<i class="far fa-sign-out"></i>
-												</span>
-												{{ $t('navmenu.logout') }}
-											</a>
-										</li>
-									</ul>
-								</div>
-							</li>
-						</ul>
-					</div>
-				</div>
-		</div>
-
-		<b-modal
-			ref="uis"
-			hide-footer
-			centered
-			body-class="p-0 ui-menu"
-			title="$t('navmenu.appearance')">
-			<div class="list-group list-group-flush">
-				<div class="list-group-item px-3">
-					<div class="d-flex justify-content-between align-items-center">
-						<div>
-							<p class="font-weight-bold mb-1">{{ $t('appearance.theme') }}</p>
-							<p class="small text-muted mb-0"></p>
-						</div>
-
-						<div class="btn-group btn-group-sm">
-							<button
-								class="btn"
-								:class="[ uiColorScheme == 'system' ? 'btn-primary' : 'btn-outline-primary']"
-								@click="toggleUi('system')">
-								{{ $t('appearance.auto') }}
-							</button>
-							<button
-								class="btn"
-								:class="[ uiColorScheme == 'light' ? 'btn-primary' : 'btn-outline-primary']"
-								@click="toggleUi('light')">
-								{{ $t('appearance.lightMode') }}
-							</button>
-							<button
-								class="btn"
-								:class="[ uiColorScheme == 'dark' ? 'btn-primary' : 'btn-outline-primary']"
-								@click="toggleUi('dark')">
-								{{ $t('appearance.darkMode') }}
-							</button>
-						</div>
-					</div>
-				</div>
-
-				<div class="list-group-item px-3">
-					<div class="d-flex justify-content-between align-items-center">
-						<div>
-							<p class="font-weight-bold mb-1">{{ $t('appearance.profileLayout') }}</p>
-							<p class="small text-muted mb-0"></p>
-						</div>
-
-						<div class="btn-group btn-group-sm">
-							<button
-								class="btn"
-								:class="[ profileLayout == 'grid' ? 'btn-primary' : 'btn-outline-primary']"
-								@click="toggleProfileLayout('grid')">
-								{{ $t('appearance.grid') }}
-							</button>
-							<button
-								class="btn"
-								:class="[ profileLayout == 'masonry' ? 'btn-primary' : 'btn-outline-primary']"
-								@click="toggleProfileLayout('masonry')">
-								{{ $t('appearance.masonry') }}
-							</button>
-							<button
-								class="btn"
-								:class="[ profileLayout == 'feed' ? 'btn-primary' : 'btn-outline-primary']"
-								@click="toggleProfileLayout('feed')">
-								{{ $t('appearance.feed') }}
-							</button>
-						</div>
-					</div>
-				</div>
-
-				<div class="list-group-item px-3">
-					<div class="d-flex justify-content-between align-items-center">
-						<div>
-							<p class="font-weight-bold mb-0">{{ $t('appearance.compactPreviews') }}</p>
-						</div>
-						<b-form-checkbox v-model="fixedHeight" switch size="lg" />
-					</div>
-				</div>
-
-				<div class="list-group-item px-3">
-					<div class="d-flex justify-content-between align-items-center">
-						<div>
-							<p class="font-weight-bold mb-0">{{ $t('appearance.loadComments') }}</p>
-						</div>
-						<b-form-checkbox v-model="autoloadComments" switch size="lg" />
-					</div>
-				</div>
-
-				<div class="list-group-item px-3">
-					<div class="d-flex justify-content-between align-items-center">
-						<div>
-							<p class="font-weight-bold mb-0">{{ $t('appearance.hideStats') }}</p>
-						</div>
-						<b-form-checkbox v-model="hideCounts" switch size="lg" />
-					</div>
-				</div>
-
-			</div>
-		</b-modal>
-	</nav>
+    <nav class="metro-nav navbar navbar-expand navbar-light navbar-laravel sticky-top shadow-none py-1">
+        <div class="container-fluid">
+                <a class="navbar-brand d-flex align-items-center" href="/i/web" title="Logo">
+                    <img src="/img/pixelfed-icon-color.svg" height="30px" class="px-2" loading="eager" alt="Pixelfed logo">
+                    <span class="font-weight-bold mb-0 d-none d-sm-block" style="font-size:20px;">
+                        {{ brandName }}
+                    </span>
+                </a>
+
+                <div class="collapse navbar-collapse">
+                    <div class="navbar-nav ml-auto">
+                        <autocomplete
+                            class="searchbox"
+                            :search="autocompleteSearch"
+                            :placeholder="$t('navmenu.search')"
+                            aria-label="Search"
+                            :get-result-value="getSearchResultValue"
+                            :debounceTime="700"
+                            @submit="onSearchSubmit"
+                            ref="autocomplete">
+
+                            <template #result="{ result, props }">
+                                <li
+                                v-bind="props"
+                                class="autocomplete-result sr"
+                                >
+                                    <div v-if="result.s_type === 'account'" class="media align-items-center my-0">
+                                        <img :src="result.avatar" width="40" height="40" class="sr-avatar" style="border-radius: 40px" onerror="this.src='/storage/avatars/default.png?v=0';this.onerror=null;">
+                                        <div class="media-body sr-account">
+                                            <div class="sr-account-acct" :class="{ compact: result.acct && result.acct.length > 24 }">
+                                                &commat;{{ result.acct }}
+                                                <b-button
+                                                    v-if="result.locked"
+                                                    v-b-tooltip.html
+                                                    title="Private Account"
+                                                    variant="link"
+                                                    size="sm"
+                                                    class="p-0"
+                                                    >
+                                                    <i class="far fa-lock fa-sm text-lighter ml-1"></i>
+                                                </b-button>
+                                            </div>
+                                            <template v-if="result.is_admin">
+                                                <div class="sr-account-stats">
+                                                    <div class="sr-account-stats-followers text-danger font-weight-bold">
+                                                        Admin
+                                                    </div>
+                                                    <div>·</div>
+                                                    <div class="sr-account-stats-followers font-weight-bold">
+                                                        <span>{{ formatCount(result.followers_count) }}</span>
+                                                        <span>Followers</span>
+                                                    </div>
+                                                </div>
+                                            </template>
+                                            <template v-else>
+                                                <template v-if="result.local">
+                                                    <div class="sr-account-stats">
+                                                        <div v-if="result.followers_count" class="sr-account-stats-followers font-weight-bold">
+                                                            <span>{{ formatCount(result.followers_count) }}</span>
+                                                            <span>Followers</span>
+                                                        </div>
+                                                        <div v-if="result.followers_count && result.statuses_count">·</div>
+                                                        <div v-if="result.statuses_count" class="sr-account-stats-statuses font-weight-bold">
+                                                            <span>{{ formatCount(result.statuses_count) }}</span>
+                                                            <span>Posts</span>
+                                                        </div>
+                                                        <div v-if="!result.followers_count && result.statuses_count">·</div>
+                                                        <div class="sr-account-stats-statuses font-weight-bold">
+                                                            <i class="far fa-clock fa-sm"></i>
+                                                            <span>{{ timeAgo(result.created_at) }}</span>
+                                                        </div>
+                                                    </div>
+                                                </template>
+                                                <template v-else>
+                                                    <div class="sr-account-stats">
+                                                        <div v-if="result.followers_count" class="sr-account-stats-followers font-weight-bold">
+                                                            <span>{{ formatCount(result.followers_count) }}</span>
+                                                            <span>Followers</span>
+                                                        </div>
+                                                        <div v-if="result.followers_count && result.statuses_count">·</div>
+                                                        <div v-if="result.statuses_count" class="sr-account-stats-statuses font-weight-bold">
+                                                            <span>{{ formatCount(result.statuses_count) }}</span>
+                                                            <span>Posts</span>
+                                                        </div>
+                                                        <div v-if="!result.followers_count && result.statuses_count">·</div>
+
+                                                        <div v-if="!result.followers_count && !result.statuses_count" class="sr-account-stats-statuses font-weight-bold">
+                                                            Remote Account
+                                                        </div>
+                                                        <div v-if="!result.followers_count && !result.statuses_count">
+                                                            ·
+                                                        </div>
+                                                        <b-button
+                                                            v-b-tooltip.html
+                                                            :title="'Joined ' + timeAgo(result.created_at) + ' ago'"
+                                                            variant="link"
+                                                            size="sm"
+                                                            class="sr-account-stats-statuses p-0"
+                                                            >
+                                                            <i class="far fa-clock fa-sm"></i>
+                                                            <span class="font-weight-bold">{{ timeAgo(result.created_at) }}</span>
+                                                        </b-button>
+                                                    </div>
+                                                </template>
+                                            </template>
+                                        </div>
+                                    </div>
+
+                                    <div v-else-if="result.s_type === 'hashtag'" class="media align-items-center my-0">
+                                        <div class="media-icon">
+                                            <i class="far fa-hashtag fa-large"></i>
+                                        </div>
+                                        <div class="media-body sr-tag">
+                                            <div class="sr-tag-name" :class="{ compact: result.name && result.name.length > 26 }">
+                                                #{{ result.name }}
+                                            </div>
+                                            <div v-if="result.count && result.count > 100" class="sr-tag-count">
+                                                {{ formatCount(result.count) }} {{ result.count == 1 ? 'Post' : 'Posts' }}
+                                            </div>
+                                        </div>
+                                    </div>
+
+                                    <div v-else-if="result.s_type === 'status'" class="media align-items-center my-0">
+                                        <img :src="result.account.avatar"  width="40" height="40" class="sr-avatar" style="border-radius: 40px" onerror="this.src='/storage/avatars/default.png?v=0';this.onerror=null;">
+
+                                        <div class="media-body sr-post">
+                                            <div class="sr-post-acct" :class="{ compact: result.acct && result.acct.length > 26 }">
+                                                &commat;{{ truncate(result.account.acct, 20) }}
+                                                <b-button
+                                                    v-if="result.locked"
+                                                    v-b-tooltip.html
+                                                    title="Private Account"
+                                                    variant="link"
+                                                    size="sm"
+                                                    class="p-0"
+                                                    >
+                                                    <i class="far fa-lock fa-sm text-lighter ml-1"></i>
+                                                </b-button>
+                                            </div>
+                                            <div class="sr-post-action">
+                                                <div class="sr-post-action-timestamp">
+                                                    <i class="far fa-clock fa-sm"></i>
+                                                    {{ timeAgo(result.created_at)}}
+                                                </div>
+                                                <div>·</div>
+                                                <div class="sr-post-action-label">
+                                                    Tap to view post
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </li>
+                            </template>
+                        </autocomplete>
+
+                    </div>
+                    <div class="ml-auto">
+                        <ul class="navbar-nav align-items-center">
+                            <li class="nav-item dropdown ml-2">
+                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="User Menu">
+                                    <i class="d-none far fa-user fa-lg text-dark"></i>
+                                    <span class="sr-only">User Menu</span>
+                                    <img :src="user.avatar" class="nav-avatar rounded-circle border shadow" width="30" height="30" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
+                                </a>
+
+                                <div class="dropdown-menu dropdown-menu-right shadow" aria-labelledby="navbarDropdown">
+                                    <ul class="nav flex-column">
+                                        <li class="nav-item nav-icons">
+                                            <div class="d-flex justify-content-between align-items-center">
+                                                <router-link class="nav-link text-center" to="/i/web">
+                                                    <div class="icon text-lighter"><i class="far fa-home fa-lg"></i></div>
+                                                    <div class="small">{{ $t('navmenu.homeFeed') }}</div>
+                                                </router-link>
+
+                                                <router-link v-if="hasLocalTimeline" class="nav-link text-center" :to="{ name: 'timeline', params: { scope: 'local' } }">
+                                                    <div class="icon text-lighter"><i class="fas fa-stream fa-lg"></i></div>
+                                                    <div class="small">{{ $t('navmenu.localFeed') }}</div>
+                                                </router-link>
+
+                                                <router-link v-if="hasNetworkTimeline" class="nav-link text-center" :to="{ name: 'timeline', params: { scope: 'global' } }">
+                                                    <div class="icon text-lighter"><i class="far fa-globe fa-lg"></i></div>
+                                                    <div class="small">{{ $t('navmenu.globalFeed') }}</div>
+                                                </router-link>
+                                            </div>
+                                        </li>
+
+                                        <li class="nav-item nav-icons">
+                                            <div class="d-flex justify-content-between align-items-center">
+                                                <router-link class="nav-link text-center" to="/i/web/discover">
+                                                    <div class="icon text-lighter"><i class="far fa-compass"></i></div>
+                                                    <div class="small">{{ $t('navmenu.discover') }}</div>
+                                                </router-link>
+
+                                                <router-link class="nav-link text-center" to="/i/web/notifications">
+                                                    <div class="icon text-lighter">
+                                                        <i class="far fa-bell"></i>
+                                                    </div>
+                                                    <div class="small">
+                                                        {{ $t('navmenu.notifications') }}
+                                                    </div>
+                                                </router-link>
+
+                                                <router-link class="nav-link text-center px-3" :to="'/i/web/profile/' + user.id">
+                                                    <div class="icon text-lighter">
+                                                        <i class="far fa-user"></i>
+                                                    </div>
+                                                    <div class="small">{{ $t('navmenu.profile') }}</div>
+                                                </router-link>
+                                            </div>
+                                            <hr class="mb-0" style="margin-top: -5px;opacity: 0.4;" />
+                                        </li>
+
+                                        <li class="nav-item">
+                                            <router-link class="nav-link" to="/i/web/compose">
+                                                <span class="icon text-lighter"><i class="far fa-plus-square"></i></span>
+                                                {{ $t('navmenu.compose') }}
+                                            </router-link>
+                                        </li>
+
+                                        <li class="nav-item">
+                                            <router-link class="nav-link d-flex justify-content-between align-items-center" to="/i/web/direct">
+                                                <span>
+                                                    <span class="icon text-lighter">
+                                                        <i class="far fa-envelope"></i>
+                                                    </span>
+                                                    {{ $t('navmenu.directMessages') }}
+                                                </span>
+                                            </router-link>
+                                        </li>
+
+                                        <li class="nav-item">
+                                            <a class="nav-link" href="/i/web" @click.prevent="openUserInterfaceSettings">
+                                                <span class="icon text-lighter"><i class="far fa-brush"></i></span>
+                                                {{ $t('navmenu.appearance') }}
+                                            </a>
+                                        </li>
+
+                                        <li class="nav-item">
+                                            <router-link class="nav-link" to="/settings/home">
+                                                <span class="icon text-lighter"><i class="far fa-cog"></i></span>
+                                                {{ $t('navmenu.settings') }}
+                                            </router-link>
+                                        </li>
+
+                                        <li v-if="user.is_admin" class="nav-item">
+                                            <hr class="mt-n1" style="opacity: 0.4;margin-bottom: 0;" />
+                                            <a class="nav-link" href="/i/admin/dashboard">
+                                                <span class="icon text-lighter">
+                                                    <i class="far fa-tools"></i>
+                                                </span>
+                                                {{ $t('navmenu.admin') }}
+                                            </a>
+                                        </li>
+
+                                        <li class="nav-item">
+                                            <hr class="mt-n1" style="opacity: 0.4;margin-bottom: 0;" />
+                                            <a class="nav-link" href="/" @click.prevent="logout()">
+                                                <span class="icon text-lighter">
+                                                    <i class="far fa-sign-out"></i>
+                                                </span>
+                                                {{ $t('navmenu.logout') }}
+                                            </a>
+                                        </li>
+                                    </ul>
+                                </div>
+                            </li>
+                        </ul>
+                    </div>
+                </div>
+        </div>
+
+        <b-modal
+            ref="uis"
+            hide-footer
+            centered
+            body-class="p-0 ui-menu"
+            title="$t('navmenu.appearance')">
+            <div class="list-group list-group-flush">
+                <div class="list-group-item px-3">
+                    <div class="d-flex justify-content-between align-items-center">
+                        <div>
+                            <p class="font-weight-bold mb-1">{{ $t('appearance.theme') }}</p>
+                            <p class="small text-muted mb-0"></p>
+                        </div>
+
+                        <div class="btn-group btn-group-sm">
+                            <button
+                                class="btn"
+                                :class="[ uiColorScheme == 'system' ? 'btn-primary' : 'btn-outline-primary']"
+                                @click="toggleUi('system')">
+                                {{ $t('appearance.auto') }}
+                            </button>
+                            <button
+                                class="btn"
+                                :class="[ uiColorScheme == 'light' ? 'btn-primary' : 'btn-outline-primary']"
+                                @click="toggleUi('light')">
+                                {{ $t('appearance.lightMode') }}
+                            </button>
+                            <button
+                                class="btn"
+                                :class="[ uiColorScheme == 'dark' ? 'btn-primary' : 'btn-outline-primary']"
+                                @click="toggleUi('dark')">
+                                {{ $t('appearance.darkMode') }}
+                            </button>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="list-group-item px-3">
+                    <div class="d-flex justify-content-between align-items-center">
+                        <div>
+                            <p class="font-weight-bold mb-1">{{ $t('appearance.profileLayout') }}</p>
+                            <p class="small text-muted mb-0"></p>
+                        </div>
+
+                        <div class="btn-group btn-group-sm">
+                            <button
+                                class="btn"
+                                :class="[ profileLayout == 'grid' ? 'btn-primary' : 'btn-outline-primary']"
+                                @click="toggleProfileLayout('grid')">
+                                {{ $t('appearance.grid') }}
+                            </button>
+                            <button
+                                class="btn"
+                                :class="[ profileLayout == 'masonry' ? 'btn-primary' : 'btn-outline-primary']"
+                                @click="toggleProfileLayout('masonry')">
+                                {{ $t('appearance.masonry') }}
+                            </button>
+                            <button
+                                class="btn"
+                                :class="[ profileLayout == 'feed' ? 'btn-primary' : 'btn-outline-primary']"
+                                @click="toggleProfileLayout('feed')">
+                                {{ $t('appearance.feed') }}
+                            </button>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="list-group-item px-3">
+                    <div class="d-flex justify-content-between align-items-center">
+                        <div>
+                            <p class="font-weight-bold mb-0">{{ $t('appearance.compactPreviews') }}</p>
+                        </div>
+                        <b-form-checkbox v-model="fixedHeight" switch size="lg" />
+                    </div>
+                </div>
+
+                <div class="list-group-item px-3">
+                    <div class="d-flex justify-content-between align-items-center">
+                        <div>
+                            <p class="font-weight-bold mb-0">{{ $t('appearance.loadComments') }}</p>
+                        </div>
+                        <b-form-checkbox v-model="autoloadComments" switch size="lg" />
+                    </div>
+                </div>
+
+                <div class="list-group-item px-3">
+                    <div class="d-flex justify-content-between align-items-center">
+                        <div>
+                            <p class="font-weight-bold mb-0">{{ $t('appearance.hideStats') }}</p>
+                        </div>
+                        <b-form-checkbox v-model="hideCounts" switch size="lg" />
+                    </div>
+                </div>
+
+            </div>
+        </b-modal>
+    </nav>
 </template>
 
 <script type="text/javascript">
-	import Autocomplete from '@trevoreyre/autocomplete-vue'
-	import '@trevoreyre/autocomplete-vue/dist/style.css'
-
-	export default {
-		components: {
-			Autocomplete
-		},
-
-		data() {
-			return {
-				brandName: 'pixelfed',
-				user: window._sharedData.user,
-				profileLayoutModel: 'grid',
-				hasLocalTimeline: true,
-				hasNetworkTimeline: false
-			}
-		},
-
-		computed: {
-			profileLayout: {
-				get() {
-					return this.$store.state.profileLayout;
-				},
-
-				set(val) {
-					this.$store.commit('setProfileLayout', val);
-				}
-			},
-
-			hideCounts: {
-				get() {
-					return this.$store.state.hideCounts;
-				},
-
-				set(val) {
-					this.$store.commit('setHideCounts', val);
-				}
-			},
-			autoloadComments: {
-				get() {
-					return this.$store.state.autoloadComments;
-				},
-
-				set(val) {
-					this.$store.commit('setAutoloadComments', val);
-				}
-			},
-			newReactions: {
-				get() {
-					return this.$store.state.newReactions;
-				},
-
-				set(val) {
-					this.$store.commit('setNewReactions', val);
-				}
-			},
-
-			fixedHeight: {
-				get() {
-					return this.$store.state.fixedHeight;
-				},
-
-				set(val) {
-					this.$store.commit('setFixedHeight', val);
-				}
-			},
-
-			uiColorScheme: {
-				get() {
-					return this.$store.state.colorScheme;
-				},
-
-				set(val) {
-					this.$store.commit('setColorScheme', val);
-				}
-			}
-		},
-
-		mounted() {
-			if(window.App.config.features.hasOwnProperty('timelines')) {
-				this.hasLocalTimeline = App.config.features.timelines.local;
-				this.hasNetworkTimeline = App.config.features.timelines.network;
-			}
-
-			let u = new URLSearchParams(window.location.search);
-			if(u.has('q') && u.get('q') && u.has('src') && u.get('src') === 'ac') {
-				this.$refs.autocomplete.setValue(u.get('q'));
-				setTimeout(() => {
-					let ai = document.querySelector('.autocomplete-input')
-					ai.focus();
-				}, 1000)
-			}
-
-			this.brandName = window.App.config.site.name;
-		},
-
-		methods: {
-			autocompleteSearch(q) {
-				if (!q || q.length < 2) {
-					return [];
-				}
-
-				let resolve = q.startsWith('https://') || q.startsWith('@');
-
-				return axios.get('/api/v2/search', {
-					params: {
-						q: q,
-						resolve: resolve,
-						'_pe': 1
-					}
-				}).then(res => {
-					let results = [];
-					let accounts = res.data.accounts.map(res => {
-						let account = res;
-						account.s_type = 'account';
-						return account;
-					});
-					let hashtags = res.data.hashtags.map(res => {
-						let tag = res;
-						tag.s_type = 'hashtag';
-						return tag;
-					})
-					// let statuses = res.data.statuses.map(res => {
-					// 	let status = res;
-					// 	status.s_type = 'status';
-					// 	return status;
-					// });
-
-					// results.push(...statuses.slice(0,5));
-					results.push(...accounts.slice(0,5));
-					results.push(...hashtags.slice(0,5));
-
-					if(res.data.statuses) {
-						if(Array.isArray(res.data.statuses)) {
-							let statuses = res.data.statuses.map(res => {
-								let status = res;
-								status.s_type = 'status';
-								return status;
-							});
-							results.push(...statuses);
-						} else {
-							if(q === res.data.statuses.url) {
-								this.$refs.autocomplete.value = '';
-
-								this.$router.push({
-									name: 'post',
-									path: `/i/web/post/${res.data.statuses.id}`,
-									params: {
-										id: res.data.statuses.id,
-										cachedStatus: res.data.statuses,
-										cachedProfile: this.user
-									}
-								});
-							}
-						}
-					}
-					return results;
-				});
-			},
-
-			getSearchResultValue(result) {
-				return result;
-			},
-
-			onSearchSubmit(result) {
-				if (result.length < 1) {
-					return;
-				}
-				this.$refs.autocomplete.value = '';
-				switch(result.s_type) {
-					case 'account':
-						// this.$router.push({
-						// 	name: 'profile',
-						// 	path: `/i/web/profile/${result.id}`,
-						// 	params: {
-						// 		id: result.id,
-						// 		cachedProfile: result,
-						// 		cachedUser: this.user
-						// 	}
-						// });
-						location.href = `/i/web/profile/${result.id}`;
-					break;
-
-					case 'hashtag':
-						// this.$router.push({
-						// 	name: 'hashtag',
-						// 	path: `/i/web/hashtag/${result.name}`,
-						// 	params: {
-						// 		id: result.name,
-						// 	}
-						// });
-						location.href = `/i/web/hashtag/${result.name}`;
-					break;
-
-					case 'status':
-						// this.$router.push({
-						// 	name: 'post',
-						// 	path: `/i/web/post/${result.id}`,
-						// 	params: {
-						// 		id: result.id,
-						// 	}
-						// });
-						location.href = `/i/web/post/${result.id}`;
-					break;
-				}
-			},
-
-			truncate(text, limit = 30) {
-				if(text.length <= limit) {
-					return text;
-				}
-
-				return text.slice(0, limit) + '...'
-			},
-
-			timeAgo(ts) {
-				return window.App.util.format.timeAgo(ts);
-			},
-
-			formatCount(val) {
-				if(!val) {
-					return 0;
-				}
-
-				return new Intl.NumberFormat('en-CA', { notation: 'compact' , compactDisplay: "short" }).format(val);
-			},
-
-			logout() {
-				axios.post('/logout')
-				.then(res => {
-					location.href = '/';
-				}).catch(err => {
-					location.href = '/';
-				})
-			},
-
-			openUserInterfaceSettings() {
-				event.currentTarget.blur();
-				this.$refs.uis.show();
-			},
-
-			toggleUi(ui) {
-				event.currentTarget.blur();
-				this.uiColorScheme = ui;
-			},
-
-			toggleProfileLayout(layout) {
-				event.currentTarget.blur();
-				this.profileLayout = layout;
-			}
-		}
-	}
+    import Autocomplete from '@trevoreyre/autocomplete-vue'
+    import '@trevoreyre/autocomplete-vue/dist/style.css'
+
+    export default {
+        components: {
+            Autocomplete
+        },
+
+        data() {
+            return {
+                brandName: 'pixelfed',
+                user: window._sharedData.user,
+                profileLayoutModel: 'grid',
+                hasLocalTimeline: true,
+                hasNetworkTimeline: false
+            }
+        },
+
+        computed: {
+            profileLayout: {
+                get() {
+                    return this.$store.state.profileLayout;
+                },
+
+                set(val) {
+                    this.$store.commit('setProfileLayout', val);
+                }
+            },
+
+            hideCounts: {
+                get() {
+                    return this.$store.state.hideCounts;
+                },
+
+                set(val) {
+                    this.$store.commit('setHideCounts', val);
+                }
+            },
+            autoloadComments: {
+                get() {
+                    return this.$store.state.autoloadComments;
+                },
+
+                set(val) {
+                    this.$store.commit('setAutoloadComments', val);
+                }
+            },
+            newReactions: {
+                get() {
+                    return this.$store.state.newReactions;
+                },
+
+                set(val) {
+                    this.$store.commit('setNewReactions', val);
+                }
+            },
+
+            fixedHeight: {
+                get() {
+                    return this.$store.state.fixedHeight;
+                },
+
+                set(val) {
+                    this.$store.commit('setFixedHeight', val);
+                }
+            },
+
+            uiColorScheme: {
+                get() {
+                    return this.$store.state.colorScheme;
+                },
+
+                set(val) {
+                    this.$store.commit('setColorScheme', val);
+                }
+            }
+        },
+
+        mounted() {
+            if(window.App.config.features.hasOwnProperty('timelines')) {
+                this.hasLocalTimeline = App.config.features.timelines.local;
+                this.hasNetworkTimeline = App.config.features.timelines.network;
+            }
+
+            let u = new URLSearchParams(window.location.search);
+            if(u.has('q') && u.get('q') && u.has('src') && u.get('src') === 'ac') {
+                this.$refs.autocomplete.setValue(u.get('q'));
+                setTimeout(() => {
+                    let ai = document.querySelector('.autocomplete-input')
+                    ai.focus();
+                }, 1000)
+            }
+
+            this.brandName = window.App.config.site.name;
+        },
+
+        methods: {
+            autocompleteSearch(q) {
+                if (!q || q.length < 2) {
+                    return [];
+                }
+
+                let resolve = q.startsWith('https://') || q.startsWith('@');
+
+                return axios.get('/api/v2/search', {
+                    params: {
+                        q: q,
+                        resolve: resolve,
+                        '_pe': 1
+                    }
+                }).then(res => {
+                    let results = [];
+                    let accounts = res.data.accounts.map(res => {
+                        let account = res;
+                        account.s_type = 'account';
+                        return account;
+                    });
+                    let hashtags = res.data.hashtags.map(res => {
+                        let tag = res;
+                        tag.s_type = 'hashtag';
+                        return tag;
+                    })
+
+                    results.push(...accounts.slice(0,5));
+                    results.push(...hashtags.slice(0,5));
+
+                    if(res.data.statuses) {
+                        if(Array.isArray(res.data.statuses)) {
+                            let statuses = res.data.statuses.map(res => {
+                                let status = res;
+                                status.s_type = 'status';
+                                return status;
+                            });
+                            results.push(...statuses);
+                        } else {
+                            if(q === res.data.statuses.url) {
+                                this.$refs.autocomplete.value = '';
+
+                                this.$router.push({
+                                    name: 'post',
+                                    path: `/i/web/post/${res.data.statuses.id}`,
+                                    params: {
+                                        id: res.data.statuses.id,
+                                        cachedStatus: res.data.statuses,
+                                        cachedProfile: this.user
+                                    }
+                                });
+                            }
+                        }
+                    }
+                    return results;
+                });
+            },
+
+            getSearchResultValue(result) {
+                return result;
+            },
+
+            onSearchSubmit(result) {
+                if (result.length < 1) {
+                    return;
+                }
+                this.$refs.autocomplete.value = '';
+                switch(result.s_type) {
+                    case 'account':
+                        location.href = `/i/web/profile/${result.id}`;
+                    break;
+
+                    case 'hashtag':
+                        location.href = `/i/web/hashtag/${result.name}`;
+                    break;
+
+                    case 'status':
+                        location.href = `/i/web/post/${result.id}`;
+                    break;
+                }
+            },
+
+            truncate(text, limit = 30) {
+                if(text.length <= limit) {
+                    return text;
+                }
+
+                return text.slice(0, limit) + '...'
+            },
+
+            timeAgo(ts) {
+                return window.App.util.format.timeAgo(ts);
+            },
+
+            formatCount(val) {
+                if(!val) {
+                    return 0;
+                }
+
+                return new Intl.NumberFormat('en-CA', { notation: 'compact' , compactDisplay: "short" }).format(val);
+            },
+
+            logout() {
+                axios.post('/logout')
+                .then(res => {
+                    location.href = '/';
+                }).catch(err => {
+                    location.href = '/';
+                })
+            },
+
+            openUserInterfaceSettings() {
+                event.currentTarget.blur();
+                this.$refs.uis.show();
+            },
+
+            toggleUi(ui) {
+                event.currentTarget.blur();
+                this.uiColorScheme = ui;
+            },
+
+            toggleProfileLayout(layout) {
+                event.currentTarget.blur();
+                this.profileLayout = layout;
+            }
+        }
+    }
 </script>
 
 <style lang="scss">
-	.metro-nav {
-		z-index: 4;
-
-		.dropdown-menu {
-			min-width: 18rem;
-			padding: 0;
-			border: none;
-
-			.nav {
-				overflow: auto;
-			}
-
-			.nav-item {
-				.nav-link {
-					font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
-					font-weight: 500;
-					color: rgba(156,163,175, 1);
-					padding-left: 14px;
-					margin-bottom: 5px;
-
-					.icon {
-						display: inline-block;
-						width: 40px;
-						text-align: center;
-					}
-
-				}
-
-				.router-link-exact-active {
-					color: var(--primary);
-					font-weight: 700;
-					padding-left: 14px;
-
-					&:not(.text-center) {
-						padding-left: 10px;
-						border-left: 4px solid var(--primary);
-					}
-
-					.icon {
-						color: var(--primary) !important;
-					}
-				}
-
-				&.nav-icons {
-					.small {
-						font-weight: 700 !important;
-					}
-				}
-
-				&:is(:last-child) {
-					.nav-link {
-						margin-bottom: 0;
-						border-bottom-left-radius: 15px;
-						border-bottom-right-radius: 15px;
-					}
-				}
-			}
-		}
-
-		.fa-layers {
-			display: inline-block;
-			height: 1em;
-			position: relative;
-			text-align: center;
-			vertical-align: -0.125em;
-			width: 1em;
-
-			.fa-layers-counter {
-				background-color: #ff253a;
-				border-radius: 1em;
-				-webkit-box-sizing: border-box;
-				box-sizing: border-box;
-				color: #fff;
-				height: 1.5em;
-				line-height: 1;
-				max-width: 5em;
-				min-width: 1.5em;
-				overflow: hidden;
-				padding: 0.25em;
-				right: 0;
-				text-overflow: ellipsis;
-				top: 0;
-				transform: scale(.5);
-				-webkit-transform-origin: top right;
-				transform-origin: top right;
-
-				display: inline-block;
-			    position: absolute;
-
-			    margin-right: -5px;
-			    margin-top: -10px;
-			}
-
-			.far {
-				bottom: 0;
-				left: 0;
-				margin: auto;
-				position: absolute;
-				right: 0;
-				top: 0;
-			}
-		}
-
-		.searchbox {
-			@media (min-width: 768px) {
-				width: 300px;
-			}
-		}
-
-		.nav-avatar {
-			@media (min-width: 768px) {
-				width: 50px;
-				height: 50px;
-			}
-		}
-
-		.autocomplete[data-loading="true"]::after {
-			content: "";
-			border-right: 3px solid var(--primary);
-		}
-
-		.autocomplete {
-			&-input {
-				padding: 0.375rem 0.75rem 0.375rem 2.6rem;
-				background-color: var(--light-gray);
-				font-size: 0.9rem;
-				border-radius: 50rem;
-				background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjQjhDMkNDIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iNSIvPjxwYXRoIGQ9Ik0xOSAxOWwtNC00Ii8+PC9zdmc+");
-				box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%) !important;
-			}
-
-			&-result {
-				background-image: none;
-				padding: 10px 12px;
-				cursor: pointer;
-
-				&-list {
-					box-shadow: 0 0.125rem 0.45rem var(--border-color);
-					-ms-overflow-style: none;
-					scrollbar-width: none;
-
-					&::-webkit-scrollbar {
-						width: 0 !important
-					}
-				}
-
-				.media-icon {
-					display: flex;
-					justify-content: center;
-					align-items: center;
-					width: 40px;
-					height: 40px;
-					margin-right: 12px;
-					background: var(--light-gray);
-					border: 1px solid var(--input-border);
-					border-radius: 40px;
-					box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%);
-				}
-
-			}
-		}
-
-		.sr {
-			&:not(:last-child) {
-				border-bottom: 1px solid var(--input-border);
-			}
-
-			&-avatar {
-				margin-right: 12px;
-				box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075)
-			}
-
-			&-account {
-				display: flex;
-				flex-direction: column;
-				align-items: flex-start;
-				justify-content: center;
-				gap: 3px;
-
-				&-acct {
-					word-wrap: break-word;
-					word-break: break-all;
-					font-size: 14px;
-					line-height: 18px;
-					font-weight: bold;
-					color: var(--dark);
-					margin-right: 1rem;
-
-					&.compact {
-						font-size: 12px;
-					}
-				}
-
-				&-stats {
-					display: flex;
-					align-items: center;
-					gap: 5px;
-					line-height: 14px;
-
-					&-followers,
-					&-statuses {
-						font-size: 11px;
-						font-weight: 500;
-						color: var(--text-lighter);
-					}
-				}
-			}
-
-			&-tag {
-				display: flex;
-				flex-direction: column;
-				align-items: flex-start;
-				justify-content: center;
-				gap: 3px;
-
-				&-name {
-					word-wrap: break-word;
-					word-break: break-all;
-					font-size: 14px;
-					line-height: 18px;
-					font-weight: bold;
-					color: var(--dark);
-					margin-right: 1rem;
-
-					&.compact {
-						font-size: 12px;
-					}
-				}
-
-				&-count {
-					font-size: 11px;
-					line-height: 13px;
-					color: var(--text-lighter);
-					font-weight: bold;
-				}
-			}
-
-			&-post {
-				display: flex;
-				flex-direction: column;
-				align-items: flex-start;
-				justify-content: center;
-				gap: 3px;
-
-				&-acct {
-					font-size: 14px;
-					line-height: 18px;
-					font-weight: bold;
-					color: var(--dark);
-				}
-
-				&-action {
-					display: flex;
-					font-size: 11px;
-					line-height: 14px;
-					color: var(--text-lighter);
-					font-weight: 500;
-					gap: 3px;
-					align-items: center;
-
-					&-timestamp {
-						font-weight: 700;
-					}
-
-					&-label {
-						font-weight: 700;
-					}
-				}
-			}
-		}
-	}
-
-	.force-dark-mode {
-		.autocomplete-result-list {
-			border-color: var(--input-border);
-		}
-
-		.autocomplete-result:hover, .autocomplete-result[aria-selected=true] {
-			box-shadow: 0;
-    		background-color: rgba(255, 255, 255, .1);
-		}
-
-		.autocomplete[data-loading="true"]::after {
-			content: "";
-			border: 3px solid rgba(255, 255, 255, 0.22);
-			border-right: 3px solid var(--primary);
-		}
-	}
+    .metro-nav {
+        z-index: 4;
+
+        .dropdown-menu {
+            min-width: 18rem;
+            padding: 0;
+            border: none;
+
+            .nav {
+                overflow: auto;
+            }
+
+            .nav-item {
+                .nav-link {
+                    font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
+                    font-weight: 500;
+                    color: rgba(156,163,175, 1);
+                    padding-left: 14px;
+                    margin-bottom: 5px;
+
+                    .icon {
+                        display: inline-block;
+                        width: 40px;
+                        text-align: center;
+                    }
+
+                }
+
+                .router-link-exact-active {
+                    color: var(--primary);
+                    font-weight: 700;
+                    padding-left: 14px;
+
+                    &:not(.text-center) {
+                        padding-left: 10px;
+                        border-left: 4px solid var(--primary);
+                    }
+
+                    .icon {
+                        color: var(--primary) !important;
+                    }
+                }
+
+                &.nav-icons {
+                    .small {
+                        font-weight: 700 !important;
+                    }
+                }
+
+                &:is(:last-child) {
+                    .nav-link {
+                        margin-bottom: 0;
+                        border-bottom-left-radius: 15px;
+                        border-bottom-right-radius: 15px;
+                    }
+                }
+            }
+        }
+
+        .fa-layers {
+            display: inline-block;
+            height: 1em;
+            position: relative;
+            text-align: center;
+            vertical-align: -0.125em;
+            width: 1em;
+
+            .fa-layers-counter {
+                background-color: #ff253a;
+                border-radius: 1em;
+                -webkit-box-sizing: border-box;
+                box-sizing: border-box;
+                color: #fff;
+                height: 1.5em;
+                line-height: 1;
+                max-width: 5em;
+                min-width: 1.5em;
+                overflow: hidden;
+                padding: 0.25em;
+                right: 0;
+                text-overflow: ellipsis;
+                top: 0;
+                transform: scale(.5);
+                -webkit-transform-origin: top right;
+                transform-origin: top right;
+
+                display: inline-block;
+                position: absolute;
+
+                margin-right: -5px;
+                margin-top: -10px;
+            }
+
+            .far {
+                bottom: 0;
+                left: 0;
+                margin: auto;
+                position: absolute;
+                right: 0;
+                top: 0;
+            }
+        }
+
+        .searchbox {
+            @media (min-width: 768px) {
+                width: 300px;
+            }
+        }
+
+        .nav-avatar {
+            @media (min-width: 768px) {
+                width: 50px;
+                height: 50px;
+            }
+        }
+
+        .autocomplete[data-loading="true"]::after {
+            content: "";
+            border-right: 3px solid var(--primary);
+        }
+
+        .autocomplete {
+            &-input {
+                padding: 0.375rem 0.75rem 0.375rem 2.6rem;
+                background-color: var(--light-gray);
+                font-size: 0.9rem;
+                border-radius: 50rem;
+                background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjQjhDMkNDIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iNSIvPjxwYXRoIGQ9Ik0xOSAxOWwtNC00Ii8+PC9zdmc+");
+                box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%) !important;
+            }
+
+            &-result {
+                background-image: none;
+                padding: 10px 12px;
+                cursor: pointer;
+
+                &-list {
+                    box-shadow: 0 0.125rem 0.45rem var(--border-color);
+                    -ms-overflow-style: none;
+                    scrollbar-width: none;
+
+                    &::-webkit-scrollbar {
+                        width: 0 !important
+                    }
+                }
+
+                .media-icon {
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    width: 40px;
+                    height: 40px;
+                    margin-right: 12px;
+                    background: var(--light-gray);
+                    border: 1px solid var(--input-border);
+                    border-radius: 40px;
+                    box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%);
+                }
+
+            }
+        }
+
+        .sr {
+            &:not(:last-child) {
+                border-bottom: 1px solid var(--input-border);
+            }
+
+            &-avatar {
+                margin-right: 12px;
+                box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075)
+            }
+
+            &-account {
+                display: flex;
+                flex-direction: column;
+                align-items: flex-start;
+                justify-content: center;
+                gap: 3px;
+
+                &-acct {
+                    word-wrap: break-word;
+                    word-break: break-all;
+                    font-size: 14px;
+                    line-height: 18px;
+                    font-weight: bold;
+                    color: var(--dark);
+                    margin-right: 1rem;
+
+                    &.compact {
+                        font-size: 12px;
+                    }
+                }
+
+                &-stats {
+                    display: flex;
+                    align-items: center;
+                    gap: 5px;
+                    line-height: 14px;
+
+                    &-followers,
+                    &-statuses {
+                        font-size: 11px;
+                        font-weight: 500;
+                        color: var(--text-lighter);
+                    }
+                }
+            }
+
+            &-tag {
+                display: flex;
+                flex-direction: column;
+                align-items: flex-start;
+                justify-content: center;
+                gap: 3px;
+
+                &-name {
+                    word-wrap: break-word;
+                    word-break: break-all;
+                    font-size: 14px;
+                    line-height: 18px;
+                    font-weight: bold;
+                    color: var(--dark);
+                    margin-right: 1rem;
+
+                    &.compact {
+                        font-size: 12px;
+                    }
+                }
+
+                &-count {
+                    font-size: 11px;
+                    line-height: 13px;
+                    color: var(--text-lighter);
+                    font-weight: bold;
+                }
+            }
+
+            &-post {
+                display: flex;
+                flex-direction: column;
+                align-items: flex-start;
+                justify-content: center;
+                gap: 3px;
+
+                &-acct {
+                    font-size: 14px;
+                    line-height: 18px;
+                    font-weight: bold;
+                    color: var(--dark);
+                }
+
+                &-action {
+                    display: flex;
+                    font-size: 11px;
+                    line-height: 14px;
+                    color: var(--text-lighter);
+                    font-weight: 500;
+                    gap: 3px;
+                    align-items: center;
+
+                    &-timestamp {
+                        font-weight: 700;
+                    }
+
+                    &-label {
+                        font-weight: 700;
+                    }
+                }
+            }
+        }
+    }
+
+    .force-dark-mode {
+        .autocomplete-result-list {
+            border-color: var(--input-border);
+        }
+
+        .autocomplete-result:hover, .autocomplete-result[aria-selected=true] {
+            box-shadow: 0;
+            background-color: rgba(255, 255, 255, .1);
+        }
+
+        .autocomplete[data-loading="true"]::after {
+            content: "";
+            border: 3px solid rgba(255, 255, 255, 0.22);
+            border-right: 3px solid var(--primary);
+        }
+    }
 </style>

+ 34 - 25
resources/assets/js/app.js

@@ -78,31 +78,40 @@ window.App.util = {
 			}
 			return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count);
 		}),
-		timeAgo: (function(ts) {
-			let date = Date.parse(ts);
-			let seconds = Math.floor((new Date() - date) / 1000);
-			let interval = Math.floor(seconds / 63072000);
-			if (interval >= 1) {
-				return interval + "y";
-			}
-			interval = Math.floor(seconds / 604800);
-			if (interval >= 1) {
-				return interval + "w";
-			}
-			interval = Math.floor(seconds / 86400);
-			if (interval >= 1) {
-				return interval + "d";
-			}
-			interval = Math.floor(seconds / 3600);
-			if (interval >= 1) {
-				return interval + "h";
-			}
-			interval = Math.floor(seconds / 60);
-			if (interval >= 1) {
-				return interval + "m";
-			}
-			return Math.floor(seconds) + "s";
-		}),
+        timeAgo: function(ts) {
+            const date = new Date(ts);
+            const now = new Date();
+
+            const seconds = Math.floor((now - date) / 1000);
+
+            const secondsInYear = 60 * 60 * 24 * 365.25;
+            let interval = Math.floor(seconds / secondsInYear);
+            if (interval >= 1) {
+                return interval + "y";
+            }
+
+            interval = Math.floor(seconds / (60 * 60 * 24 * 7));
+            if (interval >= 1) {
+                return interval + "w";
+            }
+
+            interval = Math.floor(seconds / (60 * 60 * 24));
+            if (interval >= 1) {
+                return interval + "d";
+            }
+
+            interval = Math.floor(seconds / (60 * 60));
+            if (interval >= 1) {
+                return interval + "h";
+            }
+
+            interval = Math.floor(seconds / 60);
+            if (interval >= 1) {
+                return interval + "m";
+            }
+
+            return Math.floor(seconds) + "s";
+        },
 		timeAhead: (function(ts, short = true) {
 			let date = Date.parse(ts);
 			let diff = date - Date.parse(new Date());

+ 13 - 13
resources/assets/js/i18n/bn.json

@@ -111,20 +111,20 @@
         "embed": "\u098f\u09ae\u09ac\u09c7\u09a1 \u0995\u09b0\u09c1\u09a8",
         "selectOneOption": "\u09a8\u09bf\u09ae\u09cd\u09a8\u09b2\u09bf\u0996\u09bf\u09a4 \u09ac\u09bf\u0995\u09b2\u09cd\u09aa\u0997\u09c1\u09b2\u09bf\u09b0 \u098f\u0995\u099f\u09bf \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u0995\u09b0\u09c1\u09a8\u0964",
         "unlistFromTimelines": "\u099f\u09be\u0987\u09ae\u09b2\u09be\u0987\u09a8 \u09a5\u09c7\u0995\u09c7 Unlist \u0995\u09b0\u09c1\u09a8",
-        "addCW": "Add Content Warning",
-        "removeCW": "Remove Content Warning",
-        "markAsSpammer": "Mark as Spammer",
-        "markAsSpammerText": "Unlist + CW existing and future posts",
-        "spam": "Spam",
-        "sensitive": "Sensitive Content",
-        "abusive": "Abusive or Harmful",
-        "underageAccount": "Underage Account",
-        "copyrightInfringement": "Copyright Infringement",
+        "addCW": "\u0995\u09a8\u099f\u09c7\u09a8\u09cd\u099f \u09b8\u09a4\u09b0\u09cd\u0995\u09a4\u09be \u09af\u09cb\u0997 \u0995\u09b0\u09cb",
+        "removeCW": "\u0995\u09a8\u099f\u09c7\u09a8\u09cd\u099f \u09b8\u09a4\u09b0\u09cd\u0995\u09a4\u09be \u09b8\u09b0\u09be\u0993",
+        "markAsSpammer": "\u09ac\u09bf\u099c\u09cd\u099e\u09be\u09aa\u0995 \u09b9\u09bf\u09b8\u09c7\u09ac\u09c7 \u099a\u09bf\u09b9\u09cd\u09a8\u09bf\u09a4 \u0995\u09b0\u09cb",
+        "markAsSpammerText": "\u0985\u09a4\u09be\u09b2\u09bf\u0995\u09be\u09ac\u09a6\u09cd\u09a7 \u0995\u09b0\u09cb + \u0995\u09a8\u099f\u09c7\u09a8\u09cd\u099f \u09b8\u09a4\u09b0\u09cd\u0995\u09a4\u09be \u09ac\u09b0\u09cd\u09a4\u09ae\u09be\u09a8 \u0993 \u09ad\u09ac\u09bf\u09b7\u09cd\u09af\u09ce \u09b8\u0995\u09b2 \u09aa\u09cb\u09b8\u09cd\u099f\u09c7 \u09af\u09cb\u0997 \u0995\u09b0\u09cb",
+        "spam": "\u09b8\u09cd\u09aa\u09cd\u09af\u09be\u09ae",
+        "sensitive": "\u09b8\u0982\u09ac\u09c7\u09a6\u09a8\u09b6\u09c0\u09b2 \u09ac\u09b8\u09cd\u09a4\u09c1",
+        "abusive": "\u0985\u09a4\u09cd\u09af\u09be\u099a\u09be\u09b0\u099c\u09a8\u0995 \u09ac\u09be \u0995\u09cd\u09b7\u09a4\u09bf\u0995\u09be\u09b0\u0995",
+        "underageAccount": "\u0995\u09ae\u09ac\u09df\u09b8\u09c0 \u0985\u09cd\u09af\u09be\u0995\u09be\u0989\u09a8\u09cd\u099f",
+        "copyrightInfringement": "\u09b8\u09cd\u09ac\u09a4\u09cd\u09a4\u09cd\u09ac\u09be\u09a7\u09bf\u0995\u09be\u09b0 \u09b2\u0999\u09cd\u0998\u09a8",
         "impersonation": "Impersonation",
-        "scamOrFraud": "Scam or Fraud",
-        "confirmReport": "Confirm Report",
-        "confirmReportText": "Are you sure you want to report this post?",
-        "reportSent": "Report Sent!",
+        "scamOrFraud": "\u09ad\u09a3\u09cd\u09a1\u09be\u09ae\u09bf \u09ac\u09be \u09aa\u09cd\u09b0\u09a4\u09be\u09b0\u09a3\u09be",
+        "confirmReport": "\u09aa\u09cd\u09b0\u09a4\u09bf\u09ac\u09c7\u09a6\u09a8 \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4 \u0995\u09b0\u09cb",
+        "confirmReportText": "\u09a4\u09c1\u09ae\u09bf \u0995\u09c0 \u098f\u0987 \u09aa\u09cb\u09b8\u09cd\u099f\u099f\u09bf \u09a8\u09bf\u09df\u09c7 \u09b8\u09ae\u09b8\u09cd\u09af\u09be \u099c\u09be\u09a8\u09be\u09a8\u09cb\u09b0 \u09ac\u09cd\u09af\u09be\u09aa\u09be\u09b0\u09c7 \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4?",
+        "reportSent": "\u09aa\u09cd\u09b0\u09a4\u09bf\u09ac\u09c7\u09a6\u09a8 \u09aa\u09be\u09a0\u09be\u09a8\u09cb \u09b9\u09df\u09c7\u099b\u09c7!",
         "reportSentText": "We have successfully received your report.",
         "reportSentError": "There was an issue reporting this post.",
         "modAddCWConfirm": "Are you sure you want to add a content warning to this post?",

+ 4 - 4
resources/assets/js/i18n/de.json

@@ -5,7 +5,7 @@
         "comments": "Kommentare",
         "like": "Gef\u00e4llt mir",
         "liked": "Gef\u00e4llt",
-        "likes": "Gef\u00e4llt",
+        "likes": "Gefiel",
         "share": "Teilen",
         "shared": "Geteilt",
         "shares": "Geteilt",
@@ -150,7 +150,7 @@
     "timeline": {
         "peopleYouMayKnow": "Leute, die du vielleicht kennst",
         "onboarding": {
-            "welcome": "Herzlich Willkommen",
+            "welcome": "Herzlich willkommen",
             "thisIsYourHomeFeed": "Dies ist dein Heim-Feed, ein chronologischer Feed von Beitr\u00e4gen aus den Konten, denen du folgst.",
             "letUsHelpYouFind": "Lass uns dir helfen, einige interessante Leute zu finden, denen du folgen kannst",
             "refreshFeed": "Feed aktualisieren"
@@ -164,7 +164,7 @@
         "selectReason": "Einen Grund ausw\u00e4hlen",
         "reported": "Gemeldet",
         "sendingReport": "Meldung wird gesendet",
-        "thanksMsg": "Vielen Dank f\u00fcr die Meldung, Leute wie du helfen, unsere Community sicher zu halten!",
-        "contactAdminMsg": "Wenn du einen Administrator zu diesem Beitrag oder dieser Meldung kontaktieren m\u00f6chtest"
+        "thanksMsg": "Danke f\u00fcr deine Meldung! Damit erh\u00f6hst du die Sicherheit der Community!",
+        "contactAdminMsg": "Wenn du die Administration wegen dieses Beitrags oder dieser Meldung kontaktieren m\u00f6chtest"
     }
 }

+ 14 - 0
resources/assets/js/i18n/en.json

@@ -46,6 +46,7 @@
         "profile": "Profile",
         "drive": "Drive",
         "settings": "Settings",
+        "appearance": "Appearance",
         "compose": "Create New",
         "logout": "Logout",
         "about": "About",
@@ -166,5 +167,18 @@
         "sendingReport": "Sending report",
         "thanksMsg": "Thanks for the report, people like you help keep our community safe!",
         "contactAdminMsg": "If you'd like to contact an administrator about this post or report"
+    },
+    "appearance": {
+        "theme": "Theme",
+        "profileLayout": "Profile Layout",
+        "compactPreviews": "Compact Media Previews",
+        "loadComments": "Load Comments",
+        "hideStats": "Hide Counts & Stats",
+        "auto": "Auto",
+        "lightMode": "Light mode",
+        "darkMode": "Dark mode",
+        "grid": "Grid",
+        "masonry": "Masonry",
+        "feed": "Feed"
     }
 }

+ 32 - 18
resources/assets/js/i18n/es.json

@@ -23,7 +23,7 @@
         "proceed": "Proceder",
         "next": "Siguiente",
         "close": "Cerrar",
-        "clickHere": "haz click aqu\u00ed",
+        "clickHere": "haz clic aqu\u00ed",
         "sensitive": "Sensible",
         "sensitiveContent": "Contenido Sensible",
         "sensitiveContentWarning": "Esta publicaci\u00f3n podr\u00eda contener contenido sensible"
@@ -46,6 +46,7 @@
         "profile": "Perfil",
         "drive": "Multimedia",
         "settings": "Ajustes",
+        "appearance": "Aspecto",
         "compose": "Crear Nuevo",
         "logout": "Cerrar sesi\u00f3n",
         "about": "Acerca de",
@@ -82,9 +83,9 @@
         "noneFound": "No se han encontrado notificaciones"
     },
     "post": {
-        "shareToFollowers": "Compartir a seguidores",
-        "shareToOther": "Compartir a otros",
-        "noLikes": "No hay me gustas",
+        "shareToFollowers": "Compartir con seguidores",
+        "shareToOther": "Compartir con otros",
+        "noLikes": "A\u00fan no hay \"me gusta\"",
         "uploading": "Subiendo"
     },
     "profile": {
@@ -95,30 +96,30 @@
         "collections": "Colecciones",
         "follow": "Seguir",
         "unfollow": "Dejar de seguir",
-        "editProfile": "Editar Perfil",
+        "editProfile": "Editar perfil",
         "followRequested": "Seguimiento Solicitado",
         "joined": "Se uni\u00f3",
         "emptyCollections": "Parece que no podemos encontrar ninguna colecci\u00f3n",
-        "emptyPosts": "Parece que no podemos encontrar ning\u00fan post"
+        "emptyPosts": "Parece que no podemos encontrar ninguna publicaci\u00f3n"
     },
     "menu": {
-        "viewPost": "Ver Publicaci\u00f3n",
-        "viewProfile": "Ver Perfil",
-        "moderationTools": "Herramientas de Moderaci\u00f3n",
+        "viewPost": "Ver publicaci\u00f3n",
+        "viewProfile": "Ver perfil",
+        "moderationTools": "Herramientas de moderaci\u00f3n",
         "report": "Reportar",
         "archive": "Archivar",
-        "unarchive": "No Archivar",
+        "unarchive": "Desarchivar",
         "embed": "Incrustar",
-        "selectOneOption": "Escoge una de las siguientes opciones",
+        "selectOneOption": "Selecciona una de las siguientes opciones",
         "unlistFromTimelines": "No listar en L\u00edneas Temporales",
         "addCW": "A\u00f1adir Spoiler",
         "removeCW": "Quitar Spoiler",
         "markAsSpammer": "Marcar como Spammer",
         "markAsSpammerText": "No listar + Spoiler publicaciones actuales y futuras",
         "spam": "Spam",
-        "sensitive": "Contenido Sensible",
+        "sensitive": "Contenido sensible",
         "abusive": "Abusivo o Da\u00f1ino",
-        "underageAccount": "Cuenta de Menor de Edad",
+        "underageAccount": "Cuenta de menor de edad",
         "copyrightInfringement": "Infracci\u00f3n de derechos de autor",
         "impersonation": "Suplantaci\u00f3n de identidad",
         "scamOrFraud": "Scam o Fraude",
@@ -131,21 +132,21 @@
         "modCWSuccess": "Spoiler a\u00f1adido correctamente",
         "modRemoveCWConfirm": "\u00bfSeguro que desea eliminar el spoiler de esta publicaci\u00f3n?",
         "modRemoveCWSuccess": "Spoiler eliminado correctamente",
-        "modUnlistConfirm": "\u00bfSeguro que desea no listar esta publicaci\u00f3n?",
+        "modUnlistConfirm": "\u00bfEst\u00e1s seguro de que deseas no listar esta publicaci\u00f3n?",
         "modUnlistSuccess": "Publicaci\u00f3n no listada correctamente",
         "modMarkAsSpammerConfirm": "\u00bfSeguro que quiere marcar este usuario como spammer? Todas las publicaciones existentes y futuras no ser\u00e1n listadas en las l\u00edneas temporales y se les agregar\u00e1 un Spoiler o alerta de contenido.",
         "modMarkAsSpammerSuccess": "Cuenta marcada como spam correctamente",
         "toFollowers": "a Seguidores",
         "showCaption": "Mostrar subt\u00edtulos",
-        "showLikes": "Mostrar me gustas",
-        "compactMode": "Modo Compacto",
-        "embedConfirmText": "Usando este incrustado, usted acepta",
+        "showLikes": "Mostrar \"me gusta\"",
+        "compactMode": "Modo compacto",
+        "embedConfirmText": "Al utilizar esta incrustaci\u00f3n, usted acepta nuestros",
         "deletePostConfirm": "\u00bfSeguro que deseas eliminar esta publicaci\u00f3n?",
         "archivePostConfirm": "\u00bfSeguro que deseas archivar esta publicaci\u00f3n?",
         "unarchivePostConfirm": "\u00bfSeguro que desea desarchivar esta publicaci\u00f3n?"
     },
     "story": {
-        "add": "A\u00f1adir Historia"
+        "add": "A\u00f1adir historia"
     },
     "timeline": {
         "peopleYouMayKnow": "Personas que quiz\u00e1s conozcas",
@@ -166,5 +167,18 @@
         "sendingReport": "Enviando reporte",
         "thanksMsg": "Gracias por el reporte, \u00a1personas como t\u00fa ayudan a mantener nuestra comunidad segura!",
         "contactAdminMsg": "Si quieres contactar un administrador sobre esta publicaci\u00f3n o reporte"
+    },
+    "appearance": {
+        "theme": "Tema",
+        "profileLayout": "Arreglo de Perfil",
+        "compactPreviews": "Previstas Compactas de Medios",
+        "loadComments": "Cargar Comentarios",
+        "hideStats": "Ocultar Cuentas y Estad\u00edsticas",
+        "auto": "Autom\u00e1tico",
+        "lightMode": "Modo claro",
+        "darkMode": "Modo oscuro",
+        "grid": "Cuadr\u00edcula",
+        "masonry": "Mamposter\u00eda",
+        "feed": "Feed"
     }
 }

+ 68 - 68
resources/assets/js/i18n/fi.json

@@ -1,6 +1,6 @@
 {
     "common": {
-        "comment": "Kommentti",
+        "comment": "Kommentoi",
         "commented": "Kommentoitu",
         "comments": "Kommentit",
         "like": "Tykk\u00e4\u00e4",
@@ -10,7 +10,7 @@
         "shared": "Jaettu",
         "shares": "Jaot",
         "unshare": "Peru jakaminen",
-        "bookmark": "Bookmark",
+        "bookmark": "Kirjanmerkki",
         "cancel": "Peruuta",
         "copyLink": "Kopioi linkki",
         "delete": "Poista",
@@ -20,10 +20,10 @@
         "other": "Muu",
         "readMore": "Lue lis\u00e4\u00e4",
         "success": "Onnistui",
-        "proceed": "Proceed",
-        "next": "Next",
-        "close": "Close",
-        "clickHere": "click here",
+        "proceed": "Jatka",
+        "next": "Seuraava",
+        "close": "Sulje",
+        "clickHere": "klikkaa t\u00e4st\u00e4",
         "sensitive": "Sensitiivinen",
         "sensitiveContent": "Sensitiivinen sis\u00e4lt\u00f6",
         "sensitiveContentWarning": "T\u00e4m\u00e4 julkaisu saattaa sis\u00e4lt\u00e4\u00e4 sensitiivist\u00e4 sis\u00e4lt\u00f6\u00e4"
@@ -63,62 +63,62 @@
     "notifications": {
         "liked": "tykk\u00e4si sinun",
         "commented": "kommentoi sinun",
-        "reacted": "reacted to your",
-        "shared": "shared your",
-        "tagged": "tagged you in a",
-        "updatedA": "updated a",
-        "sentA": "sent a",
-        "followed": "followed",
-        "mentioned": "mentioned",
-        "you": "you",
-        "yourApplication": "Your application to join",
-        "applicationApproved": "was approved!",
-        "applicationRejected": "was rejected. You can re-apply to join in 6 months.",
-        "dm": "dm",
-        "groupPost": "group post",
-        "modlog": "modlog",
-        "post": "post",
-        "story": "story",
-        "noneFound": "No notifications found"
+        "reacted": "reagoi sinun",
+        "shared": "jakoi sinun",
+        "tagged": "merkitsi sinut",
+        "updatedA": "p\u00e4ivitetty",
+        "sentA": "l\u00e4hetti",
+        "followed": "seuraa",
+        "mentioned": "mainittu",
+        "you": "sin\u00e4",
+        "yourApplication": "Liittymist\u00e4 koskeva hakemuksesi",
+        "applicationApproved": "oli hyv\u00e4ksytty!",
+        "applicationRejected": "oli hyl\u00e4tty. Voit hakea uudelleen 6 kuukauden kuluttua.",
+        "dm": "yv",
+        "groupPost": "ryhm\u00e4viesti",
+        "modlog": "modelogi",
+        "post": "viesti",
+        "story": "tarina",
+        "noneFound": "Ilmoituksia ei l\u00f6ytynyt"
     },
     "post": {
-        "shareToFollowers": "Share to followers",
-        "shareToOther": "Share to other",
-        "noLikes": "No likes yet",
-        "uploading": "Uploading"
+        "shareToFollowers": "Jaa seuraajille",
+        "shareToOther": "Jaa muille",
+        "noLikes": "Tykk\u00e4yksi\u00e4 ei ole viel\u00e4",
+        "uploading": "Siirret\u00e4\u00e4n"
     },
     "profile": {
-        "posts": "Posts",
-        "followers": "Followers",
-        "following": "Following",
-        "admin": "Admin",
-        "collections": "Collections",
-        "follow": "Follow",
-        "unfollow": "Unfollow",
-        "editProfile": "Edit Profile",
-        "followRequested": "Follow Requested",
-        "joined": "Joined",
-        "emptyCollections": "We can't seem to find any collections",
-        "emptyPosts": "We can't seem to find any posts"
+        "posts": "Julkaisut",
+        "followers": "Seuraajat",
+        "following": "Seurataan",
+        "admin": "Yll\u00e4pit\u00e4j\u00e4",
+        "collections": "Kokoelmat",
+        "follow": "Seuraa",
+        "unfollow": "Lopeta seuraaminen",
+        "editProfile": "Muokkaa profiilia",
+        "followRequested": "Seuraamista pyydetty",
+        "joined": "Liittynyt",
+        "emptyCollections": "Emme n\u00e4yt\u00e4 l\u00f6yt\u00e4v\u00e4n yht\u00e4\u00e4n kokoelmaa",
+        "emptyPosts": "Emme n\u00e4yt\u00e4 l\u00f6yt\u00e4v\u00e4n yht\u00e4\u00e4n viesti\u00e4"
     },
     "menu": {
-        "viewPost": "View Post",
-        "viewProfile": "View Profile",
-        "moderationTools": "Moderation Tools",
-        "report": "Report",
-        "archive": "Archive",
-        "unarchive": "Unarchive",
-        "embed": "Embed",
-        "selectOneOption": "Select one of the following options",
-        "unlistFromTimelines": "Unlist from Timelines",
-        "addCW": "Add Content Warning",
-        "removeCW": "Remove Content Warning",
-        "markAsSpammer": "Mark as Spammer",
-        "markAsSpammerText": "Unlist + CW existing and future posts",
-        "spam": "Spam",
-        "sensitive": "Sensitive Content",
-        "abusive": "Abusive or Harmful",
-        "underageAccount": "Underage Account",
+        "viewPost": "N\u00e4yt\u00e4 viesti",
+        "viewProfile": "N\u00e4yt\u00e4 profiili",
+        "moderationTools": "Moderointity\u00f6kalut",
+        "report": "Ilmoita",
+        "archive": "Arkisto",
+        "unarchive": "Palauta arkistosta",
+        "embed": "Upota",
+        "selectOneOption": "Valitse yksi seuraavista vaihtoehdoista",
+        "unlistFromTimelines": "Poista aikajanalta",
+        "addCW": "Lis\u00e4\u00e4 sis\u00e4lt\u00f6varoitus",
+        "removeCW": "Poista sis\u00e4lt\u00f6varoitus",
+        "markAsSpammer": "Merkitse roskapostittajaksi",
+        "markAsSpammerText": "Unlist + SV olevat ja tulevat julkaisut",
+        "spam": "Roskaposti",
+        "sensitive": "Arkaluonteista sis\u00e4lt\u00f6\u00e4",
+        "abusive": "Hy\u00f6kk\u00e4\u00e4v\u00e4 tai haitallinen",
+        "underageAccount": "Alaik\u00e4isen tili",
         "copyrightInfringement": "Tekij\u00e4noikeusloukkaus",
         "impersonation": "V\u00e4\u00e4rennetty henkil\u00f6llisyys",
         "scamOrFraud": "Huijaus tai petos",
@@ -148,23 +148,23 @@
         "add": "Lis\u00e4\u00e4 tarina"
     },
     "timeline": {
-        "peopleYouMayKnow": "People you may know",
+        "peopleYouMayKnow": "Ihmisi\u00e4, jotka saatat tuntea",
         "onboarding": {
-            "welcome": "Welcome",
-            "thisIsYourHomeFeed": "This is your home feed, a chronological feed of posts from accounts you follow.",
-            "letUsHelpYouFind": "Let us help you find some interesting people to follow",
-            "refreshFeed": "Refresh my feed"
+            "welcome": "Tervetuloa",
+            "thisIsYourHomeFeed": "T\u00e4m\u00e4 on kotisy\u00f6tteesi. Aikaj\u00e4rjestyksess\u00e4 oleva sy\u00f6te seuraamiesi k\u00e4ytt\u00e4jien julkaisuista.",
+            "letUsHelpYouFind": "Anna meid\u00e4n auttaa l\u00f6yt\u00e4m\u00e4\u00e4n mielenkiintoisia ihmisi\u00e4 seurattavaksi",
+            "refreshFeed": "P\u00e4ivit\u00e4 sy\u00f6tteeni"
         }
     },
     "hashtags": {
-        "emptyFeed": "We can't seem to find any posts for this hashtag"
+        "emptyFeed": "Emme n\u00e4yt\u00e4 l\u00f6yt\u00e4v\u00e4n yht\u00e4\u00e4n viesti\u00e4 t\u00e4lle aihetunnisteelle"
     },
     "report": {
-        "report": "Report",
-        "selectReason": "Select a reason",
-        "reported": "Reported",
-        "sendingReport": "Sending report",
-        "thanksMsg": "Thanks for the report, people like you help keep our community safe!",
-        "contactAdminMsg": "If you'd like to contact an administrator about this post or report"
+        "report": "Ilmoita",
+        "selectReason": "Valitse syy",
+        "reported": "Ilmoitettu",
+        "sendingReport": "L\u00e4hetet\u00e4\u00e4n ilmoitusta",
+        "thanksMsg": "Kiitos ilmoituksesta. Sin\u00e4 ja kaltaisesi autatte pit\u00e4m\u00e4\u00e4n yhteis\u00f6n turvallisena!",
+        "contactAdminMsg": "Jos haluaisit ottaa yhteytt\u00e4 yll\u00e4pitoom t\u00e4st\u00e4 julkaisusta tai ilmoittaa sen"
     }
 }

+ 8 - 8
resources/assets/js/i18n/fr.json

@@ -5,7 +5,7 @@
         "comments": "Commentaires",
         "like": "J'aime",
         "liked": "Aim\u00e9",
-        "likes": "J'aime",
+        "likes": "J'aimes",
         "share": "Partager",
         "shared": "Partag\u00e9",
         "shares": "Partages",
@@ -35,9 +35,9 @@
     "navmenu": {
         "search": "Chercher",
         "admin": "Tableau de bord d'administration",
-        "homeFeed": "Flux principal",
-        "localFeed": "Flux local",
-        "globalFeed": "Flux global",
+        "homeFeed": "Fil principal",
+        "localFeed": "Fil local",
+        "globalFeed": "Fil global",
         "discover": "D\u00e9couvrir",
         "directMessages": "Messages Priv\u00e9s",
         "notifications": "Notifications",
@@ -71,7 +71,7 @@
         "followed": "s'est abonn\u00e9\u00b7e \u00e0",
         "mentioned": "a mentionn\u00e9",
         "you": "vous",
-        "yourApplication": "Votre candidature \u00e0 rejoindre",
+        "yourApplication": "Votre demande d'adh\u00e9sion",
         "applicationApproved": "a \u00e9t\u00e9 approuv\u00e9e !",
         "applicationRejected": "a \u00e9t\u00e9 rejet\u00e9e. Vous pouvez refaire une demande dans 6 mois.",
         "dm": "mp",
@@ -85,7 +85,7 @@
         "shareToFollowers": "Partager avec ses abonn\u00e9\u00b7e\u00b7s",
         "shareToOther": "Partager avec d'autres",
         "noLikes": "Aucun J'aime pour le moment",
-        "uploading": "Envoi en cours"
+        "uploading": "T\u00e9l\u00e9versement"
     },
     "profile": {
         "posts": "Publications",
@@ -132,7 +132,7 @@
         "modRemoveCWConfirm": "\u00cates-vous s\u00fbr\u00b7e de vouloir supprimer l'avertissement de contenu sur cette publication ?",
         "modRemoveCWSuccess": "Avertissement de contenu supprim\u00e9 avec succ\u00e8s",
         "modUnlistConfirm": "\u00cates-vous s\u00fbr\u00b7e de vouloir retirer cette publication des flux ?",
-        "modUnlistSuccess": "Publication retir\u00e9e des flux avec succ\u00e8s",
+        "modUnlistSuccess": "Publication retir\u00e9e des fils avec succ\u00e8s",
         "modMarkAsSpammerConfirm": "\u00cates-vous s\u00fbr\u00b7e de vouloir marquer cet utilisateur\u00b7rice comme spammeur\u00b7euse ? Toutes les publications existantes et futures seront retir\u00e9es des flux et un avertissement de contenu sera appliqu\u00e9.",
         "modMarkAsSpammerSuccess": "Compte marqu\u00e9 avec succ\u00e8s comme spammeur",
         "toFollowers": "aux abonn\u00e9\u00b7e\u00b7s",
@@ -148,7 +148,7 @@
         "add": "Ajouter une story"
     },
     "timeline": {
-        "peopleYouMayKnow": "Connaissances possibles",
+        "peopleYouMayKnow": "Personnes que vous connaissez peut-\u00eatre",
         "onboarding": {
             "welcome": "Bienvenue",
             "thisIsYourHomeFeed": "Ceci est votre flux personnel, un flux chronologique des publications de comptes que vous suivez.",

+ 32 - 32
resources/assets/js/i18n/hi.json

@@ -106,8 +106,8 @@
         "viewProfile": "\u092a\u094d\u0930\u094b\u092b\u093e\u0907\u0932 \u0926\u0947\u0916\u0947\u0902",
         "moderationTools": "\u092e\u0949\u0921\u0930\u0947\u0936\u0928 \u091f\u0942\u0932\u094d\u0938",
         "report": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f",
-        "archive": "Archive",
-        "unarchive": "Unarchive",
+        "archive": "\u092a\u0941\u0930\u093e\u0932\u0947\u0916",
+        "unarchive": "\u0905\u092a\u0941\u0930\u093e\u0932\u0947\u0916",
         "embed": "\u090f\u092e\u094d\u092c\u0947\u0921",
         "selectOneOption": "\u0915\u093f\u0938\u0940 \u090f\u0915 \u0935\u093f\u0915\u0932\u094d\u092a \u0915\u094b \u091a\u0941\u0928\u0947",
         "unlistFromTimelines": "\u091f\u093e\u0907\u092e\u0932\u093e\u0907\u0928\u094d\u0938 \u0938\u0947 \u0939\u091f\u093e\u090f\u0902",
@@ -121,50 +121,50 @@
         "underageAccount": "\u0928\u093e\u092c\u093e\u0932\u093f\u0917 \u0905\u0915\u093e\u0909\u0902\u091f",
         "copyrightInfringement": "\u0915\u0949\u092a\u0940\u0930\u093e\u0908\u091f \u0915\u093e \u0909\u0932\u094d\u0932\u0902\u0918\u0928",
         "impersonation": "\u092a\u094d\u0930\u0924\u093f\u0930\u0942\u092a\u0923",
-        "scamOrFraud": "Scam or Fraud",
-        "confirmReport": "Confirm Report",
-        "confirmReportText": "Are you sure you want to report this post?",
-        "reportSent": "Report Sent!",
-        "reportSentText": "We have successfully received your report.",
-        "reportSentError": "There was an issue reporting this post.",
+        "scamOrFraud": "\u0918\u094b\u091f\u093e\u0932\u093e \u092f\u093e \u0927\u094b\u0916\u093e\u0927\u0921\u093c\u0940",
+        "confirmReport": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0915\u0940 \u092a\u0941\u0937\u094d\u091f\u093f \u0915\u0930\u0947\u0902",
+        "confirmReportText": "\u0915\u094d\u092f\u093e \u0906\u092a \u0928\u093f\u0936\u094d\u091a\u093f\u0924 \u0930\u0942\u092a \u0938\u0947 \u0907\u0938 \u092a\u094b\u0938\u094d\u091f \u0915\u0940 \u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0915\u0930\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?",
+        "reportSent": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f \u092d\u0947\u091c\u0940 \u0917\u0908!",
+        "reportSentText": "\u0939\u092e\u0947\u0902 \u0906\u092a\u0915\u0940 \u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0938\u092b\u0932\u0924\u093e\u092a\u0942\u0930\u094d\u0935\u0915 \u092a\u094d\u0930\u093e\u092a\u094d\u0924 \u0939\u094b \u0917\u0908 \u0939\u0948\u0964",
+        "reportSentError": "\u0907\u0938 \u092a\u094b\u0938\u094d\u091f \u0915\u0940 \u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0915\u0930\u0928\u0947 \u092e\u0947\u0902 \u0938\u092e\u0938\u094d\u092f\u093e \u0939\u0941\u0908\u0964",
         "modAddCWConfirm": "\u0915\u094d\u092f\u093e \u092f\u0939 \u0928\u093f\u0936\u094d\u091a\u093f\u0924 \u0939\u0948 \u0915\u093f \u0906\u092a \u092a\u094d\u0930\u0915\u093e\u0936\u093f\u0924 \u0938\u093e\u092e\u0917\u094d\u0930\u0940 \u0915\u0947 \u0938\u093e\u0925 \u0935\u093f\u0937\u092f-\u0935\u0938\u094d\u0924\u0941 \u0938\u092e\u094d\u092c\u0902\u0927\u093f\u0924 \u091a\u0947\u0924\u093e\u0935\u0928\u0940 \u091c\u094b\u095c\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?",
         "modCWSuccess": "\u0935\u093f\u0937\u092f-\u0935\u0938\u094d\u0924\u0941 \u0938\u092e\u094d\u092c\u0902\u0927\u093f\u0924 \u091a\u0947\u0924\u093e\u0935\u0928\u0940 \u0938\u092b\u0932\u0924\u093e \u0938\u0947 \u091c\u094b\u095c\u0940 \u0917\u092f\u0940",
         "modRemoveCWConfirm": "\u0915\u094d\u092f\u093e \u092f\u0939 \u0928\u093f\u0936\u094d\u091a\u093f\u0924 \u0939\u0948 \u0915\u093f \u0906\u092a \u092a\u094d\u0930\u0915\u093e\u0936\u093f\u0924 \u0938\u093e\u092e\u0917\u094d\u0930\u0940 \u0938\u0947 \u0935\u093f\u0937\u092f-\u0935\u0938\u094d\u0924\u0941 \u0938\u092e\u094d\u092c\u0902\u0927\u093f\u0924 \u091a\u0947\u0924\u093e\u0935\u0928\u0940 \u0939\u091f\u093e\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?",
         "modRemoveCWSuccess": "\u0935\u093f\u0937\u092f-\u0935\u0938\u094d\u0924\u0941 \u0938\u092e\u094d\u092c\u0902\u0927\u093f\u0924 \u091a\u0947\u0924\u093e\u0935\u0928\u0940 \u0938\u092b\u0932\u0924\u093e \u0938\u0947 \u0939\u091f\u093e\u0908 \u0917\u092f\u0940",
-        "modUnlistConfirm": "Are you sure you want to unlist this post?",
-        "modUnlistSuccess": "Successfully unlisted post",
-        "modMarkAsSpammerConfirm": "Are you sure you want to mark this user as a spammer? All existing and future posts will be unlisted on timelines and a content warning will be applied.",
-        "modMarkAsSpammerSuccess": "Successfully marked account as spammer",
-        "toFollowers": "to Followers",
-        "showCaption": "Show Caption",
-        "showLikes": "Show Likes",
-        "compactMode": "Compact Mode",
+        "modUnlistConfirm": "\u0915\u094d\u092f\u093e \u0906\u092a \u0928\u093f\u0936\u094d\u091a\u093f\u0924 \u0930\u0942\u092a \u0938\u0947 \u0907\u0938 \u092a\u094b\u0938\u094d\u091f \u0915\u094b \u0915\u0930\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?",
+        "modUnlistSuccess": "\u092a\u094b\u0938\u094d\u091f \u0915\u094b \u0938\u092b\u0932\u0924\u093e\u092a\u0942\u0930\u094d\u0935\u0915 \u0905\u0938\u0942\u091a\u0940\u092c\u0926\u094d\u0927 \u0915\u093f\u092f\u093e \u0917\u092f\u093e",
+        "modMarkAsSpammerConfirm": "\u0915\u094d\u092f\u093e \u0906\u092a \u0935\u093e\u0915\u0908 \u0907\u0938 \u0909\u092a\u092f\u094b\u0917\u0915\u0930\u094d\u0924\u093e \u0915\u094b \u0938\u094d\u092a\u0948\u092e\u0930 \u0915\u0947 \u0930\u0942\u092a \u092e\u0947\u0902 \u091a\u093f\u0939\u094d\u0928\u093f\u0924 \u0915\u0930\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902? \u0938\u092d\u0940 \u092e\u094c\u091c\u0942\u0926\u093e \u0914\u0930 \u092d\u0935\u093f\u0937\u094d\u092f \u0915\u0940 \u092a\u094b\u0938\u094d\u091f \u091f\u093e\u0907\u092e\u0932\u093e\u0907\u0928 \u092a\u0930 \u0905\u0938\u0942\u091a\u0940\u092c\u0926\u094d\u0927 \u0915\u0930 \u0926\u0940 \u091c\u093e\u090f\u0902\u0917\u0940 \u0914\u0930 \u090f\u0915 \u0938\u093e\u092e\u0917\u094d\u0930\u0940 \u091a\u0947\u0924\u093e\u0935\u0928\u0940 \u0932\u093e\u0917\u0942 \u0915\u0940 \u091c\u093e\u090f\u0917\u0940\u0964",
+        "modMarkAsSpammerSuccess": "\u0916\u093e\u0924\u0947 \u0915\u094b \u0938\u094d\u092a\u0948\u092e\u0930 \u0915\u0947 \u0930\u0942\u092a \u092e\u0947\u0902 \u0938\u092b\u0932\u0924\u093e\u092a\u0942\u0930\u094d\u0935\u0915 \u091a\u093f\u0939\u094d\u0928\u093f\u0924 \u0915\u093f\u092f\u093e \u0917\u092f\u093e",
+        "toFollowers": "\u0905\u0928\u0941\u092f\u093e\u092f\u093f\u092f\u094b\u0902 \u0915\u0947 \u0932\u093f\u090f",
+        "showCaption": "\u0936\u0940\u0930\u094d\u0937\u0915 \u0926\u093f\u0916\u093e\u090f\u0902",
+        "showLikes": "\u0932\u093e\u0907\u0915 \u0926\u093f\u0916\u093e\u090f\u0902",
+        "compactMode": "\u0938\u0902\u0915\u094d\u0937\u093f\u092a\u094d\u0924 \u092e\u094b\u0921",
         "embedConfirmText": "By using this embed, you agree to our",
-        "deletePostConfirm": "Are you sure you want to delete this post?",
-        "archivePostConfirm": "Are you sure you want to archive this post?",
-        "unarchivePostConfirm": "Are you sure you want to unarchive this post?"
+        "deletePostConfirm": "\u0915\u094d\u092f\u093e \u0906\u092a \u0935\u093e\u0915\u0908 \u092e\u0947\u0902 \u0907\u0938 \u092a\u094b\u0938\u094d\u091f \u0915\u094b \u0939\u091f\u093e\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?",
+        "archivePostConfirm": "\u0915\u094d\u092f\u093e \u0906\u092a \u0935\u093e\u0915\u0908 \u0907\u0938 \u0906\u0907\u091f\u092e \u0915\u094b \u0906\u0930\u094d\u0915\u093e\u0907\u0935 \u0915\u0930\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?",
+        "unarchivePostConfirm": "\u0915\u094d\u092f\u093e \u0906\u092a \u0935\u093e\u0915\u0908 \u0907\u0938 \u092a\u094b\u0938\u094d\u091f \u0915\u094b \u0905\u0928\u0906\u0930\u094d\u0915\u093e\u0907\u0935 \u0915\u0930\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902?"
     },
     "story": {
-        "add": "Add Story"
+        "add": "\u0915\u0939\u093e\u0928\u0940 \u091c\u094b\u0921\u093c\u0947\u0902"
     },
     "timeline": {
-        "peopleYouMayKnow": "People you may know",
+        "peopleYouMayKnow": "\u091c\u093f\u0928 \u0932\u094b\u0917\u094b\u0902 \u0915\u094b \u0906\u092a \u091c\u093e\u0928\u0924\u0947 \u0939\u094b\u0902",
         "onboarding": {
-            "welcome": "Welcome",
-            "thisIsYourHomeFeed": "This is your home feed, a chronological feed of posts from accounts you follow.",
-            "letUsHelpYouFind": "Let us help you find some interesting people to follow",
-            "refreshFeed": "Refresh my feed"
+            "welcome": "\u0938\u094d\u0935\u093e\u0917\u0924",
+            "thisIsYourHomeFeed": "\u092f\u0939 \u0906\u092a\u0915\u093e \u0939\u094b\u092e \u092b\u0940\u0921 \u0939\u0948, \u091c\u094b \u0906\u092a\u0915\u0947 \u0926\u094d\u0935\u093e\u0930\u093e \u0905\u0928\u0941\u0938\u0930\u0923 \u0915\u093f\u092f\u0947 \u091c\u093e\u0928\u0947 \u0935\u093e\u0932\u0947 \u0916\u093e\u0924\u094b\u0902 \u0915\u0947 \u092a\u094b\u0938\u094d\u091f\u094b\u0902 \u0915\u093e \u0915\u093e\u0932\u093e\u0928\u0941\u0915\u094d\u0930\u092e\u093f\u0915 \u092b\u0940\u0921 \u0939\u0948\u0964",
+            "letUsHelpYouFind": "\u0906\u0907\u090f \u0939\u092e \u0906\u092a\u0915\u094b \u0905\u0928\u0941\u0938\u0930\u0923 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093f\u090f \u0915\u0941\u091b \u0926\u093f\u0932\u091a\u0938\u094d\u092a \u0932\u094b\u0917\u094b\u0902 \u0915\u094b \u0916\u094b\u091c\u0928\u0947 \u092e\u0947\u0902 \u092e\u0926\u0926 \u0915\u0930\u0947\u0902",
+            "refreshFeed": "\u092e\u0947\u0930\u093e \u092b\u093c\u0940\u0921 \u0924\u093e\u091c\u093c\u093e \u0915\u0930\u0947\u0902"
         }
     },
     "hashtags": {
-        "emptyFeed": "We can't seem to find any posts for this hashtag"
+        "emptyFeed": "\u0939\u092e\u0947\u0902 \u0907\u0938 \u0939\u0948\u0936\u091f\u0948\u0917 \u0915\u0947 \u0932\u093f\u090f \u0915\u094b\u0908 \u092a\u094b\u0938\u094d\u091f \u0928\u0939\u0940\u0902 \u092e\u093f\u0932 \u0930\u0939\u093e \u0939\u0948"
     },
     "report": {
-        "report": "Report",
-        "selectReason": "Select a reason",
-        "reported": "Reported",
-        "sendingReport": "Sending report",
-        "thanksMsg": "Thanks for the report, people like you help keep our community safe!",
-        "contactAdminMsg": "If you'd like to contact an administrator about this post or report"
+        "report": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f",
+        "selectReason": "\u0915\u093e\u0930\u0923 \u091a\u0941\u0928\u0947\u0902",
+        "reported": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0915\u093f\u092f\u093e \u0917\u092f\u093e",
+        "sendingReport": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f \u092d\u0947\u091c \u0930\u0939\u0947 \u0939\u0948\u0902",
+        "thanksMsg": "\u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0915\u0947 \u0932\u093f\u090f \u0927\u0928\u094d\u092f\u0935\u093e\u0926, \u0906\u092a \u091c\u0948\u0938\u0947 \u0932\u094b\u0917 \u0939\u092e\u093e\u0930\u0947 \u0938\u092e\u0941\u0926\u093e\u092f \u0915\u094b \u0938\u0941\u0930\u0915\u094d\u0937\u093f\u0924 \u0930\u0916\u0928\u0947 \u092e\u0947\u0902 \u092e\u0926\u0926 \u0915\u0930\u0924\u0947 \u0939\u0948\u0902!",
+        "contactAdminMsg": "\u092f\u0926\u093f \u0906\u092a \u0907\u0938 \u092a\u094b\u0938\u094d\u091f \u092f\u093e \u0930\u093f\u092a\u094b\u0930\u094d\u091f \u0915\u0947 \u092c\u093e\u0930\u0947 \u092e\u0947\u0902 \u0915\u093f\u0938\u0940 \u0935\u094d\u092f\u0935\u0938\u094d\u0925\u093e\u092a\u0915 \u0938\u0947 \u0938\u0902\u092a\u0930\u094d\u0915 \u0915\u0930\u0928\u093e \u091a\u093e\u0939\u0924\u0947 \u0939\u0948\u0902"
     }
 }

+ 4 - 4
resources/assets/js/i18n/hu.json

@@ -35,7 +35,7 @@
     "navmenu": {
         "search": "Keres\u00e9s",
         "admin": "Admin ir\u00e1ny\u00edt\u00f3pult",
-        "homeFeed": "Kezd\u0151lap",
+        "homeFeed": "Saj\u00e1t id\u0151vonal",
         "localFeed": "Helyi id\u0151vonal",
         "globalFeed": "Glob\u00e1lis id\u0151vonal",
         "discover": "Felfedez\u00e9s",
@@ -48,7 +48,7 @@
         "settings": "Be\u00e1ll\u00edt\u00e1sok",
         "compose": "\u00daj l\u00e9trehoz\u00e1sa",
         "logout": "Kijelentkez\u00e9s",
-        "about": "R\u00f3lunk",
+        "about": "N\u00e9vjegy",
         "help": "S\u00fag\u00f3",
         "language": "Nyelv",
         "privacy": "Adatv\u00e9delem",
@@ -151,9 +151,9 @@
         "peopleYouMayKnow": "Emberek, akiket ismerhet",
         "onboarding": {
             "welcome": "\u00dcdv\u00f6z\u00f6lj\u00fck",
-            "thisIsYourHomeFeed": "Ez a kezd\u0151lap, az \u00d6n \u00e1ltal k\u00f6vetett fi\u00f3kok bejegyz\u00e9sei id\u0151rendi sorrendben.",
+            "thisIsYourHomeFeed": "Ez a saj\u00e1t id\u0151vonala, az \u00d6n \u00e1ltal k\u00f6vetett fi\u00f3kok bejegyz\u00e9sei id\u0151rendi sorrendben.",
             "letUsHelpYouFind": "Keress\u00fcnk \u00e9rdekes, k\u00f6vethet\u0151 embereket",
-            "refreshFeed": "Kezd\u0151lap friss\u00edt\u00e9se"
+            "refreshFeed": "Id\u0151vonal friss\u00edt\u00e9se"
         }
     },
     "hashtags": {

+ 2 - 2
resources/assets/js/i18n/it.json

@@ -20,10 +20,10 @@
         "other": "Altro",
         "readMore": "Leggi di pi\u00f9",
         "success": "Riuscito",
-        "proceed": "Continua",
+        "proceed": "Procedi",
         "next": "Avanti",
         "close": "Chiudi",
-        "clickHere": "clicca qui",
+        "clickHere": "cclicca qui",
         "sensitive": "Sensibile",
         "sensitiveContent": "Contenuto Sensibile",
         "sensitiveContentWarning": "Questo post potrebbe contenere contenuti sensibili"

+ 44 - 44
resources/assets/js/i18n/pt.json

@@ -15,7 +15,7 @@
         "copyLink": "Copiar link",
         "delete": "Eliminar",
         "error": "Erro",
-        "errorMsg": "Algo correu mal. Por favor, tente novamente mais tarde.",
+        "errorMsg": "Algo correu mal. Tenta novamente mais tarde.",
         "oops": "Oops!",
         "other": "Outro",
         "readMore": "Ler mais",
@@ -23,23 +23,23 @@
         "proceed": "Continuar",
         "next": "Seguinte",
         "close": "Fechar",
-        "clickHere": "clique aqui",
+        "clickHere": "clica aqui",
         "sensitive": "Sens\u00edvel",
         "sensitiveContent": "Conte\u00fado sens\u00edvel",
-        "sensitiveContentWarning": "Este post pode conter conte\u00fado sens\u00edvel"
+        "sensitiveContentWarning": "Esta publica\u00e7\u00e3o pode conter conte\u00fado sens\u00edvel"
     },
     "site": {
-        "terms": "Termos de Uso",
+        "terms": "Termos de Utiliza\u00e7\u00e3o",
         "privacy": "Pol\u00edtica de Privacidade"
     },
     "navmenu": {
-        "search": "Pesquisa",
-        "admin": "Painel de Administra\u00e7\u00e3o",
-        "homeFeed": "Inicio",
+        "search": "Pesquisar",
+        "admin": "Painel de administra\u00e7\u00e3o",
+        "homeFeed": "In\u00edcio",
         "localFeed": "Feed local",
         "globalFeed": "Feed global",
         "discover": "Descobrir",
-        "directMessages": "Mensagens Diretas",
+        "directMessages": "Mensagens diretas",
         "notifications": "Notifica\u00e7\u00f5es",
         "groups": "Grupos",
         "stories": "Stories",
@@ -47,7 +47,7 @@
         "drive": "Disco",
         "settings": "Defini\u00e7\u00f5es",
         "compose": "Criar novo",
-        "logout": "Terminar Sess\u00e3o",
+        "logout": "Terminar sess\u00e3o",
         "about": "Sobre",
         "help": "Ajuda",
         "language": "Idioma",
@@ -56,7 +56,7 @@
         "backToPreviousDesign": "Voltar ao design antigo"
     },
     "directMessages": {
-        "inbox": "Caixa de Entrada",
+        "inbox": "Caixa de entrada",
         "sent": "Enviadas",
         "requests": "Pedidos"
     },
@@ -64,21 +64,21 @@
         "liked": "gostou do seu",
         "commented": "comentou no seu",
         "reacted": "reagiu ao seu",
-        "shared": "Partilhou o seu",
-        "tagged": "marcou voc\u00ea numa publica\u00e7\u00e3o",
-        "updatedA": "atualizou",
-        "sentA": "enviou um",
-        "followed": "seguiu",
-        "mentioned": "mencionou",
-        "you": "voc\u00ea",
-        "yourApplication": "A sua candidatura para se juntar",
+        "shared": "partilhou o teu",
+        "tagged": "etiquetou-te numa publica\u00e7\u00e3o",
+        "updatedA": "atualizou uma",
+        "sentA": "enviou uma",
+        "followed": "seguiu-te",
+        "mentioned": "mencionou-te",
+        "you": "tu",
+        "yourApplication": "O teu pedido de ades\u00e3o",
         "applicationApproved": "foi aprovado!",
-        "applicationRejected": "foi rejeitado. Voc\u00ea pode inscrever-se novamente em 6 meses.",
-        "dm": "dm",
+        "applicationRejected": "foi rejeitado. Podes voltar a candidatar-te dentro de 6 meses.",
+        "dm": "md",
         "groupPost": "publica\u00e7\u00e3o de grupo",
         "modlog": "hist\u00f3rico de modera\u00e7\u00e3o",
         "post": "publica\u00e7\u00e3o",
-        "story": "story",
+        "story": "est\u00f3ria",
         "noneFound": "Nenhuma notifica\u00e7\u00e3o encontrada"
     },
     "post": {
@@ -90,12 +90,12 @@
     "profile": {
         "posts": "Publica\u00e7\u00f5es",
         "followers": "Seguidores",
-        "following": "A seguir",
-        "admin": "Admin",
+        "following": "Seguindo",
+        "admin": "Administrador",
         "collections": "Cole\u00e7\u00f5es",
         "follow": "Seguir",
         "unfollow": "Deixar de seguir",
-        "editProfile": "Editar Perfil",
+        "editProfile": "Editar perfil",
         "followRequested": "Pedido para seguir enviado",
         "joined": "Juntou-se",
         "emptyCollections": "N\u00e3o conseguimos encontrar nenhuma cole\u00e7\u00e3o",
@@ -109,51 +109,51 @@
         "archive": "Arquivar",
         "unarchive": "Retirar do arquivo",
         "embed": "Incorporar",
-        "selectOneOption": "Selecione uma das seguintes op\u00e7\u00f5es",
+        "selectOneOption": "Seleciona uma das seguintes op\u00e7\u00f5es",
         "unlistFromTimelines": "Remover das cronologias",
         "addCW": "Adicionar aviso de conte\u00fado",
         "removeCW": "Remover aviso de conte\u00fado",
         "markAsSpammer": "Marcar como spammer",
         "markAsSpammerText": "Remover das cronologias e adicionar um aviso de conte\u00fado \u00e0s publica\u00e7\u00f5es existentes e futuras",
         "spam": "Spam",
-        "sensitive": "Conte\u00fado Sens\u00edvel",
+        "sensitive": "Conte\u00fado sens\u00edvel",
         "abusive": "Abusivo ou prejudicial",
         "underageAccount": "Conta de menor de idade",
         "copyrightInfringement": "Viola\u00e7\u00e3o de direitos de autor",
-        "impersonation": "Roubo de Identidade",
+        "impersonation": "Roubo de identidade",
         "scamOrFraud": "Esquema ou fraude",
         "confirmReport": "Confirmar den\u00fancia",
-        "confirmReportText": "Tem a certeza que deseja denunciar esta mensagem?",
+        "confirmReportText": "Tens a certeza que desejas denunciar esta mensagem?",
         "reportSent": "Den\u00fancia enviada!",
-        "reportSentText": "Recebemos com sucesso a sua den\u00fancia.",
+        "reportSentText": "Recebemos com sucesso a tua den\u00fancia.",
         "reportSentError": "Ocorreu um erro ao denunciar este conte\u00fado.",
-        "modAddCWConfirm": "Tem a certeza que pretende adicionar um aviso de conte\u00fado \u00e0 publica\u00e7\u00e3o?",
-        "modCWSuccess": "Adicionou com sucesso um aviso de conte\u00fado",
-        "modRemoveCWConfirm": "Tem a certeza que pretende remover o aviso de conte\u00fado desta publica\u00e7\u00e3o?",
-        "modRemoveCWSuccess": "Removeu com sucesso o aviso de conte\u00fado",
+        "modAddCWConfirm": "Tens a certeza que pretendes adicionar um aviso de conte\u00fado \u00e0 publica\u00e7\u00e3o?",
+        "modCWSuccess": "Adicionaste com sucesso um aviso de conte\u00fado",
+        "modRemoveCWConfirm": "Tens a certeza que pretendes remover o aviso de conte\u00fado desta publica\u00e7\u00e3o?",
+        "modRemoveCWSuccess": "Removeste com sucesso o aviso de conte\u00fado",
         "modUnlistConfirm": "Tem a certeza que pretende deslistar este post?",
         "modUnlistSuccess": "Deslistou com sucesso este post",
         "modMarkAsSpammerConfirm": "Tem a certeza que deseja marcar este utilizador como spammer? Todos os posts existentes e futuros ser\u00e3o deslistados da timeline e o alerta de conte\u00fado ser\u00e1 aplicado.",
         "modMarkAsSpammerSuccess": "Marcou com sucesso esta conta como spammer",
         "toFollowers": "para Seguidores",
         "showCaption": "Mostar legenda",
-        "showLikes": "Mostrar Gostos",
+        "showLikes": "Mostrar gostos",
         "compactMode": "Modo compacto",
-        "embedConfirmText": "Ao utilizar este conte\u00fado, aceita os nossos",
-        "deletePostConfirm": "Tem a certeza que pretende apagar esta publica\u00e7\u00e3o?",
-        "archivePostConfirm": "Tem a certeza que pretende arquivar esta publica\u00e7\u00e3o?",
+        "embedConfirmText": "Ao utilizar este conte\u00fado, concordas com:",
+        "deletePostConfirm": "Tens a certeza que pretendes eliminar esta publica\u00e7\u00e3o?",
+        "archivePostConfirm": "Tens a certeza que pretendes arquivar esta publica\u00e7\u00e3o?",
         "unarchivePostConfirm": "Tem a certeza que pretende desarquivar este post?"
     },
     "story": {
         "add": "Adicionar Storie"
     },
     "timeline": {
-        "peopleYouMayKnow": "Pessoas que talvez conhe\u00e7a",
+        "peopleYouMayKnow": "Pessoas que talvez conhe\u00e7as",
         "onboarding": {
             "welcome": "Bem-vindo",
-            "thisIsYourHomeFeed": "Este \u00e9 o seu feed pessoal, com publica\u00e7\u00f5es em ordem cronol\u00f3gica das contas que segue.",
-            "letUsHelpYouFind": "Deixe-nos ajudar a encontrar algumas pessoas interessantes para seguir",
-            "refreshFeed": "Atualizar o meu feed"
+            "thisIsYourHomeFeed": "Este \u00e9 a tua cronologia inicial pessoal, com publica\u00e7\u00f5es em ordem cronol\u00f3gica das contas que segue.",
+            "letUsHelpYouFind": "Deixa-nos ajudar-te a encontrar algumas pessoas interessantes para seguires",
+            "refreshFeed": "Atualizar a minha cronologia"
         }
     },
     "hashtags": {
@@ -161,10 +161,10 @@
     },
     "report": {
         "report": "Denunciar",
-        "selectReason": "Selecione uma raz\u00e3o",
+        "selectReason": "Seleciona um motivo",
         "reported": "Denunciado",
         "sendingReport": "A enviar den\u00fancia",
-        "thanksMsg": "Obrigado pela den\u00fancia, pessoas como voc\u00ea ajudam a manter a nossa comunidade segura!",
-        "contactAdminMsg": "Se quiser entrar em contato com um administrador acerca desta publica\u00e7\u00e3o ou den\u00fancia"
+        "thanksMsg": "Obrigado pela den\u00fancia, as pessoas como tu ajudam a manter a nossa comunidade segura!",
+        "contactAdminMsg": "Se quiseres entrar em contacto com um administrador sobre esta publica\u00e7\u00e3o ou den\u00fancia"
     }
 }

+ 21 - 21
resources/assets/js/i18n/ru.json

@@ -24,9 +24,9 @@
         "next": "\u0414\u0430\u043b\u0435\u0435",
         "close": "\u0417\u0430\u043a\u0440\u044b\u0442\u044c",
         "clickHere": "\u043d\u0430\u0436\u043c\u0438\u0442\u0435 \u0437\u0434\u0435\u0441\u044c",
-        "sensitive": "\u0427\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439",
-        "sensitiveContent": "\u0427\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442",
-        "sensitiveContentWarning": "\u042d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442"
+        "sensitive": "\u0412\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439",
+        "sensitiveContent": "\u0412\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435",
+        "sensitiveContentWarning": "\u042d\u0442\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435"
     },
     "site": {
         "terms": "\u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f",
@@ -71,24 +71,24 @@
         "followed": "\u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043b\u0441\u044f",
         "mentioned": "\u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b(\u0430)",
         "you": "\u0432\u044b",
-        "yourApplication": "\u0412\u0430\u0448\u0435 \u0437\u0430\u044f\u0432\u043a\u0430 \u043d\u0430 \u0432\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435",
-        "applicationApproved": "\u0431\u044b\u043b\u043e \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u043e!",
-        "applicationRejected": "\u0431\u044b\u043b\u043e \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u043e. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u043e\u0434\u0430\u0442\u044c \u0437\u0430\u044f\u0432\u043a\u0443 \u043d\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 6 \u043c\u0435\u0441\u044f\u0446\u0435\u0432.",
+        "yourApplication": "\u0412\u0430\u0448\u0430 \u0437\u0430\u044f\u0432\u043a\u0430 \u043d\u0430 \u0432\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435",
+        "applicationApproved": "\u0431\u044b\u043b\u0430 \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u0430!",
+        "applicationRejected": "\u0431\u044b\u043b\u0430 \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0430. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u043e\u0434\u0430\u0442\u044c \u0437\u0430\u044f\u0432\u043a\u0443 \u043d\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 6 \u043c\u0435\u0441\u044f\u0446\u0435\u0432.",
         "dm": "\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435",
-        "groupPost": "\u043f\u043e\u0441\u0442 \u0432 \u0433\u0440\u0443\u043f\u043f\u0435",
+        "groupPost": "\u0437\u0430\u043f\u0438\u0441\u044c \u0432 \u0433\u0440\u0443\u043f\u043f\u0435",
         "modlog": "\u0436\u0443\u0440\u043d\u0430\u043b \u043c\u043e\u0434\u0435\u0440\u0430\u0446\u0438\u0438",
-        "post": "\u043f\u043e\u0441\u0442",
+        "post": "\u0437\u0430\u043f\u0438\u0441\u044c",
         "story": "\u0438\u0441\u0442\u043e\u0440\u0438\u044f",
         "noneFound": "\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b"
     },
     "post": {
         "shareToFollowers": "\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c\u0438",
         "shareToOther": "\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438",
-        "noLikes": "\u041f\u043e\u043a\u0430 \u043d\u0438\u043a\u043e\u043c\u0443 \u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c",
+        "noLikes": "\u041f\u043e\u043a\u0430 \u043d\u0435\u0442 \u043b\u0430\u0439\u043a\u043e\u0432",
         "uploading": "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f"
     },
     "profile": {
-        "posts": "\u041f\u043e\u0441\u0442\u044b",
+        "posts": "\u0417\u0430\u043f\u0438\u0441\u0438",
         "followers": "\u041f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0438",
         "following": "\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438",
         "admin": "\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440",
@@ -102,7 +102,7 @@
         "emptyPosts": "\u041f\u043e\u0445\u043e\u0436\u0435, \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0439\u0442\u0438 \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438"
     },
     "menu": {
-        "viewPost": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u0441\u0442",
+        "viewPost": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c",
         "viewProfile": "\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u0440\u043e\u0444\u0438\u043b\u044c",
         "moderationTools": "\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043c\u043e\u0434\u0435\u0440\u0430\u0446\u0438\u0438",
         "report": "\u041f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c\u0441\u044f",
@@ -118,22 +118,22 @@
         "spam": "\u0421\u043f\u0430\u043c",
         "sensitive": "\u0414\u0435\u043b\u0438\u043a\u0430\u0442\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442",
         "abusive": "\u0416\u0435\u0441\u0442\u043e\u043a\u043e\u0435 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0438\u043b\u0438 \u043f\u0440\u0438\u0447\u0438\u043d\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u0434\u0430",
-        "underageAccount": "\u041d\u0435\u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e\u043b\u0435\u0442\u043d\u0438\u0439 \u0430\u043a\u043a\u0430\u0443\u043d\u0442",
+        "underageAccount": "\u041f\u0440\u043e\u0444\u0438\u043b\u044c \u043d\u0435\u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e\u043b\u0435\u0442\u043d\u0435\u0433\u043e",
         "copyrightInfringement": "\u041d\u0430\u0440\u0443\u0448\u0435\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0441\u043a\u0438\u0445 \u043f\u0440\u0430\u0432",
         "impersonation": "\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0435\u0431\u044f \u0437\u0430 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u0430",
         "scamOrFraud": "\u041e\u0431\u043c\u0430\u043d \u0438\u043b\u0438 \u043c\u043e\u0448\u0435\u043d\u043d\u0438\u0447\u0435\u0441\u0442\u0432\u043e",
         "confirmReport": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0436\u0430\u043b\u043e\u0431\u0443",
-        "confirmReportText": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442?",
+        "confirmReportText": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u0442\u0443 \u0437\u0430\u043f\u0438\u0441\u044c?",
         "reportSent": "\u0416\u0430\u043b\u043e\u0431\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430!",
         "reportSentText": "\u041c\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0412\u0430\u0448\u0443 \u0436\u0430\u043b\u043e\u0431\u0443.",
-        "reportSentError": "\u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0436\u0430\u043b\u043e\u0431\u044b \u043d\u0430 \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430.",
-        "modAddCWConfirm": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435 \u043d\u0430 \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442?",
+        "reportSentError": "\u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0436\u0430\u043b\u043e\u0431\u044b \u043d\u0430 \u044d\u0442\u0443 \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430.",
+        "modAddCWConfirm": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c \u043d\u0430 \u044d\u0442\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438?",
         "modCWSuccess": "\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e",
-        "modRemoveCWConfirm": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435 \u0441 \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0430?",
+        "modRemoveCWConfirm": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c \u0441 \u044d\u0442\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438?",
         "modRemoveCWSuccess": "\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u043e",
-        "modUnlistConfirm": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043a\u0440\u044b\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442 \u0438\u0437 \u043b\u0435\u043d\u0442?",
-        "modUnlistSuccess": "\u041f\u043e\u0441\u0442 \u0443\u0434\u0430\u043b\u0435\u043d",
-        "modMarkAsSpammerConfirm": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u043f\u0430\u043c\u043e\u043c? \u0412\u0441\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0438 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0432 \u0441\u0440\u043e\u043a\u0438, \u0438 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0438.",
+        "modUnlistConfirm": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043a\u0440\u044b\u0442\u044c \u044d\u0442\u0443 \u0437\u0430\u043f\u0438\u0441\u044c \u0441\u043e \u0432\u0441\u0435\u0445 \u043b\u0435\u043d\u0442?",
+        "modUnlistSuccess": "\u0417\u0430\u043f\u0438\u0441\u044c \u0441\u043a\u0440\u044b\u0442\u0430",
+        "modMarkAsSpammerConfirm": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a\u0430\u043a \u0441\u043f\u0430\u043c\u0435\u0440\u0430? \u0412\u0441\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0438 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0441\u043a\u0440\u044b\u0442\u044b \u0432 \u043b\u0435\u043d\u0442\u0430\u0445 \u0438 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0438.",
         "modMarkAsSpammerSuccess": "\u0410\u043a\u043a\u0430\u0443\u043d\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u043c\u0435\u0447\u0435\u043d \u043a\u0430\u043a \u0441\u043f\u0430\u043c\u043c\u0435\u0440",
         "toFollowers": "\u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c",
         "showCaption": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u0434\u043f\u0438\u0441\u044c",
@@ -151,7 +151,7 @@
         "peopleYouMayKnow": "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0434\u0440\u0443\u0437\u044c\u044f",
         "onboarding": {
             "welcome": "\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c",
-            "thisIsYourHomeFeed": "\u042d\u0442\u043e \u0432\u0430\u0448\u0430 \u0434\u043e\u043c\u0430\u0448\u043d\u044f\u044f \u043b\u0435\u043d\u0442\u0430, \u0433\u0434\u0435 \u0445\u0440\u043e\u043d\u043e\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u043f\u043e\u0441\u0442\u044b \u0441 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u0432, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u044b.",
+            "thisIsYourHomeFeed": "\u042d\u0442\u043e \u0432\u0430\u0448\u0430 \u0434\u043e\u043c\u0430\u0448\u043d\u044f\u044f \u043b\u0435\u043d\u0442\u0430, \u0433\u0434\u0435 \u0445\u0440\u043e\u043d\u043e\u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0431\u0440\u0430\u043d\u044b \u0437\u0430\u043f\u0438\u0441\u0438 \u043e\u0442 \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u0439 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u044b.",
             "letUsHelpYouFind": "\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043c\u044b \u043f\u043e\u043c\u043e\u0436\u0435\u043c \u0432\u0430\u043c \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043b\u044e\u0434\u0435\u0439, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f",
             "refreshFeed": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043c\u043e\u044e \u043b\u0435\u043d\u0442\u0443"
         }
@@ -165,6 +165,6 @@
         "reported": "\u0416\u0430\u043b\u043e\u0431\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430",
         "sendingReport": "\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0436\u0430\u043b\u043e\u0431\u044b",
         "thanksMsg": "\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435! \u0422\u0430\u043a\u0438\u0435, \u043a\u0430\u043a \u0432\u044b, \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0435\u0435!",
-        "contactAdminMsg": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043b\u0438 \u0432\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c\u0441\u044f \u0441 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 \u044d\u0442\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0436\u0430\u043b\u043e\u0431\u044b"
+        "contactAdminMsg": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043b\u0438 \u0432\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c\u0441\u044f \u0441 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u043c \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 \u044d\u0442\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c\u0441\u044f"
     }
 }

+ 2 - 2
resources/assets/js/i18n/sk.json

@@ -151,9 +151,9 @@
         "peopleYouMayKnow": "\u013dudia ktor\u00fdch mo\u017eno pozn\u00e1te",
         "onboarding": {
             "welcome": "Vitajte",
-            "thisIsYourHomeFeed": "This is your home feed, a chronological feed of posts from accounts you follow.",
+            "thisIsYourHomeFeed": "Toto je tvoj dom\u00e1ci kan\u00e1l pr\u00edspevkov z \u00fa\u010dtov, ktor\u00e9 nasleduje\u0161.",
             "letUsHelpYouFind": "Nechajte n\u00e1s pom\u00f4c\u0165 v\u00e1m n\u00e1js\u0165 zauj\u00edmav\u00fdch \u013eud\u00ed ktor\u00fdch m\u00f4\u017eete sledova\u0165",
-            "refreshFeed": "Refresh my feed"
+            "refreshFeed": "Obnovi\u0165 m\u00f4j kan\u00e1l"
         }
     },
     "hashtags": {

+ 17 - 11
resources/assets/js/spa.js

@@ -697,34 +697,40 @@ window.App.util = {
 			}
 			return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count);
 		}),
-		timeAgo: (function(ts) {
-			let date = Date.parse(ts);
-			let seconds = Math.floor((new Date() - date) / 1000);
-			let interval = Math.floor(seconds / 63072000);
-			if (interval < 0) {
-				return "0s";
-			}
+		timeAgo: function(ts) {
+			const date = new Date(ts);
+			const now = new Date();
+
+			const seconds = Math.floor((now - date) / 1000);
+
+			const secondsInYear = 60 * 60 * 24 * 365.25;
+			let interval = Math.floor(seconds / secondsInYear);
 			if (interval >= 1) {
 				return interval + "y";
 			}
-			interval = Math.floor(seconds / 604800);
+
+			interval = Math.floor(seconds / (60 * 60 * 24 * 7));
 			if (interval >= 1) {
 				return interval + "w";
 			}
-			interval = Math.floor(seconds / 86400);
+
+			interval = Math.floor(seconds / (60 * 60 * 24));
 			if (interval >= 1) {
 				return interval + "d";
 			}
-			interval = Math.floor(seconds / 3600);
+
+			interval = Math.floor(seconds / (60 * 60));
 			if (interval >= 1) {
 				return interval + "h";
 			}
+
 			interval = Math.floor(seconds / 60);
 			if (interval >= 1) {
 				return interval + "m";
 			}
+
 			return Math.floor(seconds) + "s";
-		}),
+		},
 		timeAhead: (function(ts, short = true) {
 			let date = Date.parse(ts);
 			let diff = date - Date.parse(new Date());

+ 4 - 4
resources/views/status/reply.blade.php

@@ -28,7 +28,7 @@
                   <div class="media-body">
                     <span class="font-weight-bold" v-pre>{{$gp->profile->username}}</span>
                     <div class="">
-                      <p class="w-100 text-break" v-pre>{!!$gp->rendered!!}</p>
+                      <p class="w-100 text-break" v-pre>{!!$gp->caption!!}</p>
                     </div>
                     <div class="mb-0 small">
                       <a href="{{$gp->url()}}" class="text-muted">
@@ -65,7 +65,7 @@
                   <div class="media-body">
                     <span class="font-weight-bold" v-pre>{{$parent->profile->username}}</span>
                     <div class="">
-                      <p class="w-100 text-break" v-pre>{!!$parent->rendered!!}</p>
+                      <p class="w-100 text-break" v-pre>{!!$parent->caption!!}</p>
                     </div>
                     <div class="mb-0 small">
                       <a href="{{$parent->url()}}" class="text-muted">
@@ -92,7 +92,7 @@
               <img class="mr-3 rounded-circle img-thumbnail" src="{{$status->profile->avatarUrl()}}" width="60px" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
               <div class="media-body">
                 <h5 class="mt-0 font-weight-bold" v-pre>{{$status->profile->username}}</h5>
-                <p class="" v-pre>{!! $status->rendered !!}</p>
+                <p class="" v-pre>{!! $status->caption !!}</p>
                 <div class="mb-0 small">
                   <a href="{{$status->url()}}" class="text-muted">
                     {{$status->created_at->diffForHumans()}}
@@ -106,7 +106,7 @@
             <img class="mr-3 rounded-circle img-thumbnail" src="{{$status->profile->avatarUrl()}}" width="60px" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
             <div class="media-body">
               <h5 class="mt-0 font-weight-bold" v-pre>{{$status->profile->username}}</h5>
-              <p class="" v-pre>{!! $status->rendered !!}</p>
+              <p class="" v-pre>{!! $status->caption !!}</p>
               <div class="mb-0 small">
                 <a href="{{$status->url()}}" class="text-muted">
                   {{$status->created_at->diffForHumans()}}