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(' / ____/ /> 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 --- a/public/js/direct.js +++ /dev/null @@ -1,1557 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = "/"; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 10); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/assets/js/components/Direct.vue?vue&type=script&lang=js&": -/*!************************************************************************************************************************************************************************!*\ - !*** ./node_modules/babel-loader/lib??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./resources/assets/js/components/Direct.vue?vue&type=script&lang=js& ***! - \************************************************************************************************************************************************************************/ -/*! exports provided: default */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -/* harmony default export */ __webpack_exports__["default"] = ({ - data: function data() { - return { - threads: [], - thread: false - }; - }, - mounted: function mounted() {}, - methods: {} -}); - -/***/ }), - -/***/ "./node_modules/css-loader/index.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/assets/js/components/Direct.vue?vue&type=style&index=0&id=96e9d740&scoped=true&lang=css&": -/*!*******************************************************************************************************************************************************************************************************************************************************************************************************!*\ - !*** ./node_modules/css-loader??ref--8-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-2!./node_modules/vue-loader/lib??vue-loader-options!./resources/assets/js/components/Direct.vue?vue&type=style&index=0&id=96e9d740&scoped=true&lang=css& ***! - \*******************************************************************************************************************************************************************************************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); -// imports - - -// module -exports.push([module.i, "\n.chat.card[data-v-96e9d740] {\n\theight: 500px;\n\tbackground-color: rgba(0,0,0,0.4) !important;\n}\n.im-card[data-v-96e9d740] {\n\toverflow-y: auto;\n}\n.chat.card.card-header[data-v-96e9d740] {\n\tborder-bottom: 0 !important;\n}\n.chat.card.card-footer[data-v-96e9d740] {\n\tborder-top: 0 !important;\n}\n.im-input[data-v-96e9d740] {\n\tbackground-color: rgba(0,0,0,0.3) !important;\n\tborder: 0 !important;\n\tcolor: #fff !important;\n\theight: 60px !important;\n\toverflow-y: auto;\n}\n.im-input[data-v-96e9d740]:focus {\n\tbox-shadow: none !important;\n\toutline: 0px !important;\n}\n.im-picker[data-v-96e9d740] {\n\tborder-radius: 15px 0 0 15px !important;\n\tbackground-color: rgba(0,0,0,0.3) !important;\n\tborder: 0 !important;\n\tcolor: white !important;\n\tcursor: pointer;\n}\n.im-send[data-v-96e9d740] {\n\tborder-radius: 0 15px 15px 0 !important;\n\tbackground-color: rgba(0,0,0,0.3) !important;\n\tborder: 0 !important;\n\tcolor: white !important;\n\tcursor: pointer;\n}\n.im-msg-avatar[data-v-96e9d740],\n.im-avatar[data-v-96e9d740] {\n\theight: 40px;\n\twidth: 40px;\n}\n.im-bubble-to[data-v-96e9d740] {\n\tmargin-top: auto;\n\tmargin-bottom: auto;\n\tmargin-left: 10px;\n\tborder-radius: 5px;\n\tbackground-color: #E5E4E9;\n\tcolor: #363636;\n\tpadding: 10px;\n\tposition: relative;\n}\n.im-bubble-from[data-v-96e9d740] {\n\tmargin-top: auto;\n\tmargin-bottom: auto;\n\tmargin-right: 10px;\n\tborder-radius: 5px;\n\tbackground-color: #2095FE;\n\tcolor: #fff;\n\tpadding: 10px;\n\tposition: relative;\n}\n.im-ts[data-v-96e9d740] {\n\tposition: absolute;\n\tleft: 0;\n\tbottom: -15px;\n\tcolor: #cdcdcd;\n\tfont-size: 10px;\n}\n.im-ts-to[data-v-96e9d740] {\n\tposition: absolute;\n\tright: 0;\n\tbottom: -15px;\n\tcolor: #cdcdcd;\n\tfont-size: 10px;\n}\n", ""]); - -// exports - - -/***/ }), - -/***/ "./node_modules/css-loader/lib/css-base.js": -/*!*************************************************!*\ - !*** ./node_modules/css-loader/lib/css-base.js ***! - \*************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -// css base code, injected by the css-loader -module.exports = function(useSourceMap) { - var list = []; - - // return the list of modules as css string - list.toString = function toString() { - return this.map(function (item) { - var content = cssWithMappingToString(item, useSourceMap); - if(item[2]) { - return "@media " + item[2] + "{" + content + "}"; - } else { - return content; - } - }).join(""); - }; - - // import a list of modules into the list - list.i = function(modules, mediaQuery) { - if(typeof modules === "string") - modules = [[null, modules, ""]]; - var alreadyImportedModules = {}; - for(var i = 0; i < this.length; i++) { - var id = this[i][0]; - if(typeof id === "number") - alreadyImportedModules[id] = true; - } - for(i = 0; i < modules.length; i++) { - var item = modules[i]; - // skip already imported module - // this implementation is not 100% perfect for weird media query combinations - // when a module is imported multiple times with different media queries. - // I hope this will never occur (Hey this way we have smaller bundles) - if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { - if(mediaQuery && !item[2]) { - item[2] = mediaQuery; - } else if(mediaQuery) { - item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; - } - list.push(item); - } - } - }; - return list; -}; - -function cssWithMappingToString(item, useSourceMap) { - var content = item[1] || ''; - var cssMapping = item[3]; - if (!cssMapping) { - return content; - } - - if (useSourceMap && typeof btoa === 'function') { - var sourceMapping = toComment(cssMapping); - var sourceURLs = cssMapping.sources.map(function (source) { - return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' - }); - - return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); - } - - return [content].join('\n'); -} - -// Adapted from convert-source-map (MIT) -function toComment(sourceMap) { - // eslint-disable-next-line no-undef - var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); - var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; - - return '/*# ' + data + ' */'; -} - - -/***/ }), - -/***/ "./node_modules/style-loader/index.js!./node_modules/css-loader/index.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/assets/js/components/Direct.vue?vue&type=style&index=0&id=96e9d740&scoped=true&lang=css&": -/*!***********************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ - !*** ./node_modules/style-loader!./node_modules/css-loader??ref--8-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--8-2!./node_modules/vue-loader/lib??vue-loader-options!./resources/assets/js/components/Direct.vue?vue&type=style&index=0&id=96e9d740&scoped=true&lang=css& ***! - \***********************************************************************************************************************************************************************************************************************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - - -var content = __webpack_require__(/*! !../../../../node_modules/css-loader??ref--8-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src??ref--8-2!../../../../node_modules/vue-loader/lib??vue-loader-options!./Direct.vue?vue&type=style&index=0&id=96e9d740&scoped=true&lang=css& */ "./node_modules/css-loader/index.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/assets/js/components/Direct.vue?vue&type=style&index=0&id=96e9d740&scoped=true&lang=css&"); - -if(typeof content === 'string') content = [[module.i, content, '']]; - -var transform; -var insertInto; - - - -var options = {"hmr":true} - -options.transform = transform -options.insertInto = undefined; - -var update = __webpack_require__(/*! ../../../../node_modules/style-loader/lib/addStyles.js */ "./node_modules/style-loader/lib/addStyles.js")(content, options); - -if(content.locals) module.exports = content.locals; - -if(false) {} - -/***/ }), - -/***/ "./node_modules/style-loader/lib/addStyles.js": -/*!****************************************************!*\ - !*** ./node_modules/style-loader/lib/addStyles.js ***! - \****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -var stylesInDom = {}; - -var memoize = function (fn) { - var memo; - - return function () { - if (typeof memo === "undefined") memo = fn.apply(this, arguments); - return memo; - }; -}; - -var isOldIE = memoize(function () { - // Test for IE <= 9 as proposed by Browserhacks - // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805 - // Tests for existence of standard globals is to allow style-loader - // to operate correctly into non-standard environments - // @see https://github.com/webpack-contrib/style-loader/issues/177 - return window && document && document.all && !window.atob; -}); - -var getTarget = function (target, parent) { - if (parent){ - return parent.querySelector(target); - } - return document.querySelector(target); -}; - -var getElement = (function (fn) { - var memo = {}; - - return function(target, parent) { - // If passing function in options, then use it for resolve "head" element. - // Useful for Shadow Root style i.e - // { - // insertInto: function () { return document.querySelector("#foo").shadowRoot } - // } - if (typeof target === 'function') { - return target(); - } - if (typeof memo[target] === "undefined") { - var styleTarget = getTarget.call(this, target, parent); - // Special case to return head of iframe instead of iframe itself - if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) { - try { - // This will throw an exception if access to iframe is blocked - // due to cross-origin restrictions - styleTarget = styleTarget.contentDocument.head; - } catch(e) { - styleTarget = null; - } - } - memo[target] = styleTarget; - } - return memo[target] - }; -})(); - -var singleton = null; -var singletonCounter = 0; -var stylesInsertedAtTop = []; - -var fixUrls = __webpack_require__(/*! ./urls */ "./node_modules/style-loader/lib/urls.js"); - -module.exports = function(list, options) { - if (typeof DEBUG !== "undefined" && DEBUG) { - if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); - } - - options = options || {}; - - options.attrs = typeof options.attrs === "object" ? options.attrs : {}; - - // Force single-tag solution on IE6-9, which has a hard limit on the # of + + \ 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 @@
-

- {{statusUsername}} +

+ {{statusUsername}}

@@ -124,10 +124,13 @@
-
-

This comment may contain sensitive material

-

Show

-
+ + {{truncate(reply.account.username,15)}} + + 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 @@