diff --git a/app/Console/Commands/FixHashtags.php b/app/Console/Commands/FixHashtags.php
new file mode 100644
index 000000000..dd292e6f5
--- /dev/null
+++ b/app/Console/Commands/FixHashtags.php
@@ -0,0 +1,109 @@
+info(' ____ _ ______ __ ');
+ $this->info(' / __ \(_) _____ / / __/__ ____/ / ');
+ $this->info(' / /_/ / / |/_/ _ \/ / /_/ _ \/ __ / ');
+ $this->info(' / ____/ /> __/ / __/ __/ /_/ / ');
+ $this->info(' /_/ /_/_/|_|\___/_/_/ \___/\__,_/ ');
+ $this->info(' ');
+ $this->info(' ');
+ $this->info('Pixelfed version: ' . config('pixelfed.version'));
+ $this->info(' ');
+ $this->info('Running Fix Hashtags command');
+ $this->info(' ');
+
+ $missingCount = StatusHashtag::doesntHave('profile')->doesntHave('status')->count();
+ if($missingCount > 0) {
+ $this->info("Found {$missingCount} orphaned StatusHashtag records to delete ...");
+ $this->info(' ');
+ $bar = $this->output->createProgressBar($missingCount);
+ $bar->start();
+ foreach(StatusHashtag::doesntHave('profile')->doesntHave('status')->get() as $tag) {
+ $tag->delete();
+ $bar->advance();
+ }
+ $bar->finish();
+ $this->info(' ');
+ } else {
+ $this->info(' ');
+ $this->info('Found no orphaned hashtags to delete!');
+ }
+
+
+ $this->info(' ');
+
+ $count = StatusHashtag::whereNull('status_visibility')->count();
+ if($count > 0) {
+ $this->info("Found {$count} hashtags to fix ...");
+ $this->info(' ');
+ } else {
+ $this->info('Found no hashtags to fix!');
+ $this->info(' ');
+ return;
+ }
+
+ $bar = $this->output->createProgressBar($count);
+ $bar->start();
+
+ StatusHashtag::with('status')
+ ->whereNull('status_visibility')
+ ->chunk(50, function($tags) use($bar) {
+ foreach($tags as $tag) {
+ if(!$tag->status || !$tag->status->scope) {
+ continue;
+ }
+ $tag->status_visibility = $tag->status->scope;
+ $tag->save();
+ $bar->advance();
+ }
+ });
+
+ $bar->finish();
+ $this->info(' ');
+ $this->info(' ');
+ }
+}
diff --git a/app/HashtagFollow.php b/app/HashtagFollow.php
new file mode 100644
index 000000000..0503330b0
--- /dev/null
+++ b/app/HashtagFollow.php
@@ -0,0 +1,19 @@
+belongsTo(Hashtag::class);
+ }
+}
diff --git a/app/Http/Controllers/Api/BaseApiController.php b/app/Http/Controllers/Api/BaseApiController.php
index a18efa5d2..6fa1073f2 100644
--- a/app/Http/Controllers/Api/BaseApiController.php
+++ b/app/Http/Controllers/Api/BaseApiController.php
@@ -59,14 +59,11 @@ class BaseApiController extends Controller
$res = $this->fractal->createData($resource)->toArray();
} else {
$this->validate($request, [
- 'page' => 'nullable|integer|min:1',
+ 'page' => 'nullable|integer|min:1|max:10',
'limit' => 'nullable|integer|min:1|max:10'
]);
$limit = $request->input('limit') ?? 10;
$page = $request->input('page') ?? 1;
- if($page > 3) {
- return response()->json([]);
- }
$end = (int) $page * $limit;
$start = (int) $end - $limit;
$res = NotificationService::get($pid, $start, $end);
diff --git a/app/Http/Controllers/DiscoverController.php b/app/Http/Controllers/DiscoverController.php
index bbc8d765c..1a0e6d23e 100644
--- a/app/Http/Controllers/DiscoverController.php
+++ b/app/Http/Controllers/DiscoverController.php
@@ -6,6 +6,7 @@ use App\{
DiscoverCategory,
Follower,
Hashtag,
+ HashtagFollow,
Profile,
Status,
StatusHashtag,
@@ -17,6 +18,7 @@ use App\Transformer\Api\StatusStatelessTransformer;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
+use App\Services\StatusHashtagService;
class DiscoverController extends Controller
{
@@ -36,57 +38,11 @@ class DiscoverController extends Controller
public function showTags(Request $request, $hashtag)
{
- abort_if(!Auth::check(), 403);
+ abort_if(!config('instance.discover.tags.is_public') && !Auth::check(), 403);
- $tag = Hashtag::whereSlug($hashtag)
- ->firstOrFail();
-
- $page = 1;
- $key = 'discover:tag-'.$tag->id.':page-'.$page;
- $keyMinutes = 15;
-
- $posts = Cache::remember($key, now()->addMinutes($keyMinutes), function() use ($tag, $request) {
- $tags = StatusHashtag::select('status_id')
- ->whereHashtagId($tag->id)
- ->orderByDesc('id')
- ->take(48)
- ->pluck('status_id');
-
- return Status::select(
- 'id',
- 'uri',
- 'caption',
- 'rendered',
- 'profile_id',
- 'type',
- 'in_reply_to_id',
- 'reblog_of_id',
- 'is_nsfw',
- 'scope',
- 'local',
- 'created_at',
- 'updated_at'
- )->whereIn('type', ['photo', 'photo:album', 'video', 'video:album'])
- ->with('media')
- ->whereLocal(true)
- ->whereNull('uri')
- ->whereIn('id', $tags)
- ->whereNull('in_reply_to_id')
- ->whereNull('reblog_of_id')
- ->whereNull('url')
- ->whereNull('uri')
- ->withCount(['likes', 'comments'])
- ->whereIsNsfw(false)
- ->whereVisibility('public')
- ->orderBy('id', 'desc')
- ->get();
- });
-
- if($posts->count() == 0) {
- abort(404);
- }
-
- return view('discover.tags.show', compact('tag', 'posts'));
+ $tag = Hashtag::whereSlug($hashtag)->firstOrFail();
+ $tagCount = StatusHashtagService::count($tag->id);
+ return view('discover.tags.show', compact('tag', 'tagCount'));
}
public function showCategory(Request $request, $slug)
@@ -156,7 +112,6 @@ class DiscoverController extends Controller
return $res;
}
-
public function loopWatch(Request $request)
{
abort_if(!Auth::check(), 403);
@@ -171,4 +126,26 @@ class DiscoverController extends Controller
return response()->json(200);
}
+
+ public function getHashtags(Request $request)
+ {
+ $auth = Auth::check();
+ abort_if(!config('instance.discover.tags.is_public') && !$auth, 403);
+
+ $this->validate($request, [
+ 'hashtag' => 'required|alphanum|min:2|max:124',
+ 'page' => 'nullable|integer|min:1|max:' . ($auth ? 19 : 3)
+ ]);
+
+ $page = $request->input('page') ?? '1';
+ $end = $page > 1 ? $page * 9 : 1;
+ $tag = $request->input('hashtag');
+
+ $hashtag = Hashtag::whereName($tag)->firstOrFail();
+ $res['tags'] = StatusHashtagService::get($hashtag->id, $page, $end);
+ if($page == 1) {
+ $res['follows'] = HashtagFollow::whereUserId(Auth::id())->whereHashtagId($hashtag->id)->exists();
+ }
+ return $res;
+ }
}
diff --git a/app/Http/Controllers/HashtagFollowController.php b/app/Http/Controllers/HashtagFollowController.php
new file mode 100644
index 000000000..585248abb
--- /dev/null
+++ b/app/Http/Controllers/HashtagFollowController.php
@@ -0,0 +1,61 @@
+middleware('auth');
+ }
+
+ public function store(Request $request)
+ {
+ $this->validate($request, [
+ 'name' => 'required|alpha_num|min:1|max:124|exists:hashtags,name'
+ ]);
+
+ $user = Auth::user();
+ $profile = $user->profile;
+ $tag = $request->input('name');
+
+ $hashtag = Hashtag::whereName($tag)->firstOrFail();
+
+ $hashtagFollow = HashtagFollow::firstOrCreate([
+ 'user_id' => $user->id,
+ 'profile_id' => $user->profile_id ?? $user->profile->id,
+ 'hashtag_id' => $hashtag->id
+ ]);
+
+ if($hashtagFollow->wasRecentlyCreated) {
+ $state = 'created';
+ // todo: send to HashtagFollowService
+ } else {
+ $state = 'deleted';
+ $hashtagFollow->delete();
+ }
+
+ return [
+ 'state' => $state
+ ];
+ }
+
+ public function getTags(Request $request)
+ {
+ return HashtagFollow::with('hashtag')->whereUserId(Auth::id())
+ ->inRandomOrder()
+ ->take(3)
+ ->get()
+ ->map(function($follow, $k) {
+ return $follow->hashtag->name;
+ });
+ }
+}
diff --git a/app/Http/Controllers/PublicApiController.php b/app/Http/Controllers/PublicApiController.php
index dce50d009..bd22505e3 100644
--- a/app/Http/Controllers/PublicApiController.php
+++ b/app/Http/Controllers/PublicApiController.php
@@ -211,6 +211,10 @@ class PublicApiController extends Controller
'limit' => 'nullable|integer|max:20'
]);
+ if(config('instance.timeline.local.is_public') == false && !Auth::check()) {
+ abort(403, 'Authentication required.');
+ }
+
$page = $request->input('page');
$min = $request->input('min_id');
$max = $request->input('max_id');
@@ -331,6 +335,8 @@ class PublicApiController extends Controller
->orWhere('status', '!=', null)
->pluck('id');
});
+
+ $private = $private->diff($following)->flatten();
$filters = UserFilter::whereUserId($pid)
->whereFilterableType('App\Profile')
diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php
index 6c9d4b83c..fc336c301 100644
--- a/app/Http/Controllers/SearchController.php
+++ b/app/Http/Controllers/SearchController.php
@@ -143,6 +143,7 @@ class SearchController extends Controller
'tokens' => [$item->caption],
'name' => $item->caption,
'thumb' => $item->thumb(),
+ 'filter' => $item->firstMedia()->filter_class
];
});
$tokens['posts'] = $posts;
diff --git a/app/Jobs/DeletePipeline/DeleteAccountPipeline.php b/app/Jobs/DeletePipeline/DeleteAccountPipeline.php
index ba265379d..06e8191a6 100644
--- a/app/Jobs/DeletePipeline/DeleteAccountPipeline.php
+++ b/app/Jobs/DeletePipeline/DeleteAccountPipeline.php
@@ -80,8 +80,10 @@ class DeleteAccountPipeline implements ShouldQueue
Bookmark::whereProfileId($user->profile->id)->forceDelete();
EmailVerification::whereUserId($user->id)->forceDelete();
-
$id = $user->profile->id;
+
+ StatusHashtag::whereProfileId($id)->delete();
+
FollowRequest::whereFollowingId($id)->orWhere('follower_id', $id)->forceDelete();
Follower::whereProfileId($id)->orWhere('following_id', $id)->forceDelete();
diff --git a/app/Jobs/StatusPipeline/StatusEntityLexer.php b/app/Jobs/StatusPipeline/StatusEntityLexer.php
index ecad11e1e..197477672 100644
--- a/app/Jobs/StatusPipeline/StatusEntityLexer.php
+++ b/app/Jobs/StatusPipeline/StatusEntityLexer.php
@@ -89,6 +89,9 @@ class StatusEntityLexer implements ShouldQueue
$status = $this->status;
foreach ($tags as $tag) {
+ if(mb_strlen($tag) > 124) {
+ continue;
+ }
DB::transaction(function () use ($status, $tag) {
$slug = str_slug($tag, '-', false);
$hashtag = Hashtag::firstOrCreate(
@@ -98,7 +101,8 @@ class StatusEntityLexer implements ShouldQueue
[
'status_id' => $status->id,
'hashtag_id' => $hashtag->id,
- 'profile_id' => $status->profile_id
+ 'profile_id' => $status->profile_id,
+ 'status_visibility' => $status->visibility,
]
);
});
diff --git a/app/Observers/StatusHashtagObserver.php b/app/Observers/StatusHashtagObserver.php
new file mode 100644
index 000000000..51832bcb8
--- /dev/null
+++ b/app/Observers/StatusHashtagObserver.php
@@ -0,0 +1,64 @@
+hashtag_id, $hashtag->status_id);
+ }
+
+ /**
+ * Handle the notification "updated" event.
+ *
+ * @param \App\Notification $notification
+ * @return void
+ */
+ public function updated(StatusHashtag $hashtag)
+ {
+ StatusHashtagService::set($hashtag->hashtag_id, $hashtag->status_id);
+ }
+
+ /**
+ * Handle the notification "deleted" event.
+ *
+ * @param \App\Notification $notification
+ * @return void
+ */
+ public function deleted(StatusHashtag $hashtag)
+ {
+ StatusHashtagService::del($hashtag->hashtag_id, $hashtag->status_id);
+ }
+
+ /**
+ * Handle the notification "restored" event.
+ *
+ * @param \App\Notification $notification
+ * @return void
+ */
+ public function restored(StatusHashtag $hashtag)
+ {
+ StatusHashtagService::set($hashtag->hashtag_id, $hashtag->status_id);
+ }
+
+ /**
+ * Handle the notification "force deleted" event.
+ *
+ * @param \App\Notification $notification
+ * @return void
+ */
+ public function forceDeleted(StatusHashtag $hashtag)
+ {
+ StatusHashtagService::del($hashtag->hashtag_id, $hashtag->status_id);
+ }
+}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index cf52e7fd4..6bfe29459 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -5,11 +5,13 @@ namespace App\Providers;
use App\Observers\{
AvatarObserver,
NotificationObserver,
+ StatusHashtagObserver,
UserObserver
};
use App\{
Avatar,
Notification,
+ StatusHashtag,
User
};
use Auth, Horizon, URL;
@@ -31,6 +33,7 @@ class AppServiceProvider extends ServiceProvider
Avatar::observe(AvatarObserver::class);
Notification::observe(NotificationObserver::class);
+ StatusHashtag::observe(StatusHashtagObserver::class);
User::observe(UserObserver::class);
Horizon::auth(function ($request) {
diff --git a/app/Services/StatusHashtagService.php b/app/Services/StatusHashtagService.php
new file mode 100644
index 000000000..7c5ed87bb
--- /dev/null
+++ b/app/Services/StatusHashtagService.php
@@ -0,0 +1,80 @@
+whereStatusVisibility('public')
+ ->whereHas('media')
+ ->skip($stop)
+ ->latest()
+ ->take(9)
+ ->pluck('status_id')
+ ->map(function ($i, $k) use ($id) {
+ return self::getStatus($i, $id);
+ })
+ ->all();
+ }
+
+ public static function coldGet($id, $start = 0, $stop = 2000)
+ {
+ $stop = $stop > 2000 ? 2000 : $stop;
+ $ids = StatusHashtag::whereHashtagId($id)
+ ->whereStatusVisibility('public')
+ ->whereHas('media')
+ ->latest()
+ ->skip($start)
+ ->take($stop)
+ ->pluck('status_id');
+ foreach($ids as $key) {
+ self::set($id, $key);
+ }
+ return $ids;
+ }
+
+ public static function set($key, $val)
+ {
+ return Redis::zadd(self::CACHE_KEY . $key, $val, $val);
+ }
+
+ public static function del($key)
+ {
+ return Redis::zrem(self::CACHE_KEY . $key, $key);
+ }
+
+ public static function count($id)
+ {
+ $count = Redis::zcount(self::CACHE_KEY . $id, '-inf', '+inf');
+ if(empty($count)) {
+ $count = StatusHashtag::whereHashtagId($id)->count();
+ }
+ return $count;
+ }
+
+ public static function getStatus($statusId, $hashtagId)
+ {
+ return Cache::remember('pf:services:status-hashtag:post:'.$statusId.':hashtag:'.$hashtagId, now()->addMonths(3), function() use($statusId, $hashtagId) {
+ $statusHashtag = StatusHashtag::with('profile', 'status', 'hashtag')
+ ->whereStatusVisibility('public')
+ ->whereStatusId($statusId)
+ ->whereHashtagId($hashtagId)
+ ->first();
+ $fractal = new Fractal\Manager();
+ $fractal->setSerializer(new ArraySerializer());
+ $resource = new Fractal\Resource\Item($statusHashtag, new StatusHashtagTransformer());
+ return $fractal->createData($resource)->toArray();
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/StatusHashtag.php b/app/StatusHashtag.php
index 15e282025..cee8e39b9 100644
--- a/app/StatusHashtag.php
+++ b/app/StatusHashtag.php
@@ -9,7 +9,8 @@ class StatusHashtag extends Model
public $fillable = [
'status_id',
'hashtag_id',
- 'profile_id'
+ 'profile_id',
+ 'status_visibility'
];
public function status()
@@ -26,4 +27,16 @@ class StatusHashtag extends Model
{
return $this->belongsTo(Profile::class);
}
+
+ public function media()
+ {
+ return $this->hasManyThrough(
+ Media::class,
+ Status::class,
+ 'id',
+ 'status_id',
+ 'status_id',
+ 'id'
+ );
+ }
}
diff --git a/app/Transformer/Api/StatusHashtagTransformer.php b/app/Transformer/Api/StatusHashtagTransformer.php
new file mode 100644
index 000000000..1dfdba3b9
--- /dev/null
+++ b/app/Transformer/Api/StatusHashtagTransformer.php
@@ -0,0 +1,38 @@
+hashtag;
+ $status = $statusHashtag->status;
+ $profile = $statusHashtag->profile;
+
+ return [
+ 'status' => [
+ 'id' => (int) $status->id,
+ 'type' => $status->type,
+ 'url' => $status->url(),
+ 'thumb' => $status->thumb(),
+ 'filter' => $status->firstMedia()->filter_class,
+ 'sensitive' => (bool) $status->is_nsfw,
+ 'like_count' => $status->likes_count,
+ 'share_count' => $status->reblogs_count,
+ 'user' => [
+ 'username' => $profile->username,
+ 'url' => $profile->url(),
+ ],
+ 'visibility' => $status->visibility ?? $status->scope
+ ],
+ 'hashtag' => [
+ 'name' => $hashtag->name,
+ 'url' => $hashtag->url(),
+ ]
+ ];
+ }
+}
diff --git a/app/Util/Lexer/Extractor.php b/app/Util/Lexer/Extractor.php
index 9e194b068..bcdbba919 100755
--- a/app/Util/Lexer/Extractor.php
+++ b/app/Util/Lexer/Extractor.php
@@ -264,7 +264,9 @@ class Extractor extends Regex
if (preg_match(self::$patterns['end_hashtag_match'], $outer[0])) {
continue;
}
-
+ if(mb_strlen($hashtag[0]) > 124) {
+ continue;
+ }
$tags[] = [
'hashtag' => $hashtag[0],
'indices' => [$start_position, $end_position],
diff --git a/app/Util/RateLimit/User.php b/app/Util/RateLimit/User.php
index c93aa6c4f..f54c94d33 100644
--- a/app/Util/RateLimit/User.php
+++ b/app/Util/RateLimit/User.php
@@ -49,8 +49,23 @@ trait User {
return 500;
}
+ public function getMaxUserBansPerDayAttribute()
+ {
+ return 100;
+ }
+
public function getMaxInstanceBansPerDayAttribute()
{
return 100;
}
+
+ public function getMaxHashtagFollowsPerHourAttribute()
+ {
+ return 20;
+ }
+
+ public function getMaxHashtagFollowsPerDayAttribute()
+ {
+ return 100;
+ }
}
\ No newline at end of file
diff --git a/config/instance.php b/config/instance.php
index 6fc04c902..15d1a400e 100644
--- a/config/instance.php
+++ b/config/instance.php
@@ -1,15 +1,33 @@
env('INSTANCE_CONTACT_EMAIL'),
+
+ 'announcement' => [
+ 'enabled' => env('INSTANCE_ANNOUNCEMENT_ENABLED', true),
+ 'message' => env('INSTANCE_ANNOUNCEMENT_MESSAGE', 'Example announcement message.
Something else here')
+ ],
'contact' => [
'enabled' => env('INSTANCE_CONTACT_FORM', false),
'max_per_day' => env('INSTANCE_CONTACT_MAX_PER_DAY', 1),
],
- 'announcement' => [
- 'enabled' => env('INSTANCE_ANNOUNCEMENT_ENABLED', true),
- 'message' => env('INSTANCE_ANNOUNCEMENT_MESSAGE', 'Example announcement message.
Something else here')
- ]
+ 'discover' => [
+ 'loops' => [
+ 'enabled' => false
+ ],
+ 'tags' => [
+ 'is_public' => env('INSTANCE_PUBLIC_HASHTAGS', false)
+ ],
+ ],
+
+ 'email' => env('INSTANCE_CONTACT_EMAIL'),
+
+ 'timeline' => [
+ 'local' => [
+ 'is_public' => env('INSTANCE_PUBLIC_LOCAL_TIMELINE', false)
+ ]
+ ],
+
+
];
\ No newline at end of file
diff --git a/database/migrations/2019_07_05_034644_create_hashtag_follows_table.php b/database/migrations/2019_07_05_034644_create_hashtag_follows_table.php
new file mode 100644
index 000000000..20f20f524
--- /dev/null
+++ b/database/migrations/2019_07_05_034644_create_hashtag_follows_table.php
@@ -0,0 +1,35 @@
+bigIncrements('id');
+ $table->bigInteger('user_id')->unsigned()->index();
+ $table->bigInteger('profile_id')->unsigned()->index();
+ $table->bigInteger('hashtag_id')->unsigned()->index();
+ $table->unique(['user_id', 'profile_id', 'hashtag_id']);
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('hashtag_follows');
+ }
+}
diff --git a/database/migrations/2019_07_08_045824_add_status_visibility_to_status_hashtags_table.php b/database/migrations/2019_07_08_045824_add_status_visibility_to_status_hashtags_table.php
new file mode 100644
index 000000000..fab19c210
--- /dev/null
+++ b/database/migrations/2019_07_08_045824_add_status_visibility_to_status_hashtags_table.php
@@ -0,0 +1,32 @@
+string('status_visibility')->nullable()->index()->after('profile_id');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('status_hashtags', function (Blueprint $table) {
+ $table->dropColumn('status_visibility');
+ });
+ }
+}
diff --git a/public/js/direct.js b/public/js/direct.js
deleted file mode 100644
index 3c9c23974..000000000
Binary files a/public/js/direct.js and /dev/null differ
diff --git a/public/js/hashtag.js b/public/js/hashtag.js
new file mode 100644
index 000000000..197c93842
Binary files /dev/null and b/public/js/hashtag.js differ
diff --git a/public/js/loops.js b/public/js/loops.js
index 277aaf8d7..60a1c1ddb 100644
Binary files a/public/js/loops.js and b/public/js/loops.js differ
diff --git a/public/js/mode-dot.js b/public/js/mode-dot.js
index 95c1dd94b..9517e65d9 100644
Binary files a/public/js/mode-dot.js and b/public/js/mode-dot.js differ
diff --git a/public/js/profile.js b/public/js/profile.js
index 0ddcd0afe..84b7d23c5 100644
Binary files a/public/js/profile.js and b/public/js/profile.js differ
diff --git a/public/js/quill.js b/public/js/quill.js
index 4a8e134f1..3c07d0bfa 100644
Binary files a/public/js/quill.js and b/public/js/quill.js differ
diff --git a/public/js/search.js b/public/js/search.js
index e4c95d9cb..c7ff1bd3b 100644
Binary files a/public/js/search.js and b/public/js/search.js differ
diff --git a/public/js/status.js b/public/js/status.js
index ba4af018e..6c35e9bf3 100644
Binary files a/public/js/status.js and b/public/js/status.js differ
diff --git a/public/js/theme-monokai.js b/public/js/theme-monokai.js
index 3a510271a..ff34783e2 100644
Binary files a/public/js/theme-monokai.js and b/public/js/theme-monokai.js differ
diff --git a/public/js/timeline.js b/public/js/timeline.js
index f4c3175df..62a3690bc 100644
Binary files a/public/js/timeline.js and b/public/js/timeline.js differ
diff --git a/public/js/vendor.js b/public/js/vendor.js
index 0cbfa2406..6a508774a 100644
Binary files a/public/js/vendor.js and b/public/js/vendor.js differ
diff --git a/public/mix-manifest.json b/public/mix-manifest.json
index cbe51aa43..c0c17236c 100644
Binary files a/public/mix-manifest.json and b/public/mix-manifest.json differ
diff --git a/resources/assets/js/components/Hashtag.vue b/resources/assets/js/components/Hashtag.vue
new file mode 100644
index 000000000..9d9c5fcd8
--- /dev/null
+++ b/resources/assets/js/components/Hashtag.vue
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/assets/js/components/PostComponent.vue b/resources/assets/js/components/PostComponent.vue
index 8ea58b59c..27973c4d5 100644
--- a/resources/assets/js/components/PostComponent.vue
+++ b/resources/assets/js/components/PostComponent.vue
@@ -107,8 +107,8 @@
This comment may contain sensitive material
-Show
-diff --git a/resources/assets/js/components/PostMenu.vue b/resources/assets/js/components/PostMenu.vue index dc5ab0933..b219978ad 100644 --- a/resources/assets/js/components/PostMenu.vue +++ b/resources/assets/js/components/PostMenu.vue @@ -6,8 +6,8 @@
Statuses
- --
+#{{hashtagPostsName}}
+ ++ + {{tag.status.like_count}} + + + {{tag.status.share_count}} + +
+- - {{$status->likes_count}} - - - {{$status->comments_count}} - -
-Sorry, this page isn't available.
+The link you followed may be broken, or the page may have been removed. Go back to Pixelfed.
+Sorry, this page isn't available.
+The link you followed may be broken, or the page may have been removed. Go back to Pixelfed.
+Page Not Found
- -Sorry, this page isn't available.
+The link you followed may be broken, or the page may have been removed. Go back to Pixelfed.
Whoops! Something went wrong.
-If you keep seeing this message, please contact an admin.
- -Something went wrong
+We cannot process your request at this time, please try again later. Go back to Pixelfed.
Service Unavailable
-Our services are in maintenance mode, please try again later.
- -Service Unavailable
+Our service is in maintenance mode, please try again later. Go back to Pixelfed.
+ + + How do I use a hashtag on Pixelfed? + +
+- You can add hashtags to post captions, if the post is public the hashtag will be discoverable.
+ - You can follow hashtags on Pixelfed to stay connected with interests you care about.
+
++ + + How do I follow a hashtag? + +
You can follow hashtags on Pixelfed to stay connected with interests you care about.
+To follow a hashtag:
++- Tap any hashtag (example: #art) you see on Pixelfed.
+ - Tap Follow. Once you follow a hashtag, you'll see its photos and videos appear in feed.
+
+To unfollow a hashtag, tap the hashtag and then tap Unfollow to confirm.
++ You can follow up to 20 hashtags per hour or 100 per day. +
+-
- @endif -CW / NSFW / Hidden Media
-(click to show)
-- @for($i = 0; $i < $status->media_count; $i++) - - @endfor -
--
- - - -CW / NSFW / Hidden Media
-(click to show)
-- {{$status->profile->username}} - {!! $status->rendered ?? e($status->caption) !!} -
-View all comments
-- {{ str_limit($item->profile->username, 15)}} - {!! $item->rendered ?? e($item->caption) !!} {{$item->created_at->diffForHumans(null, true, true ,true)}} -
- @endforeach --
-CW / NSFW / Hidden Media
-(click to show)
-+
{{$item->profile->username}} diff --git a/resources/views/status/timeline/album.blade.php b/resources/views/status/timeline/album.blade.php deleted file mode 100644 index f606d41c1..000000000 --- a/resources/views/status/timeline/album.blade.php +++ /dev/null @@ -1,29 +0,0 @@ -@if($status->is_nsfw) - -@else -
- @for($i = 0; $i < $status->media_count; $i++) - - @endfor -
--
- - - -CW / NSFW / Hidden Media
-(click to show)
-- @for($i = 0; $i < $status->media_count; $i++) - - @endfor -
-- @for($i = 0; $i < $status->media_count; $i++) - - @endfor -
--
-CW / NSFW / Hidden Media
-(click to show)
-- @foreach ($errors->all() as $error) -- {{ $error }}
- @endforeach
-
-{{ __('timeline.emptyPersonalTimeline') }}
-No more content
-- Maybe you could try - discovering - more people you can follow. -
-Whoops, an error
-- Try reloading the page -
-- @foreach ($errors->all() as $error) -- {{ $error }}
- @endforeach
-
-No more content
-- Maybe you could try - discovering - more people you can follow. -
-Whoops, an error
-- Try reloading the page -
-{{ __('timeline.emptyPersonalTimeline') }}
-No more content
-- Maybe you could try - discovering - more people you can follow. -
-Whoops, an error
-- Try reloading the page -
-@{{Auth::user()->username}}
-{{Auth::user()->name}}
---
-
- My Timeline
-
-
- -
-
- Local Timeline
-
-
- -
-
- Network Timeline
-
-
-
-