1
0
Fork 0

Compare commits

...

41 Commits

Author SHA1 Message Date
daniel ae60c99679
Merge pull request #5052 from pixelfed/staging
Update profile embed, fix height bug
2024-04-20 05:59:25 -06:00
Daniel Supernault a54b4fb038
Update profile embed, fix height bug 2024-04-20 05:59:01 -06:00
daniel 2f7481205c
Merge pull request #5051 from pixelfed/staging
Staging
2024-04-20 05:03:29 -06:00
Daniel Supernault cde17f5af7
Update changelog 2024-04-20 05:03:10 -06:00
Daniel Supernault 433bc4c286
Update embed.js 2024-04-20 05:02:58 -06:00
daniel 5d407ededf
Merge pull request #5050 from pixelfed/staging
Update profile embed view, fix height bug
2024-04-20 05:01:50 -06:00
Daniel Supernault 65166570c5
Update profile embed view, fix height bug 2024-04-20 05:01:04 -06:00
daniel 610326e7b0
Merge pull request #5049 from pixelfed/staging
Refactor embeds
2024-04-20 04:36:08 -06:00
Daniel Supernault 6fc066a213
Update changelog 2024-04-20 04:35:10 -06:00
Daniel Supernault 8b8b1ffc5c
Update ProfileController, refactor profile embeds 2024-04-20 04:33:47 -06:00
Daniel Supernault 9a7acc12a6
Update StatusController, refactor status embeds 2024-04-20 04:26:47 -06:00
Daniel Supernault 51b6fe7dc8
Refactor embeds 2024-04-20 04:25:22 -06:00
daniel c4ffa62242
Merge pull request #5048 from pixelfed/staging
Update webpack config
2024-04-20 02:01:19 -06:00
Daniel Supernault 87ee0633fe
Update assets, move presenters 2024-04-20 01:26:51 -06:00
Daniel Supernault ded660b2c4
Update webpack config 2024-04-20 00:55:21 -06:00
daniel 4d04227d41
Merge pull request #5043 from pixelfed/staging
Staging
2024-04-12 04:44:12 -06:00
Daniel Supernault 26f92c93ce
Update compiled assets 2024-04-12 04:43:38 -06:00
Daniel Supernault 81566987e4
Update changelog 2024-04-12 04:43:21 -06:00
Daniel Supernault 8af2360779
Update VideoPlayer component, add playsinline attribute to video element 2024-04-12 04:42:57 -06:00
daniel b3fb69c5b1
Merge pull request #5042 from pixelfed/staging
Staging
2024-04-12 04:03:20 -06:00
Daniel Supernault f30f7d79fb
Update changelog 2024-04-12 04:00:46 -06:00
Daniel Supernault 2deb65d874
Update compiled assets 2024-04-12 04:00:25 -06:00
Daniel Supernault ad03291699
Update VideoPresenter component, add webkit-playsinline attribute to video element to prevent the full screen video player 2024-04-12 03:56:33 -06:00
daniel 6be21891d5
Merge pull request #5041 from pixelfed/staging
Staging
2024-04-11 20:44:15 -06:00
daniel 141f6d38a7
Merge pull request #5035 from jippi/jippi-fork
Docker fixes
2024-04-11 20:43:52 -06:00
Daniel Supernault 4608c66c0b
Re-add .env.example 2024-04-11 20:43:24 -06:00
Christian Winther e227ee1bd5 use DOCKER_DB_HOST_PORT when checking if database is ready or not 2024-04-06 10:53:48 +00:00
Christian Winther dbc5df849f use ENABLE_CONFIG_CACHE when dumping composer autoload 2024-04-06 10:06:07 +00:00
Daniel Supernault 6bdf73de4d
Update UnfollowPipeline, fix follower count cache bug 2024-04-06 03:29:17 -06:00
daniel 5b3d0206ae
Merge pull request #5034 from pixelfed/staging
Update docker env, fix config_cache. Fixes #5033
2024-04-06 03:20:31 -06:00
Daniel Supernault 858fcbf606
Update docker env, fix config_cache. Fixes #5033 2024-04-06 03:19:50 -06:00
daniel 57df06a0b6
Merge pull request #5032 from pixelfed/staging
Staging
2024-04-06 02:56:48 -06:00
Daniel Supernault db1a4c9f8e
Update changelog 2024-04-06 02:48:31 -06:00
Daniel Supernault ce4beab9c8
Update composer deps 2024-04-06 02:48:03 -06:00
Daniel Supernault 9d5479de39
Update compiled assets 2024-04-06 02:47:39 -06:00
Daniel Supernault 512518d319
Update npm deps 2024-04-06 02:47:19 -06:00
Daniel Supernault b06a3455c2
Update compiled assets 2024-04-06 02:28:27 -06:00
Daniel Supernault 94a6e8614a
Update styles 2024-04-06 02:27:55 -06:00
Daniel Supernault 81d1e0fdab
Update context menu, add mute/block/unfollow actions and update relationship store accordingly 2024-04-06 02:27:22 -06:00
Daniel Supernault b8e96a5ff3
Update ApiV1Controller, improve refresh relations logic when (un)muting or (un)blocking 2024-04-06 01:24:09 -06:00
Daniel Supernault b7322b6874
Update PrivacySettings controller, refresh RelationshipService when unmute/unblocking 2024-04-06 01:22:52 -06:00
86 changed files with 3042 additions and 2296 deletions

View File

@ -60,6 +60,15 @@ ADMIN_DOMAIN="${APP_DOMAIN}"
# @dottie/validate required,boolean
#APP_DEBUG="false"
# Disable config cache
#
# If disabled, settings must be managed by .env variables.
#
# @default "false"
# @see https://docs.pixelfed.org/technical-documentation/config/#config_cache
# @dottie/validate required,boolean
ENABLE_CONFIG_CACHE="false"
# Enable/disable new local account registrations.
#
# @default "true"

78
.env.example Normal file
View File

@ -0,0 +1,78 @@
APP_NAME="Pixelfed"
APP_ENV="production"
APP_KEY=
APP_DEBUG="false"
# Instance Configuration
OPEN_REGISTRATION="false"
ENFORCE_EMAIL_VERIFICATION="false"
PF_MAX_USERS="1000"
OAUTH_ENABLED="true"
# Media Configuration
PF_OPTIMIZE_IMAGES="true"
IMAGE_QUALITY="80"
MAX_PHOTO_SIZE="15000"
MAX_CAPTION_LENGTH="500"
MAX_ALBUM_LENGTH="4"
# Instance URL Configuration
APP_URL="http://localhost"
APP_DOMAIN="localhost"
ADMIN_DOMAIN="localhost"
SESSION_DOMAIN="localhost"
TRUST_PROXIES="*"
# Database Configuration
DB_CONNECTION="mysql"
DB_HOST="127.0.0.1"
DB_PORT="3306"
DB_DATABASE="pixelfed"
DB_USERNAME="pixelfed"
DB_PASSWORD="pixelfed"
# Redis Configuration
REDIS_CLIENT="predis"
REDIS_SCHEME="tcp"
REDIS_HOST="127.0.0.1"
REDIS_PASSWORD="null"
REDIS_PORT="6379"
# Laravel Configuration
SESSION_DRIVER="database"
CACHE_DRIVER="redis"
QUEUE_DRIVER="redis"
BROADCAST_DRIVER="log"
LOG_CHANNEL="stack"
HORIZON_PREFIX="horizon-"
# ActivityPub Configuration
ACTIVITY_PUB="false"
AP_REMOTE_FOLLOW="false"
AP_INBOX="false"
AP_OUTBOX="false"
AP_SHAREDINBOX="false"
# Experimental Configuration
EXP_EMC="true"
## Mail Configuration (Post-Installer)
MAIL_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="pixelfed@example.com"
MAIL_FROM_NAME="Pixelfed"
## S3 Configuration (Post-Installer)
PF_ENABLE_CLOUD=false
FILESYSTEM_CLOUD=s3
#AWS_ACCESS_KEY_ID=
#AWS_SECRET_ACCESS_KEY=
#AWS_DEFAULT_REGION=
#AWS_BUCKET=<BucketName>
#AWS_URL=
#AWS_ENDPOINT=
#AWS_USE_PATH_STYLE_ENDPOINT=false

View File

@ -19,6 +19,56 @@
- Update SiteController, add curatedOnboarding method that gracefully falls back to open registration when applicable ([95199843](https://github.com/pixelfed/pixelfed/commit/95199843))
- Update AP transformers, add DeleteActor activity ([bcce1df6](https://github.com/pixelfed/pixelfed/commit/bcce1df6))
- Update commands, add user account delete cli command to federate account deletion ([4aa0e25f](https://github.com/pixelfed/pixelfed/commit/4aa0e25f))
- Update web-api popular accounts route to its own method to remove the breaking oauth scope bug ([a4bc5ce3](https://github.com/pixelfed/pixelfed/commit/a4bc5ce3))
- Update config cache ([5e4d4eff](https://github.com/pixelfed/pixelfed/commit/5e4d4eff))
- Update Config, use config_cache ([7785a2da](https://github.com/pixelfed/pixelfed/commit/7785a2da))
- Update ApiV1Dot1Controller, use config_cache for in-app registration ([b0cb4456](https://github.com/pixelfed/pixelfed/commit/b0cb4456))
- Update captcha, use config_cache helper ([8a89e3c9](https://github.com/pixelfed/pixelfed/commit/8a89e3c9))
- Update custom emoji, add config_cache support ([481314cd](https://github.com/pixelfed/pixelfed/commit/481314cd))
- Update ProfileController, fix permalink redirect bug ([75081e60](https://github.com/pixelfed/pixelfed/commit/75081e60))
- Update admin css, use font-display:swap for nucleo icons ([8a0c456e](https://github.com/pixelfed/pixelfed/commit/8a0c456e))
- Update PixelfedDirectoryController, fix boolean cast bug ([f08aab22](https://github.com/pixelfed/pixelfed/commit/f08aab22))
- Update PixelfedDirectoryController, use cached stats ([f2f2a809](https://github.com/pixelfed/pixelfed/commit/f2f2a809))
- Update AdminDirectoryController, fix type casting ([ad506e90](https://github.com/pixelfed/pixelfed/commit/ad506e90))
- Update image pipeline, use config_cache ([a72188a7](https://github.com/pixelfed/pixelfed/commit/a72188a7))
- Update cloud storage, use config_cache ([665581d8](https://github.com/pixelfed/pixelfed/commit/665581d8))
- Update pixelfed.max_album_length, use config_cache ([fecbe189](https://github.com/pixelfed/pixelfed/commit/fecbe189))
- Update media_types, use config_cache ([d670de17](https://github.com/pixelfed/pixelfed/commit/d670de17))
- Update landing settings, use config_cache ([40478f25](https://github.com/pixelfed/pixelfed/commit/40478f25))
- Update activitypub setting, use config_cache ([5071aaf4](https://github.com/pixelfed/pixelfed/commit/5071aaf4))
- Update oauth setting, use config_cache ([ce228f7f](https://github.com/pixelfed/pixelfed/commit/ce228f7f))
- Update stories config, use config_cache ([d1adb109](https://github.com/pixelfed/pixelfed/commit/d1adb109))
- Update ig import, use config_cache ([da0e0ffa](https://github.com/pixelfed/pixelfed/commit/da0e0ffa))
- Update autospam config, use config_cache ([a76cb5f4](https://github.com/pixelfed/pixelfed/commit/a76cb5f4))
- Update app.name config, use config_cache ([911446c0](https://github.com/pixelfed/pixelfed/commit/911446c0))
- Update UserObserver, fix type casting ([949e9979](https://github.com/pixelfed/pixelfed/commit/949e9979))
- Update user_filters, use config_cache ([6ce513f8](https://github.com/pixelfed/pixelfed/commit/6ce513f8))
- Update filesystems config, add to config_cache ([087b2791](https://github.com/pixelfed/pixelfed/commit/087b2791))
- Update web-admin routes, add setting api routes ([828a456f](https://github.com/pixelfed/pixelfed/commit/828a456f))
- Update hashtag component ([cee979ed](https://github.com/pixelfed/pixelfed/commit/cee979ed))
- Update AdminReadMore component, add .prevent to click action ([704e7b12](https://github.com/pixelfed/pixelfed/commit/704e7b12))
- Update admin dashboard, add admin settings partials ([eb487123](https://github.com/pixelfed/pixelfed/commit/eb487123))
- Update admin settings, refactor to vue component ([674e560f](https://github.com/pixelfed/pixelfed/commit/674e560f))
- Update ConfigCacheService, encrypt keys at rest ([3628b462](https://github.com/pixelfed/pixelfed/commit/3628b462))
- Update RemoteFollowImportRecent, use MediaPathService ([5162c070](https://github.com/pixelfed/pixelfed/commit/5162c070))
- Update AdminSettingsController, add user filter max limit settings ([ac1f0748](https://github.com/pixelfed/pixelfed/commit/ac1f0748))
- Update AdminSettingsController, add AdminSettingsService ([dcc5f416](https://github.com/pixelfed/pixelfed/commit/dcc5f416))
- Update AdminSettings component, fix user settings ([aba1e13d](https://github.com/pixelfed/pixelfed/commit/aba1e13d))
- Update AdminInstances component ([ec2fdd61](https://github.com/pixelfed/pixelfed/commit/ec2fdd61))
- Update AdminSettings, add max_account_size support ([2dcbc1d5](https://github.com/pixelfed/pixelfed/commit/2dcbc1d5))
- Update AdminSettings, use better validation for user integer settings ([d946afcc](https://github.com/pixelfed/pixelfed/commit/d946afcc))
- Update spa sass, fix timestamp dark mode bug ([4147f7c5](https://github.com/pixelfed/pixelfed/commit/4147f7c5))
- Update relationships view, fix unfollow hashtag bug. Fixes #5008 ([8c693640](https://github.com/pixelfed/pixelfed/commit/8c693640))
- Update PrivacySettings controller, refresh RelationshipService when unmute/unblocking ([b7322b68](https://github.com/pixelfed/pixelfed/commit/b7322b68))
- Update ApiV1Controller, improve refresh relations logic when (un)muting or (un)blocking ([b8e96a5f](https://github.com/pixelfed/pixelfed/commit/b8e96a5f))
- Update context menu, add mute/block/unfollow actions and update relationship store accordingly ([81d1e0fd](https://github.com/pixelfed/pixelfed/commit/81d1e0fd))
- Update docker env, fix config_cache. Fixes #5033 ([858fcbf6](https://github.com/pixelfed/pixelfed/commit/858fcbf6))
- Update UnfollowPipeline, fix follower count cache bug ([6bdf73de](https://github.com/pixelfed/pixelfed/commit/6bdf73de))
- Update VideoPresenter component, add webkit-playsinline attribute to video element to prevent the full screen video player ([ad032916](https://github.com/pixelfed/pixelfed/commit/ad032916))
- Update VideoPlayer component, add playsinline attribute to video element ([8af23607](https://github.com/pixelfed/pixelfed/commit/8af23607))
- Update StatusController, refactor status embeds ([9a7acc12](https://github.com/pixelfed/pixelfed/commit/9a7acc12))
- Update ProfileController, refactor profile embeds ([8b8b1ffc](https://github.com/pixelfed/pixelfed/commit/8b8b1ffc))
- Update profile embed view, fix height bug ([65166570](https://github.com/pixelfed/pixelfed/commit/65166570))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.13 (2024-03-05)](https://github.com/pixelfed/pixelfed/compare/v0.11.12...v0.11.13)

View File

@ -237,7 +237,7 @@ USER ${RUNTIME_UID}:${RUNTIME_GID}
# Generate optimized autoloader now that we have all files around
RUN set -ex \
&& composer dump-autoload --optimize
&& ENABLE_CONFIG_CACHE=false composer dump-autoload --optimize
USER root

View File

@ -1200,8 +1200,8 @@ class ApiV1Controller extends Controller
if ($filter) {
$filter->delete();
UserFilterService::unblock($pid, $profile->id);
RelationshipService::refresh($pid, $id);
}
RelationshipService::refresh($pid, $id);
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray();
@ -2207,9 +2207,10 @@ class ApiV1Controller extends Controller
if ($filter) {
$filter->delete();
UserFilterService::unmute($pid, $profile->id);
RelationshipService::refresh($pid, $id);
}
RelationshipService::refresh($pid, $id);
$resource = new Fractal\Resource\Item($profile, new RelationshipTransformer());
$res = $this->fractal->createData($resource)->toArray();

View File

@ -172,7 +172,7 @@ class ProfileController extends Controller
$user = $this->getCachedUser($username);
abort_if(!$user, 404);
abort_if(! $user, 404);
return redirect($user->url());
}
@ -254,7 +254,7 @@ class ProfileController extends Controller
abort_if(! $profile || $profile['locked'] || ! $profile['local'], 404);
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile['id'], 86400, function () use ($profile) {
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile['id'], 3600, function () use ($profile) {
$uid = User::whereProfileId($profile['id'])->first();
if (! $uid) {
return true;
@ -348,7 +348,7 @@ class ProfileController extends Controller
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile->id, 86400, function () use ($profile) {
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile->id, 3600, function () use ($profile) {
$exists = AccountInterstitial::whereUserId($profile->user_id)->where('is_spam', 1)->count();
if ($exists) {
return true;
@ -373,7 +373,7 @@ class ProfileController extends Controller
public function stories(Request $request, $username)
{
abort_if(!(bool) config_cache('instance.stories.enabled') || ! $request->user(), 404);
abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404);
$profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
$pid = $profile->id;
$authed = Auth::user()->profile_id;

View File

@ -2,23 +2,17 @@
namespace App\Http\Controllers\Settings;
use App\AccountLog;
use App\EmailVerification;
use App\Instance;
use App\Follower;
use App\Media;
use App\Profile;
use App\User;
use App\Services\RelationshipService;
use App\UserFilter;
use App\Util\Lexer\PrettyNumber;
use App\Util\ActivityPub\Helpers;
use Auth, Cache, DB;
use Auth;
use Cache;
use DB;
use Illuminate\Http\Request;
use App\Models\UserDomainBlock;
trait PrivacySettings
{
public function privacy()
{
$user = Auth::user();
@ -85,18 +79,19 @@ trait PrivacySettings
$settings->save();
}
$pid = $profile->id;
Cache::forget('profile:settings:' . $pid);
Cache::forget('user:account:id:' . $profile->user_id);
Cache::forget('profile:follower_count:' . $pid);
Cache::forget('profile:following_count:' . $pid);
Cache::forget('profile:atom:enabled:' . $pid);
Cache::forget('profile:embed:' . $pid);
Cache::forget('pf:acct:settings:hidden-followers:' . $pid);
Cache::forget('pf:acct:settings:hidden-following:' . $pid);
Cache::forget('pf:acct-trans:hideFollowing:' . $pid);
Cache::forget('pf:acct-trans:hideFollowers:' . $pid);
Cache::forget('pfc:cached-user:wt:' . strtolower($profile->username));
Cache::forget('pfc:cached-user:wot:' . strtolower($profile->username));
Cache::forget('profile:settings:'.$pid);
Cache::forget('user:account:id:'.$profile->user_id);
Cache::forget('profile:follower_count:'.$pid);
Cache::forget('profile:following_count:'.$pid);
Cache::forget('profile:atom:enabled:'.$pid);
Cache::forget('profile:embed:'.$pid);
Cache::forget('pf:acct:settings:hidden-followers:'.$pid);
Cache::forget('pf:acct:settings:hidden-following:'.$pid);
Cache::forget('pf:acct-trans:hideFollowing:'.$pid);
Cache::forget('pf:acct-trans:hideFollowers:'.$pid);
Cache::forget('pfc:cached-user:wt:'.strtolower($profile->username));
Cache::forget('pfc:cached-user:wot:'.strtolower($profile->username));
return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!');
}
@ -105,13 +100,14 @@ trait PrivacySettings
$pid = Auth::user()->profile->id;
$ids = (new UserFilter())->mutedUserIds($pid);
$users = Profile::whereIn('id', $ids)->simplePaginate(15);
return view('settings.privacy.muted', compact('users'));
}
public function mutedUsersUpdate(Request $request)
{
$this->validate($request, [
'profile_id' => 'required|integer|min:1'
'profile_id' => 'required|integer|min:1',
]);
$fid = $request->input('profile_id');
$pid = Auth::user()->profile->id;
@ -123,6 +119,8 @@ trait PrivacySettings
->firstOrFail();
$filter->delete();
});
RelationshipService::refresh($pid, $fid);
return redirect()->back();
}
@ -131,14 +129,14 @@ trait PrivacySettings
$pid = Auth::user()->profile->id;
$ids = (new UserFilter())->blockedUserIds($pid);
$users = Profile::whereIn('id', $ids)->simplePaginate(15);
return view('settings.privacy.blocked', compact('users'));
}
public function blockedUsersUpdate(Request $request)
{
$this->validate($request, [
'profile_id' => 'required|integer|min:1'
'profile_id' => 'required|integer|min:1',
]);
$fid = $request->input('profile_id');
$pid = Auth::user()->profile->id;
@ -150,6 +148,8 @@ trait PrivacySettings
->firstOrFail();
$filter->delete();
});
RelationshipService::refresh($pid, $fid);
return redirect()->back();
}
@ -194,7 +194,7 @@ trait PrivacySettings
$profile = Auth::user()->profile;
$settings = Auth::user()->settings;
if($mode !== 'keep-all') {
if ($mode !== 'keep-all') {
switch ($mode) {
case 'mutual-only':
$following = $profile->following()->pluck('profiles.id');
@ -211,7 +211,7 @@ trait PrivacySettings
break;
default:
# code...
// code...
break;
}
}
@ -221,6 +221,7 @@ trait PrivacySettings
$settings->save();
$profile->save();
Cache::forget('profiles:private');
return [200];
}
}

View File

@ -8,6 +8,7 @@ use App\Jobs\SharePipeline\UndoSharePipeline;
use App\Jobs\StatusPipeline\RemoteStatusDelete;
use App\Jobs\StatusPipeline\StatusDelete;
use App\Profile;
use App\Services\AccountService;
use App\Services\HashidService;
use App\Services\ReblogService;
use App\Services\StatusService;
@ -113,19 +114,33 @@ class StatusController extends Controller
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
$profile = Profile::whereNull(['domain', 'status'])
->whereIsPrivate(false)
->whereUsername($username)
->first();
$status = StatusService::get($id);
if (! $profile) {
if (
! $status ||
! isset($status['account'], $status['account']['id'], $status['local']) ||
! $status['local'] ||
strtolower($status['account']['username']) !== strtolower($username)
) {
$content = view('status.embed-removed');
return response($content, 404)->header('X-Frame-Options', 'ALLOWALL');
}
$profile = AccountService::get($status['account']['id'], true);
if (! $profile || $profile['locked'] || ! $profile['local']) {
$content = view('status.embed-removed');
return response($content)->header('X-Frame-Options', 'ALLOWALL');
}
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile->id, 86400, function () use ($profile) {
$exists = AccountInterstitial::whereUserId($profile->user_id)->where('is_spam', 1)->count();
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile['id'], 3600, function () use ($profile) {
$user = Profile::find($profile['id']);
if (! $user) {
return true;
}
$exists = AccountInterstitial::whereUserId($user->user_id)->where('is_spam', 1)->count();
if ($exists) {
return true;
}
@ -138,17 +153,22 @@ class StatusController extends Controller
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
$status = Status::whereProfileId($profile->id)
->whereNull('uri')
->whereScope('public')
->whereIsNsfw(false)
->whereIn('type', ['photo', 'video', 'photo:album'])
->find($id);
if (! $status) {
$status = StatusService::get($id);
if (
! $status ||
! isset($status['account'], $status['account']['id']) ||
intval($status['account']['id']) !== intval($profile['id']) ||
$status['sensitive'] ||
$status['visibility'] !== 'public' ||
$status['pf_type'] !== 'photo'
) {
$content = view('status.embed-removed');
return response($content)->header('X-Frame-Options', 'ALLOWALL');
}
$showLikes = $request->filled('likes') && $request->likes == true;
$showCaption = $request->filled('caption') && $request->caption !== false;
$layout = $request->filled('layout') && $request->layout == 'compact' ? 'compact' : 'full';

View File

@ -4,26 +4,25 @@ namespace App\Jobs\FollowPipeline;
use App\Follower;
use App\FollowRequest;
use App\Jobs\HomeFeedPipeline\FeedUnfollowPipeline;
use App\Notification;
use App\Profile;
use App\Services\AccountService;
use App\Services\FollowerService;
use App\Services\NotificationService;
use Cache;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Log;
use Illuminate\Support\Facades\Redis;
use App\Services\AccountService;
use App\Services\FollowerService;
use App\Services\NotificationService;
use App\Jobs\HomeFeedPipeline\FeedUnfollowPipeline;
class UnfollowPipeline implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $actor;
protected $target;
/**
@ -48,11 +47,11 @@ class UnfollowPipeline implements ShouldQueue
$target = $this->target;
$actorProfile = Profile::find($actor);
if(!$actorProfile) {
if (! $actorProfile) {
return;
}
$targetProfile = Profile::find($target);
if(!$targetProfile) {
if (! $targetProfile) {
return;
}
@ -60,37 +59,37 @@ class UnfollowPipeline implements ShouldQueue
FollowerService::remove($actor, $target);
$actorProfileSync = Cache::get(FollowerService::FOLLOWING_SYNC_KEY . $actor);
if(!$actorProfileSync) {
$actorProfileSync = Cache::get(FollowerService::FOLLOWING_SYNC_KEY.$actor);
if (! $actorProfileSync) {
FollowServiceWarmCache::dispatch($actor)->onQueue('low');
} else {
if($actorProfile->following_count) {
if ($actorProfile->following_count) {
$actorProfile->decrement('following_count');
} else {
$count = Follower::whereProfileId($actor)->count();
$actorProfile->following_count = $count;
$actorProfile->save();
}
Cache::put(FollowerService::FOLLOWING_SYNC_KEY . $actor, 1, 604800);
Cache::put(FollowerService::FOLLOWING_SYNC_KEY.$actor, 1, 604800);
AccountService::del($actor);
}
$targetProfileSync = Cache::get(FollowerService::FOLLOWERS_SYNC_KEY . $target);
if(!$targetProfileSync) {
$targetProfileSync = Cache::get(FollowerService::FOLLOWERS_SYNC_KEY.$target);
if (! $targetProfileSync) {
FollowServiceWarmCache::dispatch($target)->onQueue('low');
} else {
if($targetProfile->followers_count) {
if ($targetProfile->followers_count) {
$targetProfile->decrement('followers_count');
} else {
$count = Follower::whereFollowingId($target)->count();
$targetProfile->followers_count = $count;
$targetProfile->save();
}
Cache::put(FollowerService::FOLLOWERS_SYNC_KEY . $target, 1, 604800);
Cache::put(FollowerService::FOLLOWERS_SYNC_KEY.$target, 1, 604800);
AccountService::del($target);
}
if($targetProfile->domain == null) {
if ($targetProfile->domain == null) {
Notification::withTrashed()
->whereProfileId($target)
->whereAction('follow')
@ -98,20 +97,22 @@ class UnfollowPipeline implements ShouldQueue
->whereItemId($target)
->whereItemType('App\Profile')
->get()
->each(function($n) {
->each(function ($n) {
NotificationService::del($n->profile_id, $n->id);
$n->forceDelete();
});
}
if($actorProfile->domain == null && config('instance.timeline.home.cached')) {
Cache::forget('pf:timelines:home:' . $actor);
if ($actorProfile->domain == null && config('instance.timeline.home.cached')) {
Cache::forget('pf:timelines:home:'.$actor);
}
FollowRequest::whereFollowingId($target)
->whereFollowerId($actor)
->delete();
return;
AccountService::del($target);
AccountService::del($actor);
}
}

590
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -467,14 +467,14 @@ function await-database-ready()
case "${DB_CONNECTION:-}" in
mysql)
# shellcheck disable=SC2154
while ! echo "SELECT 1" | mysql --user="${DB_USERNAME}" --password="${DB_PASSWORD}" --host="${DB_HOST}" "${DB_DATABASE}" --silent >/dev/null; do
while ! echo "SELECT 1" | mysql --user="${DB_USERNAME}" --password="${DB_PASSWORD}" --host="${DB_HOST}" --port="${DOCKER_DB_HOST_PORT}" "${DB_DATABASE}" --silent >/dev/null; do
staggered-sleep
done
;;
pgsql)
# shellcheck disable=SC2154
while ! echo "SELECT 1" | PGPASSWORD="${DB_PASSWORD}" psql --user="${DB_USERNAME}" --host="${DB_HOST}" "${DB_DATABASE}" >/dev/null; do
while ! echo "SELECT 1" | PGPASSWORD="${DB_PASSWORD}" psql --user="${DB_USERNAME}" --host="${DB_HOST}" --port="${DOCKER_DB_HOST_PORT}" "${DB_DATABASE}" >/dev/null; do
staggered-sleep
done
;;

1381
package-lock.json generated

File diff suppressed because it is too large Load Diff

2
public/css/spa.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/admin.js vendored

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(()=>{"use strict";var e,r,n,o={},t={};function a(e){var r=t[e];if(void 0!==r)return r.exports;var n=t[e]={id:e,loaded:!1,exports:{}};return o[e].call(n.exports,n,n.exports,a),n.loaded=!0,n.exports}a.m=o,e=[],a.O=(r,n,o,t)=>{if(!n){var d=1/0;for(l=0;l<e.length;l++){for(var[n,o,t]=e[l],i=!0,c=0;c<n.length;c++)(!1&t||d>=t)&&Object.keys(a.O).every((e=>a.O[e](n[c])))?n.splice(c--,1):(i=!1,t<d&&(d=t));if(i){e.splice(l--,1);var s=o();void 0!==s&&(r=s)}}return r}t=t||0;for(var l=e.length;l>0&&e[l-1][2]>t;l--)e[l]=e[l-1];e[l]=[n,o,t]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var n in r)a.o(r,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:r[n]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((r,n)=>(a.f[n](e,r),r)),[])),a.u=e=>"js/"+{1179:"daci.chunk",1240:"discover~myhashtags.chunk",1645:"profile~following.bundle",2156:"dms.chunk",2966:"discover~hashtag.bundle",3688:"discover~serverfeed.chunk",4951:"home.chunk",6250:"discover~settings.chunk",6535:"discover.chunk",6740:"discover~memories.chunk",7399:"dms~message.chunk",7413:"error404.bundle",7521:"discover~findfriends.chunk",7744:"notifications.chunk",8087:"profile.chunk",8119:"i18n.bundle",8408:"post.chunk",8977:"profile~followers.bundle",9124:"compose.chunk",9919:"changelog.bundle"}[e]+"."+{1179:"34dc7bad3a0792cc",1240:"8886fc0d4736d819",1645:"7ca7cfa5aaae75e2",2156:"2b55effc0e8ba89f",2966:"a0f00fc7df1f313c",3688:"262bf7e3bce843c3",4951:"264eeb47bfac56c1",6250:"65d6f3cbe5323ed4",6535:"c2229e1d15bd3ada",6740:"37e0c325f900e163",7399:"976f7edaa6f71137",7413:"b397483e3991ab20",7521:"b1858bea66d9723b",7744:"0c5151643e4534aa",8087:"a2234f891ba86efd",8119:"93a02e275ac1a708",8408:"9184101a2b809af1",8977:"5d796e79f32d066c",9124:"a0cfdf07f5062445",9919:"bf44edbbfa14bd53"}[e]+".js",a.miniCssF=e=>({2305:"css/portfolio",2540:"css/landing",3364:"css/admin",6952:"css/appdark",8252:"css/app",8759:"css/spa"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},n="pixelfed:",a.l=(e,o,t,d)=>{if(r[e])r[e].push(o);else{var i,c;if(void 0!==t)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var f=s[l];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==n+t){i=f;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.setAttribute("data-webpack",n+t),i.src=e),r[e]=[o];var u=(n,o)=>{i.onerror=i.onload=null,clearTimeout(b);var t=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),t&&t.forEach((e=>e(o))),n)return n(o)},b=setTimeout(u.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=u.bind(null,i.onerror),i.onload=u.bind(null,i.onload),c&&document.head.appendChild(i)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={461:0,6952:0,8252:0,2305:0,3364:0,2540:0,8759:0};a.f.j=(r,n)=>{var o=a.o(e,r)?e[r]:void 0;if(0!==o)if(o)n.push(o[2]);else if(/^((69|82)52|2305|2540|3364|461|8759)$/.test(r))e[r]=0;else{var t=new Promise(((n,t)=>o=e[r]=[n,t]));n.push(o[2]=t);var d=a.p+a.u(r),i=new Error;a.l(d,(n=>{if(a.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var t=n&&("load"===n.type?"missing":n.type),d=n&&n.target&&n.target.src;i.message="Loading chunk "+r+" failed.\n("+t+": "+d+")",i.name="ChunkLoadError",i.type=t,i.request=d,o[1](i)}}),"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,n)=>{var o,t,[d,i,c]=n,s=0;if(d.some((r=>0!==e[r]))){for(o in i)a.o(i,o)&&(a.m[o]=i[o]);if(c)var l=c(a)}for(r&&r(n);s<d.length;s++)t=d[s],a.o(e,t)&&e[t]&&e[t][0](),e[t]=0;return a.O(l)},n=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];n.forEach(r.bind(null,0)),n.push=r.bind(null,n.push.bind(n))})(),a.nc=void 0})();
(()=>{"use strict";var e,r,n,o={},t={};function a(e){var r=t[e];if(void 0!==r)return r.exports;var n=t[e]={id:e,loaded:!1,exports:{}};return o[e].call(n.exports,n,n.exports,a),n.loaded=!0,n.exports}a.m=o,e=[],a.O=(r,n,o,t)=>{if(!n){var d=1/0;for(l=0;l<e.length;l++){for(var[n,o,t]=e[l],i=!0,c=0;c<n.length;c++)(!1&t||d>=t)&&Object.keys(a.O).every((e=>a.O[e](n[c])))?n.splice(c--,1):(i=!1,t<d&&(d=t));if(i){e.splice(l--,1);var s=o();void 0!==s&&(r=s)}}return r}t=t||0;for(var l=e.length;l>0&&e[l-1][2]>t;l--)e[l]=e[l-1];e[l]=[n,o,t]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var n in r)a.o(r,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:r[n]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce(((r,n)=>(a.f[n](e,r),r)),[])),a.u=e=>"js/"+{1179:"daci.chunk",1240:"discover~myhashtags.chunk",1645:"profile~following.bundle",2156:"dms.chunk",2966:"discover~hashtag.bundle",3688:"discover~serverfeed.chunk",4951:"home.chunk",6250:"discover~settings.chunk",6535:"discover.chunk",6740:"discover~memories.chunk",7399:"dms~message.chunk",7413:"error404.bundle",7521:"discover~findfriends.chunk",7744:"notifications.chunk",8087:"profile.chunk",8119:"i18n.bundle",8408:"post.chunk",8977:"profile~followers.bundle",9124:"compose.chunk",9919:"changelog.bundle"}[e]+"."+{1179:"e49239579f174211",1240:"25db2bcadb2836b5",1645:"9294aa1b560387c7",2156:"a42edfd973f6c593",2966:"1b11b46e0b28aa3f",3688:"4eb5e50270522771",4951:"8bbc3c5c38dde66d",6250:"3f3acf6b2d7f41a2",6535:"1404d3172761023b",6740:"315bb6896f3afec2",7399:"6cd795c99fc1a568",7413:"54601f9cdd0f7719",7521:"d0e638a697f821b4",7744:"1086603ea08d1017",8087:"33a4b9cb10dbbb6c",8119:"28bba3e12cdadf51",8408:"803d8c9f68415936",8977:"50a39058d98e16eb",9124:"47ba00abaa827b26",9919:"d5810c2672b6abc7"}[e]+".js",a.miniCssF=e=>({2305:"css/portfolio",2540:"css/landing",3364:"css/admin",6952:"css/appdark",8252:"css/app",8759:"css/spa"}[e]+".css"),a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},n="pixelfed:",a.l=(e,o,t,d)=>{if(r[e])r[e].push(o);else{var i,c;if(void 0!==t)for(var s=document.getElementsByTagName("script"),l=0;l<s.length;l++){var u=s[l];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==n+t){i=u;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.setAttribute("data-webpack",n+t),i.src=e),r[e]=[o];var f=(n,o)=>{i.onerror=i.onload=null,clearTimeout(b);var t=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),t&&t.forEach((e=>e(o))),n)return n(o)},b=setTimeout(f.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=f.bind(null,i.onerror),i.onload=f.bind(null,i.onload),c&&document.head.appendChild(i)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.p="/",(()=>{var e={461:0,6952:0,8252:0,2305:0,3364:0,2540:0,8759:0};a.f.j=(r,n)=>{var o=a.o(e,r)?e[r]:void 0;if(0!==o)if(o)n.push(o[2]);else if(/^((69|82)52|2305|2540|3364|461|8759)$/.test(r))e[r]=0;else{var t=new Promise(((n,t)=>o=e[r]=[n,t]));n.push(o[2]=t);var d=a.p+a.u(r),i=new Error;a.l(d,(n=>{if(a.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var t=n&&("load"===n.type?"missing":n.type),d=n&&n.target&&n.target.src;i.message="Loading chunk "+r+" failed.\n("+t+": "+d+")",i.name="ChunkLoadError",i.type=t,i.request=d,o[1](i)}}),"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,n)=>{var o,t,[d,i,c]=n,s=0;if(d.some((r=>0!==e[r]))){for(o in i)a.o(i,o)&&(a.m[o]=i[o]);if(c)var l=c(a)}for(r&&r(n);s<d.length;s++)t=d[s],a.o(e,t)&&e[t]&&e[t][0](),e[t]=0;return a.O(l)},n=self.webpackChunkpixelfed=self.webpackChunkpixelfed||[];n.forEach(r.bind(null,0)),n.push=r.bind(null,n.push.bind(n))})(),a.nc=void 0})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/spa.js vendored

File diff suppressed because one or more lines are too long

2
public/js/status.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/vendor.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,11 @@
{
"/js/app.js": "/js/app.js?id=d4fb3d91eb65298a439439305dd50dde",
"/js/app.js": "/js/app.js?id=3688fe4a647f902efc642afb1a50984e",
"/js/activity.js": "/js/activity.js?id=dea89d9571c6bd889dd0f51a154b3872",
"/js/components.js": "/js/components.js?id=ebf743614d3b943541f868a0bc3db9d8",
"/js/discover.js": "/js/discover.js?id=7c90d36829dfe34f19f5d1f545107db7",
"/js/profile.js": "/js/profile.js?id=daadd03b5dbf21aed9da191d2ddaaf09",
"/js/status.js": "/js/status.js?id=203ab55ba6796a2c40c856d935198f35",
"/js/timeline.js": "/js/timeline.js?id=ec7213d8adc45f4c3c8254a23d82eee7",
"/js/profile.js": "/js/profile.js?id=3525518f6fe5c963b9990836b1fc5255",
"/js/status.js": "/js/status.js?id=698e2cb5d324ebc4f238adc1dde67210",
"/js/timeline.js": "/js/timeline.js?id=6e17b91cb6203cff8a4daaba71eb42b1",
"/js/compose.js": "/js/compose.js?id=b91af90a8f7ffcebc23ac602a24f7d4c",
"/js/compose-classic.js": "/js/compose-classic.js?id=73f7993759f12417a544364c320e273b",
"/js/search.js": "/js/search.js?id=fd2788ca403610348dacddf102dcd532",
@ -16,40 +16,40 @@
"/js/profile-directory.js": "/js/profile-directory.js?id=1615064235d2acf08d84c3e3d1232d7e",
"/js/story-compose.js": "/js/story-compose.js?id=50d723634d8d22db14d630a02774e5b7",
"/js/direct.js": "/js/direct.js?id=2f7df211df1b62a0637ed87f2457e918",
"/js/admin.js": "/js/admin.js?id=6b704e46b54b8ec1b0600ff50b33f7b9",
"/js/spa.js": "/js/spa.js?id=cd6a07d612c09f17c34a6e8441768020",
"/js/admin.js": "/js/admin.js?id=0c438fd27a3e0ee99d0f2153295bcbfb",
"/js/spa.js": "/js/spa.js?id=81f014a7e6be725c0665d1a1c3d4f01d",
"/js/stories.js": "/js/stories.js?id=f3d502fa937e5fa90d173d5d7aa64e2c",
"/js/portfolio.js": "/js/portfolio.js?id=e8a1f57ef2c7c9ff40265502da5b84ac",
"/js/account-import.js": "/js/account-import.js?id=c17bc3e351e51eb64529a4b28c75d9d4",
"/js/portfolio.js": "/js/portfolio.js?id=d53caf31d3ef87b47fbc51a31ff943f8",
"/js/account-import.js": "/js/account-import.js?id=1d1d10a8a9ea46e9a66219ea6dc4c803",
"/js/admin_invite.js": "/js/admin_invite.js?id=0a0036f59cfb186f7698207ae432365b",
"/js/landing.js": "/js/landing.js?id=753a52aacb8bb884f50ed3ae9ed99a38",
"/js/landing.js": "/js/landing.js?id=d62950f5ece78070ffd1ffdcc38f687b",
"/js/remote_auth.js": "/js/remote_auth.js?id=37e5bdf3bc1896eee063db7a186b9876",
"/js/manifest.js": "/js/manifest.js?id=1aecad6992ea1cdb8054608c3604a5cc",
"/js/home.chunk.264eeb47bfac56c1.js": "/js/home.chunk.264eeb47bfac56c1.js?id=c5704eda3f241103f1ed1fa6fa4cefad",
"/js/compose.chunk.a0cfdf07f5062445.js": "/js/compose.chunk.a0cfdf07f5062445.js?id=71f4bcf44739473ee369521ea785f63e",
"/js/post.chunk.9184101a2b809af1.js": "/js/post.chunk.9184101a2b809af1.js?id=b24c0c9949fba5b3e76cb95ea8bb6e5a",
"/js/profile.chunk.a2234f891ba86efd.js": "/js/profile.chunk.a2234f891ba86efd.js?id=2c9f83cb28d3893a169200c8341dbbb1",
"/js/discover~memories.chunk.37e0c325f900e163.js": "/js/discover~memories.chunk.37e0c325f900e163.js?id=02137ba179f0f2f3819597f262f423b8",
"/js/discover~myhashtags.chunk.8886fc0d4736d819.js": "/js/discover~myhashtags.chunk.8886fc0d4736d819.js?id=0397f095e24b2bbdffd84be14bb9d8c4",
"/js/daci.chunk.34dc7bad3a0792cc.js": "/js/daci.chunk.34dc7bad3a0792cc.js?id=53da4c2b40ecc1164592bd0f66767284",
"/js/discover~findfriends.chunk.b1858bea66d9723b.js": "/js/discover~findfriends.chunk.b1858bea66d9723b.js?id=3be5b1f07a6fa6a52efae3165391c328",
"/js/discover~serverfeed.chunk.262bf7e3bce843c3.js": "/js/discover~serverfeed.chunk.262bf7e3bce843c3.js?id=4b8b7ab8f27e79fc4eef11c08ec9cbf8",
"/js/discover~settings.chunk.65d6f3cbe5323ed4.js": "/js/discover~settings.chunk.65d6f3cbe5323ed4.js?id=2f1be9f65c1abe119e7283fbb6a9c1c3",
"/js/discover.chunk.c2229e1d15bd3ada.js": "/js/discover.chunk.c2229e1d15bd3ada.js?id=a88392222bc4cd0ea8183980da6f6402",
"/js/notifications.chunk.0c5151643e4534aa.js": "/js/notifications.chunk.0c5151643e4534aa.js?id=f3239550ec43ea4ff0a0c841a8d6179f",
"/js/dms.chunk.2b55effc0e8ba89f.js": "/js/dms.chunk.2b55effc0e8ba89f.js?id=a97c2bb5c33a8265397f5712abc5d7f0",
"/js/dms~message.chunk.976f7edaa6f71137.js": "/js/dms~message.chunk.976f7edaa6f71137.js?id=5cd8c66f0cc7b483d3d848b9f24f0227",
"/js/profile~followers.bundle.5d796e79f32d066c.js": "/js/profile~followers.bundle.5d796e79f32d066c.js?id=7a607d5976217c7d5f7ca6784447b783",
"/js/profile~following.bundle.7ca7cfa5aaae75e2.js": "/js/profile~following.bundle.7ca7cfa5aaae75e2.js?id=8d3cf326f5f5a6a6e129f0148b6b69d1",
"/js/discover~hashtag.bundle.a0f00fc7df1f313c.js": "/js/discover~hashtag.bundle.a0f00fc7df1f313c.js?id=4bc97210a5b792f40c9c3516102fa23e",
"/js/error404.bundle.b397483e3991ab20.js": "/js/error404.bundle.b397483e3991ab20.js?id=d670737f4f52f3ecac26865c18cc585d",
"/js/i18n.bundle.93a02e275ac1a708.js": "/js/i18n.bundle.93a02e275ac1a708.js?id=945ffd7c76a92fbb5ab17c88f26ec739",
"/js/changelog.bundle.bf44edbbfa14bd53.js": "/js/changelog.bundle.bf44edbbfa14bd53.js?id=37af1fdc9f4a3226227946db8452eb52",
"/js/manifest.js": "/js/manifest.js?id=4e958b1146e4f0430a79b2e37adb0ee5",
"/js/home.chunk.8bbc3c5c38dde66d.js": "/js/home.chunk.8bbc3c5c38dde66d.js?id=2d0f1bd43e0ac2372493198a919d4c4f",
"/js/compose.chunk.47ba00abaa827b26.js": "/js/compose.chunk.47ba00abaa827b26.js?id=9670e07da2a58c201dfee2ed8d5308a1",
"/js/post.chunk.803d8c9f68415936.js": "/js/post.chunk.803d8c9f68415936.js?id=509a232386771d60e1fa32ed3c56a2b4",
"/js/profile.chunk.33a4b9cb10dbbb6c.js": "/js/profile.chunk.33a4b9cb10dbbb6c.js?id=62c0e217dba5a38d1a60fc08d6273b09",
"/js/discover~memories.chunk.315bb6896f3afec2.js": "/js/discover~memories.chunk.315bb6896f3afec2.js?id=a8bd9bc76eb0339a4eef06219fe7b513",
"/js/discover~myhashtags.chunk.25db2bcadb2836b5.js": "/js/discover~myhashtags.chunk.25db2bcadb2836b5.js?id=a9fe5b8f9aee962bb3df2b67af9294a4",
"/js/daci.chunk.e49239579f174211.js": "/js/daci.chunk.e49239579f174211.js?id=856de905931148b8ece9d57eebaf053c",
"/js/discover~findfriends.chunk.d0e638a697f821b4.js": "/js/discover~findfriends.chunk.d0e638a697f821b4.js?id=18b9be879d96a7fa7d3e78cc86fd75c9",
"/js/discover~serverfeed.chunk.4eb5e50270522771.js": "/js/discover~serverfeed.chunk.4eb5e50270522771.js?id=a2c5f363ee3d238eb377b6fa6c48964b",
"/js/discover~settings.chunk.3f3acf6b2d7f41a2.js": "/js/discover~settings.chunk.3f3acf6b2d7f41a2.js?id=d6695dd4bd366e3500e05834c2c1be2b",
"/js/discover.chunk.1404d3172761023b.js": "/js/discover.chunk.1404d3172761023b.js?id=64614afd8b4dc2e723d4be94f95269ee",
"/js/notifications.chunk.1086603ea08d1017.js": "/js/notifications.chunk.1086603ea08d1017.js?id=2b9a342745dfc15efee64a36479ec25b",
"/js/dms.chunk.a42edfd973f6c593.js": "/js/dms.chunk.a42edfd973f6c593.js?id=d145d14e3d6e42f2b0c48807a302d068",
"/js/dms~message.chunk.6cd795c99fc1a568.js": "/js/dms~message.chunk.6cd795c99fc1a568.js?id=84ebbb0bf9d25931c8a45573105688c6",
"/js/profile~followers.bundle.50a39058d98e16eb.js": "/js/profile~followers.bundle.50a39058d98e16eb.js?id=9464465b46bf93aa995f4f3ab6fa7e84",
"/js/profile~following.bundle.9294aa1b560387c7.js": "/js/profile~following.bundle.9294aa1b560387c7.js?id=7bf7b38c9b2967772cfce6a4871549b8",
"/js/discover~hashtag.bundle.1b11b46e0b28aa3f.js": "/js/discover~hashtag.bundle.1b11b46e0b28aa3f.js?id=a3130c4a838b9c7bbae2cb03dc2d6465",
"/js/error404.bundle.54601f9cdd0f7719.js": "/js/error404.bundle.54601f9cdd0f7719.js?id=d670737f4f52f3ecac26865c18cc585d",
"/js/i18n.bundle.28bba3e12cdadf51.js": "/js/i18n.bundle.28bba3e12cdadf51.js?id=63ef9390fe1b3c652bb4dbff0ebf11ab",
"/js/changelog.bundle.d5810c2672b6abc7.js": "/js/changelog.bundle.d5810c2672b6abc7.js?id=e00d3c004ad5274caac3ba360c734dc2",
"/css/appdark.css": "/css/appdark.css?id=7f9ba0a926020571e9c8fbedd2ec6a6f",
"/css/app.css": "/css/app.css?id=838b7d90a81e16b8a9adc8644237606a",
"/css/portfolio.css": "/css/portfolio.css?id=d98e354f173c6a8b729626384dceaa90",
"/css/admin.css": "/css/admin.css?id=20cdb9cce61b0e1bd9fb1aad30efcd2f",
"/css/landing.css": "/css/landing.css?id=589f3fa192867727925921b0f68ce022",
"/css/spa.css": "/css/spa.css?id=24b025007b418a7e7efd18d47fa56b85",
"/js/vendor.js": "/js/vendor.js?id=9e8c3caac2c4d0119e99070a0bb36dfc"
"/css/spa.css": "/css/spa.css?id=489dcce2f610f29cf967756288d13647",
"/js/vendor.js": "/js/vendor.js?id=a18881f6b7437334b43dc693a83ecaea"
}

View File

@ -10,79 +10,136 @@
body-class="list-group-flush p-0 rounded">
<div class="list-group text-center">
<div
v-if="status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer font-weight-bold"
@click="ctxMenuGoToPost()">
{{ $t('menu.viewPost') }}
<div v-if="status.visibility !== 'archived'" class="list-group-item d-flex p-0 m-0">
<div class="border-right p-2 w-50">
<a
v-if="status"
class="menu-option"
:href="status.url"
@click.prevent="ctxMenuGoToPost()">
<div class="action-icon-link">
<div class="icon"><i class="fal fa-images fa-lg"></i></div>
<p class="mb-0">{{ $t('menu.viewPost') }}</p>
</div>
</a>
</div>
<div
v-if="status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer font-weight-bold"
@click="ctxMenuGoToProfile()">
{{ $t('menu.viewProfile') }}
<div class="p-2 flex-grow-1">
<a
v-if="status"
class="menu-option"
:href="status.account.url"
@click.prevent="ctxMenuGoToProfile()">
<div class="action-icon-link">
<div class="icon"><i class="fal fa-user fa-lg"></i></div>
<p class="mb-0">{{ $t('menu.viewProfile') }}</p>
</div>
</a>
</div>
</div>
<div
<template v-if="ctxMenuRelationship">
<a
v-if="ctxMenuRelationship.following"
class="list-group-item menu-option text-danger"
href="#"
@click.prevent="handleUnfollow">
{{ $t('profile.unfollow') }}
</a>
<div v-else class="d-flex">
<div class="p-3 border-right w-50 text-center">
<a
class="small menu-option text-muted"
href="#"
@click.prevent="handleMute">
<div class="action-icon-link-inline">
<div class="icon"><i class="far" :class="[ ctxMenuRelationship.muting ? 'fa-eye' : 'fa-eye-slash' ]"></i></div>
<p class="text-muted mb-0">{{ ctxMenuRelationship.muting ? 'Unmute' : 'Mute' }}</p>
</div>
</a>
</div>
<div class="p-3 w-50">
<a
class="small menu-option text-danger"
href="#"
@click.prevent="handleBlock">
<div class="action-icon-link-inline">
<div class="icon"><i class="far fa-shield-alt"></i></div>
<p class="text-danger mb-0">Block</p>
</div>
</a>
</div>
</div>
</template>
<a
v-if="status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer font-weight-bold"
@click="ctxMenuShare()">
class="list-group-item menu-option"
href="#"
@click.prevent="ctxMenuShare()">
{{ $t('common.share') }}
</div>
</a>
<div
<a
v-if="status && profile && profile.is_admin == true && status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer font-weight-bold"
@click="ctxModMenuShow()">
class="list-group-item menu-option"
href="#"
@click.prevent="ctxModMenuShow()">
{{ $t('menu.moderationTools') }}
</div>
</a>
<div
<a
v-if="status && status.account.id != profile.id"
class="list-group-item rounded cursor-pointer text-danger font-weight-bold"
@click="ctxMenuReportPost()">
class="list-group-item menu-option text-danger"
href="#"
@click.prevent="ctxMenuReportPost()">
{{ $t('menu.report') }}
</div>
</a>
<div
<a
v-if="status && profile.id == status.account.id && status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer text-danger font-weight-bold"
@click="archivePost(status)">
class="list-group-item menu-option text-danger"
href="#"
@click.prevent="archivePost(status)">
{{ $t('menu.archive') }}
</div>
</a>
<div
<a
v-if="status && profile.id == status.account.id && status.visibility == 'archived'"
class="list-group-item rounded cursor-pointer text-danger font-weight-bold"
@click="unarchivePost(status)">
class="list-group-item menu-option text-danger"
href="#"
@click.prevent="unarchivePost(status)">
{{ $t('menu.unarchive') }}
</div>
</a>
<div
<a
v-if="config.ab.pue && status && profile.id == status.account.id && status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer text-danger font-weight-bold"
@click="editPost(status)">
class="list-group-item menu-option text-danger"
href="#"
@click.prevent="editPost(status)">
Edit
</div>
</a>
<div
<a
v-if="status && (profile.is_admin || profile.id == status.account.id) && status.visibility !== 'archived'"
class="list-group-item rounded cursor-pointer text-danger font-weight-bold"
@click="deletePost(status)">
class="list-group-item menu-option text-danger"
href="#"
@click.prevent="deletePost(status)">
<div v-if="isDeleting" class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
<div v-else>
{{ $t('common.delete') }}
</div>
</div>
</a>
<div
class="list-group-item rounded cursor-pointer text-lighter font-weight-bold"
@click="closeCtxMenu()">
<a
class="list-group-item menu-option text-lighter"
href="#"
@click.prevent="closeCtxMenu()">
{{ $t('common.cancel') }}
</div>
</a>
</div>
</b-modal>
@ -98,7 +155,7 @@
<div class="list-group text-center">
<p class="py-2 px-3 mb-0">
<div
class="text-center font-weight-bold text-danger">
class="text-center menu-option text-danger">
{{ $t('menu.moderationTools') }}
</div>
@ -107,38 +164,43 @@
</div>
</p>
<div
class="list-group-item rounded cursor-pointer"
@click="moderatePost(status, 'unlist')">
<a
class="list-group-item menu-option"
href="#"
@click.prevent="moderatePost(status, 'unlist')">
{{ $t('menu.unlistFromTimelines') }}
</div>
</a>
<div
<a
v-if="status.sensitive"
class="list-group-item rounded cursor-pointer"
@click="moderatePost(status, 'remcw')">
class="list-group-item menu-option"
href="#"
@click.prevent="moderatePost(status, 'remcw')">
{{ $t('menu.removeCW') }}
</div>
</a>
<div
<a
v-else
class="list-group-item rounded cursor-pointer"
@click="moderatePost(status, 'addcw')">
class="list-group-item menu-option"
href="#"
@click.prevent="moderatePost(status, 'addcw')">
{{ $t('menu.addCW') }}
</div>
</a>
<div
class="list-group-item rounded cursor-pointer"
@click="moderatePost(status, 'spammer')">
<a
class="list-group-item menu-option"
href="#"
@click.prevent="moderatePost(status, 'spammer')">
{{ $t('menu.markAsSpammer') }}<br />
<span class="small">{{ $t('menu.markAsSpammerText') }}</span>
</div>
</a>
<div
class="list-group-item rounded cursor-pointer text-lighter"
@click="ctxModMenuClose()">
<a
class="list-group-item menu-option text-lighter"
href="#"
@click.prevent="ctxModMenuClose()">
{{ $t('common.cancel') }}
</div>
</a>
</div>
</b-modal>
@ -170,10 +232,26 @@
rounded
size="sm"
body-class="list-group-flush p-0 rounded text-center">
<div class="list-group-item rounded cursor-pointer" @click="shareStatus(status, $event)">{{status.reblogged ? 'Unshare' : 'Share'}} {{ $t('menu.toFollowers') }}</div>
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">{{ $t('common.copyLink') }}</div>
<div v-if="status && status.local == true && !status.in_reply_to_id" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">{{ $t('menu.embed') }}</div>
<div class="list-group-item rounded cursor-pointer text-lighter" @click="closeCtxShareMenu()">{{ $t('common.cancel') }}</div>
<a
class="list-group-item menu-option"
href="#"
@click.prevent="ctxMenuCopyLink()">
{{ $t('common.copyLink') }}
</a>
<a
v-if="status && status.local == true && !status.in_reply_to_id"
class="list-group-item menu-option"
@click.prevent="ctxMenuEmbed()">
{{ $t('menu.embed') }}
</a>
<a
class="list-group-item menu-option text-lighter"
href="#"
@click.prevent="closeCtxShareMenu()">
{{ $t('common.cancel') }}
</a>
</b-modal>
<b-modal ref="ctxEmbedModal"
@ -339,19 +417,19 @@
ctxMenu() {
this.ctxMenuStatus = this.status;
this.ctxEmbedPayload = window.App.util.embed.post(this.status.url);
// if(this.status.account.id == this.profile.id) {
if(this.status.account.id == this.profile.id) {
this.ctxMenuRelationship = false;
this.$refs.ctxModal.show();
// } else {
// axios.get('/api/pixelfed/v1/accounts/relationships', {
// params: {
// 'id[]': this.status.account.id
// }
// }).then(res => {
// this.ctxMenuRelationship = res.data[0];
// this.$refs.ctxModal.show();
// });
// }
} else {
axios.get('/api/v1/accounts/relationships', {
params: {
'id[]': this.status.account.id
}
}).then(res => {
this.ctxMenuRelationship = res.data[0];
this.$refs.ctxModal.show();
});
}
},
closeCtxMenu() {
@ -667,28 +745,6 @@
}
},
shareStatus(status, $event) {
if($('body').hasClass('loggedIn') == false) {
return;
}
this.closeModals();
axios.post('/i/share', {
item: status.id
}).then(res => {
status.reblogs_count = res.data.count;
status.reblogged = !status.reblogged;
// if(status.reblogged) {
// swal('Success', 'You shared this post', 'success');
// } else {
// swal('Success', 'You unshared this post', 'success');
// }
}).catch(err => {
swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
});
},
statusUrl(status) {
if(status.account.local == true) {
this.$router.push({
@ -741,10 +797,18 @@
return;
}
if(window.confirm(this.$t('menu.deletePostConfirm')) == false) {
return;
}
swal({
title: 'Confirm Delete',
text: 'Are you sure you want to delete this post?',
icon: "warning",
buttons: true,
dangerMode: true,
})
.then(res => {
if(!res) {
this.closeModals();
this.isDeleting = false;
} else {
axios.post('/i/delete', {
type: 'status',
item: status.id
@ -753,8 +817,12 @@
this.closeModals();
this.isDeleting = false;
}).catch(err => {
this.closeModals();
this.isDeleting = false;
swal(this.$t('common.error'), this.$t('common.errorMsg'), 'error');
});
}
})
},
owner(status) {
@ -776,7 +844,7 @@
axios.post('/api/pixelfed/v2/status/' + status.id + '/archive')
.then(res => {
this.$emit('status-delete', status.id);
this.$emit('delete', status.id);
this.$emit('archived', status.id);
this.closeModals();
});
@ -797,7 +865,157 @@
editPost(status) {
this.closeModals();
this.$emit('edit', status);
},
handleMute() {
if(!this.ctxMenuRelationship) {
return;
}
let curState = this.ctxMenuRelationship.muting;
swal({
title: curState ? 'Confirm Unmute' : 'Confirm Mute',
text: curState ? 'Are you sure you want to unmute this account?' : 'Are you sure you want to mute this account?',
icon: "warning",
buttons: true,
dangerMode: true,
})
.then(res => {
if(!res) {
this.closeModals();
} else {
let url = curState ?
`/api/v1/accounts/${this.status.account.id}/unmute` :
`/api/v1/accounts/${this.status.account.id}/mute`;
axios.post(url)
.then(res => {
this.closeModals();
this.$emit('muted', this.status);
this.$store.commit('updateRelationship', [res.data]);
})
.catch(err => {
this.closeModals();
if(err && err.response && err.response.data && err.response.data.error) {
swal('Error', err.response.data.error, 'error');
} else {
swal('Oops!', 'An error occured, please try again later.', 'error');
}
})
}
})
},
handleBlock() {
if(!this.ctxMenuRelationship) {
return;
}
let curState = this.ctxMenuRelationship.blocking;
swal({
title: curState ? 'Confirm Unblock' : 'Confirm Block',
text: curState ? 'Are you sure you want to unblock this account?' : 'Are you sure you want to block this account?',
icon: "warning",
buttons: true,
dangerMode: true,
})
.then(res => {
if(!res) {
this.closeModals();
} else {
let url = curState ?
`/api/v1/accounts/${this.status.account.id}/unblock` :
`/api/v1/accounts/${this.status.account.id}/block`;
axios.post(url)
.then(res => {
this.closeModals();
this.$store.commit('updateRelationship', [res.data]);
this.$emit('muted', this.status);
})
.catch(err => {
this.closeModals();
if(err && err.response && err.response.data && err.response.data.error) {
swal('Error', err.response.data.error, 'error');
} else {
swal('Oops!', 'An error occured, please try again later.', 'error');
}
})
}
})
},
handleUnfollow() {
if(!this.ctxMenuRelationship) {
return;
}
swal({
title: 'Unfollow',
text: 'Are you sure you want to unfollow ' + this.status.account.username + '?',
icon: "warning",
buttons: true,
dangerMode: true,
})
.then(res => {
if(!res) {
this.closeModals();
} else {
axios.post(`/api/v1/accounts/${this.status.account.id}/unfollow`)
.then(res => {
this.closeModals();
this.$store.commit('updateRelationship', [res.data]);
this.$emit('unfollow', this.status);
})
.catch(err => {
this.closeModals();
if(err && err.response && err.response.data && err.response.data.error) {
swal('Error', err.response.data.error, 'error');
} else {
swal('Oops!', 'An error occured, please try again later.', 'error');
}
})
}
})
},
}
}
</script>
<style lang="scss" scoped>
.menu-option {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-decoration: none;
font-weight: 500;
color: var(--dark);
}
.list-group-item {
border-color: var(--border-color);
}
.action-icon-link {
display: flex;
flex-direction: column;
.icon {
opacity: 0.5;
margin-bottom: 5px;
}
p {
font-weight: 600;
font-size: 11px;
}
&-inline {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 8px;
p {
font-weight: bold;
}
}
}
</style>

View File

@ -169,7 +169,7 @@
<script type="text/javascript">
import BigPicture from 'bigpicture';
import ReadMore from './ReadMore.vue';
import VideoPlayer from './../../presenter/VideoPlayer.vue';
import VideoPlayer from '@/presenter/VideoPlayer.vue';
export default {
props: ['status'],

View File

@ -0,0 +1,75 @@
<template>
<div v-if="status.sensitive == true">
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">{{ status.spoiler_text ? status.spoiler_text : 'CW / NSFW / Hidden Media'}}</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<b-carousel :id="status.id + '-carousel'"
style="text-shadow: 1px 1px 2px #333; background-color: #000;"
controls
img-blank
background="#ffffff"
:interval="0"
>
<b-carousel-slide v-for="(media, index) in status.media_attachments" :key="media.id + '-media'">
<video v-if="media.type == 'video'" slot="img" class="embed-responsive-item" preload="none" controls playsinline loop :alt="media.description" width="100%" height="100%">
<source :src="media.url" :type="media.mime">
</video>
<div v-else-if="media.type == 'image'" slot="img" :title="media.description">
<img :class="media.filter_class + ' d-block img-fluid w-100'" :src="media.url" :alt="media.description" loading="lazy" onerror="this.onerror=null;this.src='/storage/no-preview.png'">
</div>
<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
</b-carousel-slide>
</b-carousel>
</details>
</div>
<div v-else class="w-100 h-100 p-0">
<!-- <b-carousel :id="status.id + '-carousel'"
style="text-shadow: 1px 1px 2px #333; background-color: #000;"
controls
img-blank
background="#ffffff"
:interval="0"
>
<b-carousel-slide v-for="(media, index) in status.media_attachments" :key="media.id + '-media'">
<video v-if="media.type == 'Video'" slot="img" class="embed-responsive-item" preload="none" controls loop :title="media.description" width="100%" height="100%" :poster="media.preview_url">
<source :src="media.url" :type="media.mime">
</video>
<div v-else-if="media.type == 'Image'" slot="img" :title="media.description">
<img :class="media.filter_class + ' d-block img-fluid w-100'" :src="media.url" :alt="media.description" loading="lazy">
</div>
<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
</b-carousel-slide>
</b-carousel> -->
<carousel ref="carousel" :centerMode="true" :loop="false" :per-page="1" :paginationPosition="'bottom-overlay'" paginationActiveColor="#3897f0" paginationColor="#dbdbdb" class="p-0 m-0">
<slide v-for="(media, index) in status.media_attachments" :key="'px-carousel-'+media.id + '-' + index" class="w-100 h-100 d-block mx-auto text-center" style="background: #000; display: flex;align-items: center;">
<video v-if="media.type == 'video'" class="embed-responsive-item" preload="none" controls loop :title="media.description" width="100%" height="100%">
<source :src="media.url" :type="media.mime">
</video>
<div v-else-if="media.type == 'image'" :title="media.description">
<img :class="media.filter_class + ' img-fluid w-100'" :src="media.url" :alt="media.description" loading="lazy" onerror="this.onerror=null;this.src='/storage/no-preview.png'">
</div>
<p v-else class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
</slide>
</carousel>
</div>
</template>
<script type="text/javascript">
export default {
props: ['status']
}
</script>

View File

@ -0,0 +1,188 @@
<template>
<div v-if="status.sensitive == true" class="content-label-wrapper">
<div class="text-light content-label">
<p class="text-center">
<i class="far fa-eye-slash fa-2x"></i>
</p>
<p class="h4 font-weight-bold text-center">
Sensitive Content
</p>
<p class="text-center py-2 content-label-text">
{{ status.spoiler_text ? status.spoiler_text : 'This album may contain sensitive content.'}}
</p>
<p class="mb-0">
<button @click="toggleContentWarning()" class="btn btn-outline-light btn-block btn-sm font-weight-bold">See Post</button>
</p>
</div>
<blur-hash-image
width="32"
height="32"
:punch="1"
:hash="status.media_attachments[0].blurhash"
:alt="altText(status)"/>
</div>
<div v-else class="w-100 h-100 p-0 album-wrapper">
<carousel ref="carousel" :centerMode="true" :loop="false" :per-page="1" :paginationPosition="'bottom-overlay'" paginationActiveColor="#3897f0" paginationColor="#dbdbdb" class="p-0 m-0" :id="'carousel-' + status.id">
<slide v-for="(img, index) in status.media_attachments" :key="'px-carousel-'+img.id + '-' + index" class="" style="background: #000; display: flex;align-items: center;" :title="img.description">
<img
class="img-fluid w-100 p-0"
:src="img.url"
:alt="altText(img)"
loading="lazy"
:data-bp="img.url"
onerror="this.onerror=null;this.src='/storage/no-preview.png'">
</slide>
</carousel>
<div class="album-overlay">
<p v-if="!status.sensitive && sensitive"
@click="status.sensitive = true"
style="
margin-top: 0;
padding: 10px;
color: #fff;
font-size: 10px;
text-align: right;
position: absolute;
top: 0;
right: 0;
border-top-left-radius: 5px;
cursor: pointer;
background: linear-gradient(0deg, rgba(0,0,0,0.5), rgba(0,0,0,0.5));
">
<i class="fas fa-eye-slash fa-lg"></i>
</p>
<p @click.prevent="toggleLightbox"
style="
margin-top: 0;
padding: 10px;
color: #fff;
font-size: 10px;
text-align: right;
position: absolute;
left: 0;
top: 0;
border-bottom-right-radius: 5px;
cursor: pointer;
background: linear-gradient(0deg, rgba(0,0,0,0.5), rgba(0,0,0,0.5));
">
<i class="fas fa-expand fa-lg"></i>
</p>
<p
v-if="status.media_attachments[0].license"
style="
margin-bottom: 0;
padding: 0 5px;
color: #fff;
font-size: 10px;
text-align: right;
position: absolute;
bottom: 0;
right: 0;
border-top-left-radius: 5px;
background: linear-gradient(0deg, rgba(0,0,0,0.5), rgba(0,0,0,0.5));
">
<a :href="status.url" class="font-weight-bold text-light">Photo</a> by <a :href="status.account.url" class="font-weight-bold text-light">&commat;{{status.account.username}}</a> licensed under <a :href="status.media_attachments[0].license.url" class="font-weight-bold text-light">{{status.media_attachments[0].license.title}}</a>
</p>
</div>
</div>
</template>
<script type="text/javascript">
import BigPicture from 'bigpicture';
export default {
props: ['status'],
data() {
return {
sensitive: this.status.sensitive,
cursor: 0
}
},
created() {
// window.addEventListener("keydown", this.keypressNavigation);
},
beforeDestroy() {
// window.removeEventListener("keydown", this.keypressNavigation);
},
methods: {
toggleContentWarning(status) {
this.$emit('togglecw');
},
toggleLightbox(e) {
BigPicture({
el: e.target,
gallery: '#carousel-' + this.status.id,
position: this.$refs.carousel.currentPage
})
},
altText(img) {
let desc = img.description;
if(desc) {
return desc;
}
return 'Photo was not tagged with any alt text.';
},
keypressNavigation(e) {
let ref = this.$refs.carousel;
if (e.keyCode == "37") {
e.preventDefault();
let direction = "backward";
ref.advancePage(direction);
ref.$emit("navigation-click", direction);
}
if (e.keyCode == "39") {
e.preventDefault();
let direction = "forward";
ref.advancePage(direction);
ref.$emit("navigation-click", direction);
}
}
}
}
</script>
<style type="text/css" scoped>
.card-img-top {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
.content-label-wrapper {
position: relative;
}
.content-label {
margin: 0;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
z-index: 2;
background: rgba(0, 0, 0, 0.2)
}
.album-wrapper {
position: relative;
}
</style>

View File

@ -0,0 +1,160 @@
<template>
<div v-if="status.sensitive == true" class="content-label-wrapper">
<div class="text-light content-label">
<p class="text-center">
<i class="far fa-eye-slash fa-2x"></i>
</p>
<p class="h4 font-weight-bold text-center">
Sensitive Content
</p>
<p class="text-center py-2 content-label-text">
{{ status.spoiler_text ? status.spoiler_text : 'This post may contain sensitive content.'}}
</p>
<p class="mb-0">
<button @click="toggleContentWarning()" class="btn btn-outline-light btn-block btn-sm font-weight-bold">See Post</button>
</p>
</div>
<blur-hash-image
width="32"
height="32"
:punch="1"
:hash="status.media_attachments[0].blurhash"
:alt="altText(status)"/>
</div>
<div v-else>
<div :title="status.media_attachments[0].description" style="position: relative;">
<img class="card-img-top"
:src="status.media_attachments[0].url"
loading="lazy"
:alt="altText(status)"
:width="width()"
:height="height()"
onerror="this.onerror=null;this.src='/storage/no-preview.png'"
@click.prevent="toggleLightbox">
<!-- <blur-hash-image
class="card-img-top"
width="32"
height="32"
:punch="1"
:hash="status.media_attachments[0].blurhash"
:src="status.media_attachments[0].url"
:alt="altText(status)"
@click.prevent="toggleLightbox"/> -->
<p v-if="!status.sensitive && sensitive"
@click="status.sensitive = true"
style="
margin-top: 0;
padding: 10px;
color: #fff;
font-size: 10px;
text-align: right;
position: absolute;
top: 0;
right: 0;
border-top-left-radius: 5px;
cursor: pointer;
background: linear-gradient(0deg, rgba(0,0,0,0.5), rgba(0,0,0,0.5));
">
<i class="fas fa-eye-slash fa-lg"></i>
</p>
<p
v-if="status.media_attachments[0].license"
style="
margin-bottom: 0;
padding: 0 5px;
color: #fff;
font-size: 10px;
text-align: right;
position: absolute;
bottom: 0;
right: 0;
border-top-left-radius: 5px;
background: linear-gradient(0deg, rgba(0,0,0,0.5), rgba(0,0,0,0.5));
"><a :href="status.url" class="font-weight-bold text-light">Photo</a> by <a :href="status.account.url" class="font-weight-bold text-light">&commat;{{status.account.username}}</a> licensed under <a :href="status.media_attachments[0].license.url" class="font-weight-bold text-light">{{status.media_attachments[0].license.title}}</a></p>
</div>
</div>
</template>
<style type="text/css" scoped>
.card-img-top {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
.content-label-wrapper {
position: relative;
}
.content-label {
margin: 0;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
z-index: 2;
background: rgba(0, 0, 0, 0.2)
}
</style>
<script type="text/javascript">
import BigPicture from 'bigpicture';
export default {
props: ['status'],
data() {
return {
sensitive: this.status.sensitive
}
},
mounted() {
},
methods: {
altText(status) {
let desc = status.media_attachments[0].description;
if(desc) {
return desc;
}
return 'Photo was not tagged with any alt text.';
},
toggleContentWarning(status) {
this.$emit('togglecw');
},
toggleLightbox(e) {
BigPicture({
el: e.target
})
},
width() {
if( !this.status.media_attachments[0].meta ||
!this.status.media_attachments[0].meta.original ||
!this.status.media_attachments[0].meta.original.width ) {
return;
}
return this.status.media_attachments[0].meta.original.width;
},
height() {
if( !this.status.media_attachments[0].meta ||
!this.status.media_attachments[0].meta.original ||
!this.status.media_attachments[0].meta.original.height ) {
return;
}
return this.status.media_attachments[0].meta.original.height;
}
}
}
</script>

View File

@ -0,0 +1,44 @@
<template>
<div v-if="status.sensitive == true">
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">{{ status.spoiler_text ? status.spoiler_text : 'CW / NSFW / Hidden Media'}}</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<b-carousel :id="status.id + '-carousel'"
style="text-shadow: 1px 1px 2px #333; background-color: #000;"
controls
img-blank
background="#ffffff"
:interval="0"
>
<b-carousel-slide v-for="(vid, index) in status.media_attachments" :key="vid.id + '-media'">
<video slot="img" class="embed-responsive-item" preload="none" controls playsinline loop :alt="vid.description" width="100%" height="100%">
<source :src="vid.url" :type="vid.mime">
</video>
</b-carousel-slide>
</b-carousel>
</details>
</div>
<div v-else>
<b-carousel :id="status.id + '-carousel'"
style="text-shadow: 1px 1px 2px #333; background-color: #000;"
controls
img-blank
background="#ffffff"
:interval="0"
>
<b-carousel-slide v-for="(vid, index) in status.media_attachments" :key="vid.id + '-media'">
<video slot="img" class="embed-responsive-item" preload="none" controls playsinline loop :alt="vid.description" width="100%" height="100%">
<source :src="vid.url" :type="vid.mime">
</video>
</b-carousel-slide>
</b-carousel>
</div>
</template>
<script type="text/javascript">
export default {
props: ['status']
}
</script>

View File

@ -29,10 +29,10 @@
</div>
<template v-else>
<video v-if="hasHls" ref="video" :class="{ fixedHeight: fixedHeight }" style="margin:0" playsinline controls autoplay="false" :poster="getPoster(status)">
<video v-if="hasHls" ref="video" :class="{ fixedHeight: fixedHeight }" style="margin:0" playsinline webkit-playsinline controls autoplay="false" :poster="getPoster(status)">
</video>
<video v-else class="card-img-top shadow" :class="{ fixedHeight: fixedHeight }" style="border-radius:15px;object-fit: contain;background-color: #000;" autoplay="false" controls :poster="getPoster(status)">
<video v-else class="card-img-top shadow" :class="{ fixedHeight: fixedHeight }" style="border-radius:15px;object-fit: contain;background-color: #000;" autoplay="false" playsinline webkit-playsinline controls :poster="getPoster(status)">
<source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
</video>
</template>

View File

@ -0,0 +1,90 @@
<template>
<div v-if="status.sensitive == true" class="content-label-wrapper">
<div class="text-light content-label">
<p class="text-center">
<i class="far fa-eye-slash fa-2x"></i>
</p>
<p class="h4 font-weight-bold text-center">
Sensitive Content
</p>
<p class="text-center py-2 content-label-text">
{{ status.spoiler_text ? status.spoiler_text : 'This post may contain sensitive content.'}}
</p>
<p class="mb-0">
<button @click="toggleContentWarning()" class="btn btn-outline-light btn-block btn-sm font-weight-bold">See Post</button>
</p>
</div>
<blur-hash-image
width="32"
height="32"
:punch="1"
:hash="status.media_attachments[0].blurhash"
:alt="altText(status)"/>
</div>
<div v-else class="embed-responsive embed-responsive-16by9">
<video class="video" controls playsinline webkit-playsinline preload="metadata" loop :data-id="status.id" :poster="poster()">
<source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
</video>
</div>
</template>
<style type="text/css" scoped>
.content-label-wrapper {
position: relative;
}
.content-label {
margin: 0;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
z-index: 2;
background: rgba(0, 0, 0, 0.2)
}
</style>
<script type="text/javascript">
export default {
props: ['status'],
methods: {
altText(status) {
let desc = status.media_attachments[0].description;
if(desc) {
return desc;
}
return 'Video was not tagged with any alt text.';
},
playOrPause(e) {
let el = e.target;
if(el.getAttribute('playing') == 1) {
el.removeAttribute('playing');
el.pause();
} else {
el.setAttribute('playing', 1);
el.play();
}
},
toggleContentWarning(status) {
this.$emit('togglecw');
},
poster() {
let url = this.status.media_attachments[0].preview_url;
if(url.endsWith('no-preview.jpg') || url.endsWith('no-preview.png')) {
return;
}
return url;
}
}
}
</script>

View File

@ -91,6 +91,8 @@
v-on:delete="deletePost"
v-on:report-modal="handleReport"
v-on:edit="handleEdit"
v-on:muted="handleMuted"
v-on:unfollow="handleUnfollow"
/>
<likes-modal
@ -543,6 +545,7 @@
deletePost() {
this.feed.splice(this.postIndex, 1);
this.forceUpdateIdx++;
},
counterChange(index, type) {
@ -788,6 +791,21 @@
.then(res => {
})
},
handleMuted(post) {
this.feed = this.feed.filter(p => {
return p.account.id !== post.account.id;
});
},
handleUnfollow(post) {
if(this.scope === 'home') {
this.feed = this.feed.filter(p => {
return p.account.id !== post.account.id;
});
}
this.updateProfile({ following_count: this.profile.following_count - 1 });
},
},
watch: {

View File

@ -22,7 +22,7 @@
:alt="altText(status)"/>
</div>
<div v-else class="embed-responsive embed-responsive-16by9">
<video class="video" controls playsinline preload="metadata" loop :data-id="status.id" :poster="poster()">
<video class="video" controls playsinline webkit-playsinline preload="metadata" loop :data-id="status.id" :poster="poster()">
<source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
</video>
</div>

View File

@ -48,27 +48,27 @@ Vue.use(VueTimeago, {
Vue.component(
'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default
require('./../components/presenter/PhotoPresenter.vue').default
);
Vue.component(
'video-presenter',
require('./components/presenter/VideoPresenter.vue').default
require('./../components/presenter/VideoPresenter.vue').default
);
Vue.component(
'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default
require('./../components/presenter/PhotoAlbumPresenter.vue').default
);
Vue.component(
'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default
require('./../components/presenter/VideoAlbumPresenter.vue').default
);
Vue.component(
'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default
require('./../components/presenter/MixedAlbumPresenter.vue').default
);
Vue.component(

View File

@ -1,26 +1,26 @@
Vue.component(
'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default
require('./../components/presenter/PhotoPresenter.vue').default
);
Vue.component(
'video-presenter',
require('./components/presenter/VideoPresenter.vue').default
require('./../components/presenter/VideoPresenter.vue').default
);
Vue.component(
'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default
require('./../components/presenter/PhotoAlbumPresenter.vue').default
);
Vue.component(
'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default
require('./../components/presenter/VideoAlbumPresenter.vue').default
);
Vue.component(
'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default
require('./../components/presenter/MixedAlbumPresenter.vue').default
);
Vue.component(

View File

@ -60,27 +60,27 @@ Vue.component(
Vue.component(
'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default
require('./../components/presenter/PhotoPresenter.vue').default
);
Vue.component(
'video-presenter',
require('./components/presenter/VideoPresenter.vue').default
require('./../components/presenter/VideoPresenter.vue').default
);
Vue.component(
'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default
require('./../components/presenter/PhotoAlbumPresenter.vue').default
);
Vue.component(
'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default
require('./../components/presenter/VideoAlbumPresenter.vue').default
);
Vue.component(
'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default
require('./../components/presenter/MixedAlbumPresenter.vue').default
);
Vue.component(

View File

@ -1,26 +1,26 @@
Vue.component(
'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default
require('./../components/presenter/PhotoPresenter.vue').default
);
Vue.component(
'video-presenter',
require('./components/presenter/VideoPresenter.vue').default
require('./../components/presenter/VideoPresenter.vue').default
);
Vue.component(
'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default
require('./../components/presenter/PhotoAlbumPresenter.vue').default
);
Vue.component(
'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default
require('./../components/presenter/VideoAlbumPresenter.vue').default
);
Vue.component(
'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default
require('./../components/presenter/MixedAlbumPresenter.vue').default
);
Vue.component(
@ -32,3 +32,13 @@ Vue.component(
'post-component',
require('./components/PostComponent.vue').default
);
// Vue.component(
// 'post-next',
// require('./components/PostNext.vue').default
// );
// Vue.component(
// 'video-component',
// require('./components/VideoComponent.vue').default
// );

View File

@ -5,27 +5,27 @@ Vue.component(
Vue.component(
'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default
require('./../components/presenter/PhotoPresenter.vue').default
);
Vue.component(
'video-presenter',
require('./components/presenter/VideoPresenter.vue').default
require('./../components/presenter/VideoPresenter.vue').default
);
Vue.component(
'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default
require('./../components/presenter/PhotoAlbumPresenter.vue').default
);
Vue.component(
'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default
require('./../components/presenter/VideoAlbumPresenter.vue').default
);
Vue.component(
'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default
require('./../components/presenter/MixedAlbumPresenter.vue').default
);
Vue.component(

View File

@ -189,6 +189,10 @@ a.text-dark:hover {
.border {
border: 1px solid var(--border-color) !important;
&-right {
border-color: var(--border-color) !important;
}
}
.bg-white,
@ -396,6 +400,10 @@ span.twitter-typeahead .tt-suggestion:focus {
color: var(--dark);
}
.modal-backdrop {
opacity: 0.8;
}
.timeline-status-component {
.username {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

View File

@ -1,53 +1,38 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<title>{{ $title ?? config('app.name', 'Pixelfed') }}</title>
<title>{{ $title ?? config_cache('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config_cache('app.name', 'pixelfed') }}">
<meta property="og:title" content="{{ $title ?? config_cache('app.name', 'pixelfed') }}">
<meta property="og:type" content="article">
<meta property="og:type" content="profile">
<meta property="og:url" content="{{$profile['url']}}">
<meta name="medium" content="image">
<meta name="theme-color" content="#10c5f8">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="shortcut icon" type="image/png" href="{{url('/img/favicon.png?v=2')}}">
<link rel="apple-touch-icon" type="image/png" href="{{url('/img/favicon.png?v=2')}}">
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<style type="text/css">
body.embed-card {
background: #fff !important;
margin: 0;
padding-bottom: 0;
}
.status-card-embed {
box-shadow: none;
border-radius: 4px;
overflow: hidden;
}
</style>
<style>.btn,img{vertical-align:middle}.btn,a{background-color:transparent}.btn:hover,a{text-decoration:none}.card,.col-4,.info-overlay,.square{position:relative}*,::after,::before{box-sizing:border-box}p{margin-top:0;margin-bottom:1rem}a{color:#2c78bf}a:hover{color:#1e5181;text-decoration:underline}img{border-style:none}.small{font-size:.875em;font-weight:400}.btn,body{font-size:.9rem;font-weight:400;line-height:1.6;color:#212529}.row{display:flex;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.col-4{width:100%;padding-right:15px;padding-left:15px;flex:0 0 33.33333333%;max-width:33.33333333%}.btn{display:inline-block;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.card,body{display:flex}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(44,120,191,.25)}.btn-primary:focus,.btn-primary:not(:disabled):not(.disabled):active:focus{box-shadow:0 0 0 .2rem rgba(76,140,201,.5)}.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn-primary,.btn-primary:disabled{color:#fff;background-color:#2c78bf;border-color:#2c78bf}.btn-primary:focus,.btn-primary:hover{background-color:#2564a0;border-color:#225e96;color:#fff}.btn-primary:not(:disabled):not(.disabled):active{color:#fff;background-color:#225e96;border-color:#20578b}.btn-sm{padding:.25rem .5rem;font-size:.7875rem;line-height:1.5;border-radius:.2rem}.card{flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card-body{flex:1 1 auto;min-height:1px;padding:1.25rem}.card-footer,.card-header{padding:.75rem 1.25rem;background-color:#fff}.card-header{margin-bottom:0;border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.bg-white{background-color:#fff!important}.border{border:1px solid #dee2e6!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.justify-content-between{justify-content:space-between!important}.align-items-center{align-items:center!important}.shadow-none{box-shadow:none!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-4{margin-top:1.5rem!important}.px-0{padding-right:0!important;padding-left:0!important}.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.px-1{padding-left:.25rem!important}.pl-2{padding-left:.5rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.text-center{text-align:center!important}.text-uppercase{text-transform:uppercase!important}.font-weight-bold{font-weight:700!important}a.text-dark:focus,a.text-dark:hover{color:#000!important}a.text-muted:focus,a.text-muted:hover{color:#454b50!important}.text-muted{color:#6c757d!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}img{page-break-inside:avoid}p{orphans:3;widows:3}body{min-width:992px!important}}body{margin:0;text-align:left;background-color:rgba(247,251,253,.4705882353);min-height:100vh;flex-flow:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}.text-dark{color:#212529!important}.square{width:100%}.square::after{content:"";display:block;padding-bottom:100%}.square-content{position:absolute;width:100%;height:100%;background-repeat:no-repeat;background-size:cover;background-position:50%}@media (max-width:576px){.card-md-border-0{border-width:0!important;border-radius:0!important}.card-md-rounded-0{border-width:1px 0;border-radius:0!important}}.card{box-shadow:0 2px 6px 0 hsla(0,0%,0%,.2);border:none}body.embed-card{background:#fff!important;margin:0;padding-bottom:0}.status-card-embed{box-shadow:none;border-radius:4px;overflow:hidden}.avatar{border-radius:100%}</style>
</head>
<body class="bg-white">
<div class="embed-card">
<div class="card status-card-embed card-md-rounded-0 border">
<div class="card-header d-inline-flex align-items-center justify-content-between bg-white">
<div>
<img src="{{$profile['avatar']}}" width="32px" height="32px" style="border-radius: 32px;">
<img src="{{$profile['avatar']}}" width="32" height="32" class="avatar" onerror="this.onerror=null;this.src='/storage/avatars/default.jpg';">
<a class="username font-weight-bold pl-2 text-dark" target="_blank" href="{{$profile['url']}}">
{{$profile['username']}}
</a>
</div>
<div>
<a class="small font-weight-bold text-muted pr-1" href="{{config('app.url')}}" target="_blank">{{config('pixelfed.domain.app')}}</a>
<img src="/img/pixelfed-icon-color.svg" width="26px">
<img src="/img/pixelfed-icon-color.svg" width="26" height="26">
</div>
</div>
<div class="card-body pb-1">
</div>
<div class="card-body pb-1">
<div class="d-flex justify-content-between align-items-center">
<div class="text-center">
<p class="mb-0 font-weight-bold prettyCount" data-count="{{$profile['statuses_count']}}"></p>
@ -58,61 +43,66 @@
<p class="mb-0 text-muted text-uppercase small font-weight-bold">Followers</p>
</div>
<div class="text-center">
<p class="mb-0"><a href="/i/intent/follow?user={{$profile['username']}}" class="btn btn-primary btn-sm py-1 px-4 text-uppercase font-weight-bold" target="_blank">Follow</a></p>
<p class="mb-0"><a href="{{config('app.url')}}/i/intent/follow?user={{$profile['username']}}" class="btn btn-primary btn-sm py-1 px-4 text-uppercase font-weight-bold" target="_blank">Follow</a></p>
</div>
</div>
<div class="row mt-4 mb-1 embed-row"></div>
</div>
<div class="card-footer bg-white">
</div>
<div class="row mt-4 mb-1 embed-row"></div>
</div>
<div class="card-footer bg-white">
<p class="text-center mb-0">
<a href="{{$profile['url']}}" class="font-weight-bold" target="_blank">View More Posts</a>
</p>
</div>
</div>
</div>
<script type="text/javascript" src="{{mix('js/manifest.js')}}"></script>
<script type="text/javascript" src="{{mix('js/vendor.js')}}"></script>
<script type="text/javascript" src="{{mix('js/app.js')}}"></script>
<script type="text/javascript">
</p>
</div>
</div>
</div>
<script type="text/javascript">
window.addEventListener("message", e=>{const t=e.data||{};});
</script>
<script type="text/javascript">document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});</script>
<script type="text/javascript">
document.querySelectorAll('.prettyCount').forEach(function(i) {
i.innerText = App.util.format.count(i.getAttribute('data-count'));
});
</script>
<script type="text/javascript">
axios.get('/api/pixelfed/v1/accounts/{{$profile['id']}}/statuses', {
params: {
only_media: true,
limit: 24
document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});
function formatCount(count = 0, locale = 'en-GB', notation = 'compact') {
if(count < 1) {
return 0;
}
})
return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count);
}
function generateElements(html) {
const template = document.createElement('template');
template.innerHTML = html.trim();
return template.content.children;
}
document.querySelectorAll('.prettyCount').forEach(function(i) {
i.innerText = formatCount(i.getAttribute('data-count'));
});
fetch("{{config('app.url')}}/api/pixelfed/v1/accounts/{{$profile['id']}}/statuses?only_media=true&limit=24")
.then(res => res.json())
.then(res => {
let parent = $('.embed-row');
res.data
.filter(res => res.pf_type == 'photo')
.filter(res => !res.sensitive)
let parent = document.querySelector('.embed-row');
res.filter(post => post.pf_type == 'photo' && !post.sensitive && post.visibility === 'public')
.slice(0, 9)
.forEach(post => {
let el = `<div class="col-4 mt-2 px-0">
<a class="card info-overlay card-md-border-0 px-1 shadow-none" href="${post.url}" target="_blank">
<div class="square">
<div class="square-content" style="background-image: url('${post.media_attachments[0].url}')">
</div>
</div>
</a>
</div>`;
parent.append(el);
.forEach((post, idx) => {
let mediaUrl = post.media_attachments[0].preview_url ? post.media_attachments[0].preview_url : post.media_attachments[0].url;
let html = `<div class="col-4 mt-2 px-0"><a class="card info-overlay card-md-border-0 px-1 shadow-none" href="${post.url}" target="_blank"><div class="square"><div class="square-content" style="background-image: url('${mediaUrl}')"></div></div></a></div>`;
let el = document.createElement('div');
el.innerHTML = html;
parent.appendChild(el.firstChild);
});
})
})
.finally(() => {
window.parent.postMessage({type:"setHeight",id:0,height:document.getElementsByTagName("html")[0].scrollHeight},"*");
window.addEventListener("message", e => {
const t = e.data || {};
if (window.parent && t.type === 'setHeight') {
updateHeight(t.id)
}
});
function updateHeight(id) {
setTimeout(() => {
window.parent.postMessage({type:"setHeight",id:0,height:document.getElementsByTagName("html")[0].scrollHeight},"*");
}, 5000);
})
</script>
window.parent.postMessage({
type: 'setHeight',
id: id,
height: document.documentElement.scrollHeight
}, "*");
}, 2500)
}
</script>
</body>
</html>

View File

@ -1,152 +1,49 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<title>{{ $title ?? config('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config('app.name', 'pixelfed') }}">
<meta property="og:title" content="{{ $title ?? config('app.name', 'pixelfed') }}">
<title>{{ $title ?? config_cache('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config_cache('app.name', 'pixelfed') }}">
<meta property="og:title" content="{{ $title ?? config_cache('app.name', 'pixelfed') }}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{$status->url()}}">
<meta property="og:url" content="{{$status['url']}}">
<meta name="medium" content="image">
<meta name="theme-color" content="#10c5f8">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="shortcut icon" type="image/png" href="/img/favicon.png?v=2">
<link rel="apple-touch-icon" type="image/png" href="/img/favicon.png?v=2">
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<style type="text/css">
body.embed-card {
background: #fff !important;
margin: 0;
padding-bottom: 0;
}
.status-card-embed {
box-shadow: none;
border-radius: 4px;
overflow: hidden;
}
</style>
<style type="text/css">hr,p{margin-bottom:1rem}.small,body{font-weight:400}.card,body{display:flex}*,::after,::before{box-sizing:border-box}p{margin-top:0}a{color:#2c78bf;text-decoration:none;background-color:transparent}a:hover{color:#1e5181;text-decoration:underline}img{vertical-align:middle;border-style:none}hr{box-sizing:content-box;height:0;overflow:visible;margin-top:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small{font-size:.875em}.card{position:relative;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card-body{flex:1 1 auto;min-height:1px;padding:1.25rem}.card-footer,.card-header{padding:.75rem 1.25rem;background-color:#fff}.card-header{margin-bottom:0;border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.bg-white{background-color:#fff!important}.border{border:1px solid #dee2e6!important}.d-inline-flex{display:inline-flex!important}.justify-content-between{justify-content:space-between!important}.align-items-center{align-items:center!important}.my-0{margin-top:0!important}.mb-0,.my-0{margin-bottom:0!important}.mb-2{margin-bottom:.5rem!important}.pr-1{padding-right:.25rem!important}.pl-2{padding-left:.5rem!important}.text-uppercase{text-transform:uppercase!important}.font-weight-bold{font-weight:700!important}a.text-dark:focus,a.text-dark:hover{color:#000!important}a.text-muted:focus,a.text-muted:hover{color:#454b50!important}.text-muted{color:#6c757d!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}img{page-break-inside:avoid}p{orphans:3;widows:3}body{min-width:992px!important}}body{margin:0;font-size:.9rem;line-height:1.6;color:#212529;text-align:left;background-color:rgba(247,251,253,.4705882353);min-height:100vh;flex-flow:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}.text-dark{color:#212529!important}@media (max-width:576px){.card-md-rounded-0{border-width:1px 0;border-radius:0!important}}.card{box-shadow:0 2px 6px 0 hsla(0,0%,0%,.2);border:none}.status-card-embed{box-shadow:none;border-radius:4px;overflow:hidden}body.embed-card{background:#fff!important;margin:0;padding-bottom:0}.avatar{border-radius:100%}</style>
</head>
<body class="bg-white">
<div class="embed-card">
@php($item = $status)
<div class="card status-card-embed card-md-rounded-0 border">
<div class="card-header d-inline-flex align-items-center bg-white">
<img src="{{$item->profile->avatarUrl()}}" width="32px" height="32px" style="border-radius: 32px;">
<a class="username font-weight-bold pl-2 text-dark" target="_blank" href="{{$item->profile->url()}}">
{{$item->profile->username}}
<img src="{{$status['account']['avatar']}}" width="32" height="32" class="avatar" onerror="this.onerror=null;this.src='/storage/avatars/default.jpg';">
<a class="username font-weight-bold pl-2 text-dark" target="_blank" rel="ugc" href="{{$status['account']['url']}}">
{{$status['account']['username']}}
</a>
</div>
<a href="{{$status->url()}}" target="_blank">
@php($status = $item)
@switch($status->viewType())
@case('photo')
@case('image')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<a class="max-hide-overflow {{$status->firstMedia()->filter_class}}" href="{{$status->url()}}" target="_blank">
<img class="card-img-top" src="{{$status->mediaUrl()}}">
</a>
</details>
@else
<div class="{{$status->firstMedia()->filter_class}}">
<img src="{{$status->mediaUrl()}}" width="100%">
<a href="{{$status['url']}}" target="_blank" rel="ugc">
<div>
<img src="{{$status['media_attachments'][0]['preview_url'] ?? $status['media_attachments'][0]['url']}}" width="100%">
</div>
@endif
@break
@case('photo:album')
<div id="photo-carousel-wrapper-{{$status->id}}" class="carousel slide carousel-fade mb-n3 " data-ride="carousel">
<ol class="carousel-indicators">
@for($i = 0; $i < $status->media_count; $i++)
<li data-target="#photo-carousel-wrapper-{{$status->id}}" data-slide-to="{{$i}}" class="{{$i == 0 ? 'active' : ''}}"></li>
@endfor
</ol>
<div class="carousel-inner">
@foreach($status->media()->orderBy('order')->get() as $media)
<div class="carousel-item {{$loop->iteration == 1 ? 'active' : ''}}">
<figure class="{{$media->filter_class}}">
<div class="float-right mr-3 badge badge-dark border border-secondary rounded-pill p-2" style="position:absolute;top:8px;right:0;margin-bottom:-20px;">{{$loop->iteration}}/{{$loop->count}}</div>
<img class="d-block w-100" src="{{$media->url()}}" alt="{{$status->caption}}">
</figure>
</div>
@endforeach
</div>
<a class="carousel-control-prev" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
@break
@case('video')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
</details>
@else
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
@endif
@break
@case('video-album')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
</details>
@else
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
@endif
@break
@endswitch
</a>
@if($layout != 'compact')
<div class="card-body">
<div class="view-more mb-2">
<a class="font-weight-bold" href="{{$status->url()}}" target="_blank">View More on Pixelfed</a>
<a class="font-weight-bold" href="{{$status['url']}}" target="_blank">View More on Pixelfed</a>
</div>
<hr>
<div class="caption">
<p class="my-0">
<span class="username font-weight-bold">
<bdi><a class="text-dark" href="{{$item->profile->url()}}" target="_blank">{{$item->profile->username}}</a></bdi>
<bdi><a class="text-dark" href="{{$status['account']['url']}}" target="_blank">{{$status['account']['username']}}</a></bdi>
</span>
@if($showCaption)
<span class="caption-container">{!! $item->rendered ?? e($item->caption) !!}</span>
<span class="caption-container">{{ $status['content_text'] }}</span>
@endif
</p>
</div>
@ -154,19 +51,22 @@
@endif
<div class="card-footer bg-white d-inline-flex justify-content-between align-items-center">
<div class="timestamp">
<p class="small text-uppercase mb-0"><a href="{{$item->url()}}" class="text-muted" target="_blank">{{$item->created_at->diffForHumans()}}</a></p>
<p class="small text-uppercase mb-0">
<a href="{{$status['url']}}" class="text-muted" target="_blank" rel="ugc">
{{now()->parse($status['created_at'])->diffForHumans()}}
</a>
</p>
</div>
<div>
<a class="small font-weight-bold text-muted pr-1" href="{{config('app.url')}}" target="_blank">{{config('pixelfed.domain.app')}}</a>
<img src="/img/pixelfed-icon-color.svg" width="26px">
<img src="/img/pixelfed-icon-color.svg" width="26" height="26" />
</div>
</div>
</div>
</div>
<script type="text/javascript">window.addEventListener("message",e=>{const t=e.data||{};window.parent&&"setHeight"===t.type&&window.parent.postMessage({type:"setHeight",id:t.id,height:document.getElementsByTagName("html")[0].scrollHeight},"*")});</script>
<script type="text/javascript">document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});</script>
<script type="text/javascript" src="{{ mix('js/manifest.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/vendor.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/app.js') }}"></script>
<script type="text/javascript">
window.addEventListener("message",e=>{const t=e.data||{};window.parent&&"setHeight"===t.type&&window.parent.postMessage({type:"setHeight",id:t.id,height:document.getElementsByTagName("html")[0].scrollHeight},"*")});
document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});
</script>
</body>
</html>

6
webpack.mix.js vendored
View File

@ -1,7 +1,9 @@
let mix = require('laravel-mix');
const fs = require("fs");
const path = require("path");
mix.before(() => {
fs.rmSync('public/css', { recursive: true, force: true });
fs.rmSync('public/js', { recursive: true, force: true });
});
@ -57,6 +59,10 @@ mix.options({
}
}
})
mix.alias({
'@': path.join(__dirname, 'resources/assets/components'),
'~': path.join(__dirname, 'resources/assets/js/components'),
});
mix.webpackConfig({
optimization: {
providedExports: false,