Compare commits

...

20 Commits

Author SHA1 Message Date
Mai-Lapyst 22db0ffc64
Merge 5ecbebc560 into 7f8bba4415 2024-04-25 08:18:46 -03:00
Daniel Supernault 7f8bba4415
Update CustomEmojiService, only return local emoji 2024-04-23 05:54:24 -06:00
Daniel Supernault e46bd6cc06
Disable config cache by default 2024-04-21 14:09:48 -06:00
Daniel Supernault a54b4fb038
Update profile embed, fix height bug 2024-04-20 05:59:01 -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 Supernault 65166570c5
Update profile embed view, fix height bug 2024-04-20 05:01:04 -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 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 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 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
Mai-Lapyst 5ecbebc560
Add support for redis username & url configuration; fixes #4909 2024-03-28 23:17:41 +01:00
42 changed files with 867 additions and 371 deletions

View File

@ -8,6 +8,7 @@ OPEN_REGISTRATION="false"
ENFORCE_EMAIL_VERIFICATION="false" ENFORCE_EMAIL_VERIFICATION="false"
PF_MAX_USERS="1000" PF_MAX_USERS="1000"
OAUTH_ENABLED="true" OAUTH_ENABLED="true"
ENABLE_CONFIG_CACHE=false
# Media Configuration # Media Configuration
PF_OPTIMIZE_IMAGES="true" PF_OPTIMIZE_IMAGES="true"

View File

@ -62,6 +62,13 @@
- Update PrivacySettings controller, refresh RelationshipService when unmute/unblocking ([b7322b68](https://github.com/pixelfed/pixelfed/commit/b7322b68)) - 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 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 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/)) - ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.13 (2024-03-05)](https://github.com/pixelfed/pixelfed/compare/v0.11.12...v0.11.13) ## [v0.11.13 (2024-03-05)](https://github.com/pixelfed/pixelfed/compare/v0.11.12...v0.11.13)

View File

@ -172,7 +172,7 @@ class ProfileController extends Controller
$user = $this->getCachedUser($username); $user = $this->getCachedUser($username);
abort_if(!$user, 404); abort_if(! $user, 404);
return redirect($user->url()); return redirect($user->url());
} }
@ -254,7 +254,7 @@ class ProfileController extends Controller
abort_if(! $profile || $profile['locked'] || ! $profile['local'], 404); 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(); $uid = User::whereProfileId($profile['id'])->first();
if (! $uid) { if (! $uid) {
return true; return true;
@ -348,7 +348,7 @@ class ProfileController extends Controller
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']); 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(); $exists = AccountInterstitial::whereUserId($profile->user_id)->where('is_spam', 1)->count();
if ($exists) { if ($exists) {
return true; return true;
@ -373,7 +373,7 @@ class ProfileController extends Controller
public function stories(Request $request, $username) 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(); $profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
$pid = $profile->id; $pid = $profile->id;
$authed = Auth::user()->profile_id; $authed = Auth::user()->profile_id;

View File

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

View File

@ -133,6 +133,7 @@ class CustomEmojiService
return CustomEmoji::when(!$pgsql, function($q, $pgsql) { return CustomEmoji::when(!$pgsql, function($q, $pgsql) {
return $q->groupBy('shortcode'); return $q->groupBy('shortcode');
}) })
->whereNull('uri')
->get() ->get()
->map(function($emojo) { ->map(function($emojo) {
$url = url('storage/' . $emojo->media_path); $url = url('storage/' . $emojo->media_path);

View File

@ -77,9 +77,11 @@ return [
'client' => env('REDIS_CLIENT', 'predis'), 'client' => env('REDIS_CLIENT', 'predis'),
'default' => [ 'default' => [
'url' => env('REDIS_URL', null),
'scheme' => env('REDIS_SCHEME', 'tcp'), 'scheme' => env('REDIS_SCHEME', 'tcp'),
'path' => env('REDIS_PATH'), 'path' => env('REDIS_PATH'),
'host' => env('REDIS_HOST', 'localhost'), 'host' => env('REDIS_HOST', 'localhost'),
'username' => env('REDIS_USERNAME', null),
'password' => env('REDIS_PASSWORD', null), 'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379), 'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DATABASE', 0), 'database' => env('REDIS_DATABASE', 0),

View File

@ -115,9 +115,11 @@ return [
'client' => env('REDIS_CLIENT', 'predis'), 'client' => env('REDIS_CLIENT', 'predis'),
'default' => [ 'default' => [
'url' => env('REDIS_URL', null),
'scheme' => env('REDIS_SCHEME', 'tcp'), 'scheme' => env('REDIS_SCHEME', 'tcp'),
'path' => env('REDIS_PATH'), 'path' => env('REDIS_PATH'),
'host' => env('REDIS_HOST', '127.0.0.1'), 'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME', null),
'password' => env('REDIS_PASSWORD', null), 'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379), 'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DATABASE', 0), 'database' => env('REDIS_DATABASE', 0),

2
public/embed.js vendored
View File

@ -1 +1 @@
!function(){var e;e=function(){var e=[];window.addEventListener("message",function(t){var n=t.data||{};"setHeight"===n.type&&e[n.id]&&(e[n.id].height=n.height)}),[].forEach.call(document.querySelectorAll("iframe.pixelfed__embed"),function(t){t.scrolling="no",t.style.overflow="hidden",e.push(t);var n=e.length-1;t.onload=function(){t.contentWindow.postMessage({type:"setHeight",id:n},"*")},t.onload()})},-1!==["interactive","complete"].indexOf(document.readyState)?e():document.addEventListener("DOMContentLoaded",e)}(); !function(){var e;e=function(){var e=[];window.addEventListener("message",function(t){var n=t.data||{};"setHeight"===n.type&&e[n.id]&&(e[n.id].height=n.height)}),[].forEach.call(document.querySelectorAll("iframe.pixelfed__embed"),function(t){t.scrolling="no",t.style.overflow="hidden",e.push(t);var n=e.length-1;t.onload=function(){t.contentWindow.postMessage({type:"setHeight",id:n},"*")},t.onload()})},-1!==["interactive","complete"].indexOf(document.readyState)?e():document.addEventListener("DOMContentLoaded",e)}();

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 c=1/0;for(l=0;l<e.length;l++){for(var[n,o,t]=e[l],i=!0,d=0;d<n.length;d++)(!1&t||c>=t)&&Object.keys(a.O).every((e=>a.O[e](n[d])))?n.splice(d--,1):(i=!1,t<c&&(c=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:"a498fff65c83f174",1240:"4cc859102b24780c",1645:"9294aa1b560387c7",2156:"a42edfd973f6c593",2966:"1b11b46e0b28aa3f",3688:"b4c4ca11f3498bf1",4951:"ccbe0267817f9a26",6250:"07417fd0cd9c5833",6535:"1404d3172761023b",6740:"321431bd290466d4",7399:"6cd795c99fc1a568",7413:"54601f9cdd0f7719",7521:"29c7f06a6a4c6f61",7744:"1086603ea08d1017",8087:"e86bfb0eb7723ddc",8119:"28bba3e12cdadf51",8408:"41ea9082b932e599",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,c)=>{if(r[e])r[e].push(o);else{var i,d;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||(d=!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),d&&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 c=a.p+a.u(r),i=new Error;a.l(c,(n=>{if(a.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var t=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src;i.message="Loading chunk "+r+" failed.\n("+t+": "+c+")",i.name="ChunkLoadError",i.type=t,i.request=c,o[1](i)}}),"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,n)=>{var o,t,[c,i,d]=n,s=0;if(c.some((r=>0!==e[r]))){for(o in i)a.o(i,o)&&(a.m[o]=i[o]);if(d)var l=d(a)}for(r&&r(n);s<c.length;s++)t=c[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

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

View File

@ -3,9 +3,9 @@
"/js/activity.js": "/js/activity.js?id=dea89d9571c6bd889dd0f51a154b3872", "/js/activity.js": "/js/activity.js?id=dea89d9571c6bd889dd0f51a154b3872",
"/js/components.js": "/js/components.js?id=ebf743614d3b943541f868a0bc3db9d8", "/js/components.js": "/js/components.js?id=ebf743614d3b943541f868a0bc3db9d8",
"/js/discover.js": "/js/discover.js?id=7c90d36829dfe34f19f5d1f545107db7", "/js/discover.js": "/js/discover.js?id=7c90d36829dfe34f19f5d1f545107db7",
"/js/profile.js": "/js/profile.js?id=daadd03b5dbf21aed9da191d2ddaaf09", "/js/profile.js": "/js/profile.js?id=3525518f6fe5c963b9990836b1fc5255",
"/js/status.js": "/js/status.js?id=203ab55ba6796a2c40c856d935198f35", "/js/status.js": "/js/status.js?id=698e2cb5d324ebc4f238adc1dde67210",
"/js/timeline.js": "/js/timeline.js?id=ec7213d8adc45f4c3c8254a23d82eee7", "/js/timeline.js": "/js/timeline.js?id=6e17b91cb6203cff8a4daaba71eb42b1",
"/js/compose.js": "/js/compose.js?id=b91af90a8f7ffcebc23ac602a24f7d4c", "/js/compose.js": "/js/compose.js?id=b91af90a8f7ffcebc23ac602a24f7d4c",
"/js/compose-classic.js": "/js/compose-classic.js?id=73f7993759f12417a544364c320e273b", "/js/compose-classic.js": "/js/compose-classic.js?id=73f7993759f12417a544364c320e273b",
"/js/search.js": "/js/search.js?id=fd2788ca403610348dacddf102dcd532", "/js/search.js": "/js/search.js?id=fd2788ca403610348dacddf102dcd532",
@ -17,24 +17,24 @@
"/js/story-compose.js": "/js/story-compose.js?id=50d723634d8d22db14d630a02774e5b7", "/js/story-compose.js": "/js/story-compose.js?id=50d723634d8d22db14d630a02774e5b7",
"/js/direct.js": "/js/direct.js?id=2f7df211df1b62a0637ed87f2457e918", "/js/direct.js": "/js/direct.js?id=2f7df211df1b62a0637ed87f2457e918",
"/js/admin.js": "/js/admin.js?id=0c438fd27a3e0ee99d0f2153295bcbfb", "/js/admin.js": "/js/admin.js?id=0c438fd27a3e0ee99d0f2153295bcbfb",
"/js/spa.js": "/js/spa.js?id=fc5a6119ba3daead75be22eca369dcad", "/js/spa.js": "/js/spa.js?id=81f014a7e6be725c0665d1a1c3d4f01d",
"/js/stories.js": "/js/stories.js?id=f3d502fa937e5fa90d173d5d7aa64e2c", "/js/stories.js": "/js/stories.js?id=f3d502fa937e5fa90d173d5d7aa64e2c",
"/js/portfolio.js": "/js/portfolio.js?id=d53caf31d3ef87b47fbc51a31ff943f8", "/js/portfolio.js": "/js/portfolio.js?id=d53caf31d3ef87b47fbc51a31ff943f8",
"/js/account-import.js": "/js/account-import.js?id=1d1d10a8a9ea46e9a66219ea6dc4c803", "/js/account-import.js": "/js/account-import.js?id=1d1d10a8a9ea46e9a66219ea6dc4c803",
"/js/admin_invite.js": "/js/admin_invite.js?id=0a0036f59cfb186f7698207ae432365b", "/js/admin_invite.js": "/js/admin_invite.js?id=0a0036f59cfb186f7698207ae432365b",
"/js/landing.js": "/js/landing.js?id=5b97a3da6c8142b374643763c7ce3693", "/js/landing.js": "/js/landing.js?id=d62950f5ece78070ffd1ffdcc38f687b",
"/js/remote_auth.js": "/js/remote_auth.js?id=37e5bdf3bc1896eee063db7a186b9876", "/js/remote_auth.js": "/js/remote_auth.js?id=37e5bdf3bc1896eee063db7a186b9876",
"/js/manifest.js": "/js/manifest.js?id=cedd1bc9b4ad502aff9074166aebaf72", "/js/manifest.js": "/js/manifest.js?id=4e958b1146e4f0430a79b2e37adb0ee5",
"/js/home.chunk.ccbe0267817f9a26.js": "/js/home.chunk.ccbe0267817f9a26.js?id=d7cd05d679e24e12eb4cd6dc9407e235", "/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/compose.chunk.47ba00abaa827b26.js": "/js/compose.chunk.47ba00abaa827b26.js?id=9670e07da2a58c201dfee2ed8d5308a1",
"/js/post.chunk.41ea9082b932e599.js": "/js/post.chunk.41ea9082b932e599.js?id=ec46a9cc22e6e01b0341c7851ba9d226", "/js/post.chunk.803d8c9f68415936.js": "/js/post.chunk.803d8c9f68415936.js?id=509a232386771d60e1fa32ed3c56a2b4",
"/js/profile.chunk.e86bfb0eb7723ddc.js": "/js/profile.chunk.e86bfb0eb7723ddc.js?id=df77e0f00aa05c609f431241d83e8f5f", "/js/profile.chunk.33a4b9cb10dbbb6c.js": "/js/profile.chunk.33a4b9cb10dbbb6c.js?id=62c0e217dba5a38d1a60fc08d6273b09",
"/js/discover~memories.chunk.321431bd290466d4.js": "/js/discover~memories.chunk.321431bd290466d4.js?id=d4c54f40fa9b1dad4bde85779dc780f2", "/js/discover~memories.chunk.315bb6896f3afec2.js": "/js/discover~memories.chunk.315bb6896f3afec2.js?id=a8bd9bc76eb0339a4eef06219fe7b513",
"/js/discover~myhashtags.chunk.4cc859102b24780c.js": "/js/discover~myhashtags.chunk.4cc859102b24780c.js?id=28a36fe80e7a08166d73fa703b912f15", "/js/discover~myhashtags.chunk.25db2bcadb2836b5.js": "/js/discover~myhashtags.chunk.25db2bcadb2836b5.js?id=a9fe5b8f9aee962bb3df2b67af9294a4",
"/js/daci.chunk.a498fff65c83f174.js": "/js/daci.chunk.a498fff65c83f174.js?id=164deda7c30d432b32f5676b0a7dfdbb", "/js/daci.chunk.e49239579f174211.js": "/js/daci.chunk.e49239579f174211.js?id=856de905931148b8ece9d57eebaf053c",
"/js/discover~findfriends.chunk.29c7f06a6a4c6f61.js": "/js/discover~findfriends.chunk.29c7f06a6a4c6f61.js?id=a99ea4f9171f58e20356da64e9cc2618", "/js/discover~findfriends.chunk.d0e638a697f821b4.js": "/js/discover~findfriends.chunk.d0e638a697f821b4.js?id=18b9be879d96a7fa7d3e78cc86fd75c9",
"/js/discover~serverfeed.chunk.b4c4ca11f3498bf1.js": "/js/discover~serverfeed.chunk.b4c4ca11f3498bf1.js?id=5c25d0b23e96f90d84137a639e59e6e2", "/js/discover~serverfeed.chunk.4eb5e50270522771.js": "/js/discover~serverfeed.chunk.4eb5e50270522771.js?id=a2c5f363ee3d238eb377b6fa6c48964b",
"/js/discover~settings.chunk.07417fd0cd9c5833.js": "/js/discover~settings.chunk.07417fd0cd9c5833.js?id=27ee8cb0cfa9535b7ad2f998424dd975", "/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/discover.chunk.1404d3172761023b.js": "/js/discover.chunk.1404d3172761023b.js?id=64614afd8b4dc2e723d4be94f95269ee",
"/js/notifications.chunk.1086603ea08d1017.js": "/js/notifications.chunk.1086603ea08d1017.js?id=2b9a342745dfc15efee64a36479ec25b", "/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.chunk.a42edfd973f6c593.js": "/js/dms.chunk.a42edfd973f6c593.js?id=d145d14e3d6e42f2b0c48807a302d068",

View File

@ -169,7 +169,7 @@
<script type="text/javascript"> <script type="text/javascript">
import BigPicture from 'bigpicture'; import BigPicture from 'bigpicture';
import ReadMore from './ReadMore.vue'; import ReadMore from './ReadMore.vue';
import VideoPlayer from './../../presenter/VideoPlayer.vue'; import VideoPlayer from '@/presenter/VideoPlayer.vue';
export default { export default {
props: ['status'], 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> </div>
<template v-else> <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>
<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"> <source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
</video> </video>
</template> </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

@ -22,7 +22,7 @@
:alt="altText(status)"/> :alt="altText(status)"/>
</div> </div>
<div v-else class="embed-responsive embed-responsive-16by9"> <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"> <source :src="status.media_attachments[0].url" :type="status.media_attachments[0].mime">
</video> </video>
</div> </div>

View File

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

View File

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

View File

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

View File

@ -1,26 +1,26 @@
Vue.component( Vue.component(
'photo-presenter', 'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default require('./../components/presenter/PhotoPresenter.vue').default
); );
Vue.component( Vue.component(
'video-presenter', 'video-presenter',
require('./components/presenter/VideoPresenter.vue').default require('./../components/presenter/VideoPresenter.vue').default
); );
Vue.component( Vue.component(
'photo-album-presenter', 'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default require('./../components/presenter/PhotoAlbumPresenter.vue').default
); );
Vue.component( Vue.component(
'video-album-presenter', 'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default require('./../components/presenter/VideoAlbumPresenter.vue').default
); );
Vue.component( Vue.component(
'mixed-album-presenter', 'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default require('./../components/presenter/MixedAlbumPresenter.vue').default
); );
Vue.component( Vue.component(
@ -32,3 +32,13 @@ Vue.component(
'post-component', 'post-component',
require('./components/PostComponent.vue').default 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( Vue.component(
'photo-presenter', 'photo-presenter',
require('./components/presenter/PhotoPresenter.vue').default require('./../components/presenter/PhotoPresenter.vue').default
); );
Vue.component( Vue.component(
'video-presenter', 'video-presenter',
require('./components/presenter/VideoPresenter.vue').default require('./../components/presenter/VideoPresenter.vue').default
); );
Vue.component( Vue.component(
'photo-album-presenter', 'photo-album-presenter',
require('./components/presenter/PhotoAlbumPresenter.vue').default require('./../components/presenter/PhotoAlbumPresenter.vue').default
); );
Vue.component( Vue.component(
'video-album-presenter', 'video-album-presenter',
require('./components/presenter/VideoAlbumPresenter.vue').default require('./../components/presenter/VideoAlbumPresenter.vue').default
); );
Vue.component( Vue.component(
'mixed-album-presenter', 'mixed-album-presenter',
require('./components/presenter/MixedAlbumPresenter.vue').default require('./../components/presenter/MixedAlbumPresenter.vue').default
); );
Vue.component( Vue.component(
@ -46,4 +46,4 @@ Vue.component(
Vue.component( Vue.component(
'story-component', 'story-component',
require('./components/StoryTimelineComponent.vue').default require('./components/StoryTimelineComponent.vue').default
); );

View File

@ -1,118 +1,108 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ app()->getLocale() }}"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<title>{{ $title ?? config_cache('app.name', 'Pixelfed') }}</title>
<title>{{ $title ?? config('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config_cache('app.name', 'pixelfed') }}"> <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: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 property="og:url" content="{{$profile['url']}}">
<meta name="medium" content="image"> <meta name="medium" content="image">
<meta name="theme-color" content="#10c5f8"> <meta name="theme-color" content="#10c5f8">
<meta name="apple-mobile-web-app-capable" content="yes"> <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="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 rel="apple-touch-icon" type="image/png" href="{{url('/img/favicon.png?v=2')}}">
<link href="{{ mix('css/app.css') }}" rel="stylesheet"> <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>
<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>
</head> </head>
<body class="bg-white"> <body class="bg-white">
<div class="embed-card"> <div class="embed-card">
<div class="card status-card-embed card-md-rounded-0 border"> <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 class="card-header d-inline-flex align-items-center justify-content-between bg-white">
<div> <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']}}"> <a class="username font-weight-bold pl-2 text-dark" target="_blank" href="{{$profile['url']}}">
{{$profile['username']}} {{$profile['username']}}
</a> </a>
</div> </div>
<div> <div>
<a class="small font-weight-bold text-muted pr-1" href="{{config('app.url')}}" target="_blank">{{config('pixelfed.domain.app')}}</a> <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 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>
<p class="mb-0 text-muted text-uppercase small font-weight-bold">Posts</p>
</div>
<div class="text-center">
<p class="mb-0 font-weight-bold prettyCount" data-count="{{$profile['followers_count']}}"></p>
<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="{{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">
<p class="text-center mb-0">
<a href="{{$profile['url']}}" class="font-weight-bold" target="_blank">View More Posts</a>
</p>
</div>
</div>
</div> </div>
</div> <script type="text/javascript">
<div class="card-body pb-1"> window.addEventListener("message", e=>{const t=e.data||{};});
<div class="d-flex justify-content-between align-items-center"> document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});
<div class="text-center"> function formatCount(count = 0, locale = 'en-GB', notation = 'compact') {
<p class="mb-0 font-weight-bold prettyCount" data-count="{{$profile['statuses_count']}}"></p> if(count < 1) {
<p class="mb-0 text-muted text-uppercase small font-weight-bold">Posts</p> return 0;
</div> }
<div class="text-center"> return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count);
<p class="mb-0 font-weight-bold prettyCount" data-count="{{$profile['followers_count']}}"></p> }
<p class="mb-0 text-muted text-uppercase small font-weight-bold">Followers</p> function generateElements(html) {
</div> const template = document.createElement('template');
<div class="text-center"> template.innerHTML = html.trim();
<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> return template.content.children;
</div> }
</div> document.querySelectorAll('.prettyCount').forEach(function(i) {
<div class="row mt-4 mb-1 embed-row"></div> i.innerText = formatCount(i.getAttribute('data-count'));
</div> });
<div class="card-footer bg-white"> fetch("{{config('app.url')}}/api/pixelfed/v1/accounts/{{$profile['id']}}/statuses?only_media=true&limit=24")
<p class="text-center mb-0"> .then(res => res.json())
<a href="{{$profile['url']}}" class="font-weight-bold" target="_blank">View More Posts</a> .then(res => {
</p> let parent = document.querySelector('.embed-row');
</div> res.filter(post => post.pf_type == 'photo' && !post.sensitive && post.visibility === 'public')
</div> .slice(0, 9)
</div> .forEach((post, idx) => {
<script type="text/javascript" src="{{mix('js/manifest.js')}}"></script> let mediaUrl = post.media_attachments[0].preview_url ? post.media_attachments[0].preview_url : post.media_attachments[0].url;
<script type="text/javascript" src="{{mix('js/vendor.js')}}"></script> 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>`;
<script type="text/javascript" src="{{mix('js/app.js')}}"></script> let el = document.createElement('div');
<script type="text/javascript"> el.innerHTML = html;
window.addEventListener("message", e=>{const t=e.data||{};}); parent.appendChild(el.firstChild);
</script> });
<script type="text/javascript">document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});</script> })
<script type="text/javascript"> window.addEventListener("message", e => {
document.querySelectorAll('.prettyCount').forEach(function(i) { const t = e.data || {};
i.innerText = App.util.format.count(i.getAttribute('data-count')); if (window.parent && t.type === 'setHeight') {
}); updateHeight(t.id)
</script> }
<script type="text/javascript"> });
axios.get('/api/pixelfed/v1/accounts/{{$profile['id']}}/statuses', {
params: { function updateHeight(id) {
only_media: true, setTimeout(() => {
limit: 24 window.parent.postMessage({
} type: 'setHeight',
}) id: id,
.then(res => { height: document.documentElement.scrollHeight
let parent = $('.embed-row'); }, "*");
res.data }, 2500)
.filter(res => res.pf_type == 'photo') }
.filter(res => !res.sensitive)
.slice(0, 9) </script>
.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);
})
})
.finally(() => {
window.parent.postMessage({type:"setHeight",id:0,height:document.getElementsByTagName("html")[0].scrollHeight},"*");
setTimeout(() => {
window.parent.postMessage({type:"setHeight",id:0,height:document.getElementsByTagName("html")[0].scrollHeight},"*");
}, 5000);
})
</script>
</body> </body>
</html> </html>

View File

@ -1,172 +1,72 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ app()->getLocale() }}"> <html lang="en">
<head> <head>
<meta charset="utf-8">
<meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes"> <title>{{ $title ?? config_cache('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config_cache('app.name', 'pixelfed') }}">
<title>{{ $title ?? config('app.name', 'Pixelfed') }}</title> <meta property="og:title" content="{{ $title ?? config_cache('app.name', 'pixelfed') }}">
<meta property="og:type" content="article">
<meta property="og:site_name" content="{{ config('app.name', 'pixelfed') }}"> <meta property="og:url" content="{{$status['url']}}">
<meta property="og:title" content="{{ $title ?? config('app.name', 'pixelfed') }}"> <meta name="medium" content="image">
<meta property="og:type" content="article"> <meta name="theme-color" content="#10c5f8">
<meta property="og:url" content="{{$status->url()}}"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="medium" content="image"> <link rel="shortcut icon" type="image/png" href="/img/favicon.png?v=2">
<meta name="theme-color" content="#10c5f8"> <link rel="apple-touch-icon" type="image/png" href="/img/favicon.png?v=2">
<meta name="apple-mobile-web-app-capable" content="yes"> <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>
<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>
</head> </head>
<body class="bg-white"> <body class="bg-white">
<div class="embed-card"> <div class="embed-card">
@php($item = $status) <div class="card status-card-embed card-md-rounded-0 border">
<div class="card status-card-embed card-md-rounded-0 border"> <div class="card-header d-inline-flex align-items-center bg-white">
<div class="card-header d-inline-flex align-items-center bg-white"> <img src="{{$status['account']['avatar']}}" width="32" height="32" class="avatar" onerror="this.onerror=null;this.src='/storage/avatars/default.jpg';">
<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" rel="ugc" href="{{$status['account']['url']}}">
<a class="username font-weight-bold pl-2 text-dark" target="_blank" href="{{$item->profile->url()}}"> {{$status['account']['username']}}
{{$item->profile->username}} </a>
</a> </div>
</div> <a href="{{$status['url']}}" target="_blank" rel="ugc">
<a href="{{$status->url()}}" target="_blank"> <div>
@php($status = $item) <img src="{{$status['media_attachments'][0]['preview_url'] ?? $status['media_attachments'][0]['url']}}" width="100%">
@switch($status->viewType()) </div>
@case('photo') </a>
@case('image') @if($layout != 'compact')
@if($status->is_nsfw) <div class="card-body">
<details class="details-animated"> <div class="view-more mb-2">
<summary> <a class="font-weight-bold" href="{{$status['url']}}" target="_blank">View More on Pixelfed</a>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p> </div>
<p class="font-weight-light">(click to show)</p> <hr>
</summary> <div class="caption">
<a class="max-hide-overflow {{$status->firstMedia()->filter_class}}" href="{{$status->url()}}" target="_blank"> <p class="my-0">
<img class="card-img-top" src="{{$status->mediaUrl()}}"> <span class="username font-weight-bold">
</a> <bdi><a class="text-dark" href="{{$status['account']['url']}}" target="_blank">{{$status['account']['username']}}</a></bdi>
</details> </span>
@else @if($showCaption)
<div class="{{$status->firstMedia()->filter_class}}"> <span class="caption-container">{{ $status['content_text'] }}</span>
<img src="{{$status->mediaUrl()}}" width="100%"> @endif
</div> </p>
@endif </div>
@break </div>
@case('photo:album') @endif
<div id="photo-carousel-wrapper-{{$status->id}}" class="carousel slide carousel-fade mb-n3 " data-ride="carousel"> <div class="card-footer bg-white d-inline-flex justify-content-between align-items-center">
<ol class="carousel-indicators"> <div class="timestamp">
@for($i = 0; $i < $status->media_count; $i++) <p class="small text-uppercase mb-0">
<li data-target="#photo-carousel-wrapper-{{$status->id}}" data-slide-to="{{$i}}" class="{{$i == 0 ? 'active' : ''}}"></li> <a href="{{$status['url']}}" class="text-muted" target="_blank" rel="ugc">
@endfor {{now()->parse($status['created_at'])->diffForHumans()}}
</ol> </a>
<div class="carousel-inner"> </p>
@foreach($status->media()->orderBy('order')->get() as $media) </div>
<div class="carousel-item {{$loop->iteration == 1 ? 'active' : ''}}"> <div>
<figure class="{{$media->filter_class}}"> <a class="small font-weight-bold text-muted pr-1" href="{{config('app.url')}}" target="_blank">{{config('pixelfed.domain.app')}}</a>
<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 src="/img/pixelfed-icon-color.svg" width="26" height="26" />
<img class="d-block w-100" src="{{$media->url()}}" alt="{{$status->caption}}"> </div>
</figure> </div>
</div> </div>
@endforeach </div>
</div> <script type="text/javascript">
<a class="carousel-control-prev" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="prev"> 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},"*")});
<span class="carousel-control-prev-icon" aria-hidden="true"></span> document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});
<span class="sr-only">Previous</span> </script>
</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>
</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>
</span>
@if($showCaption)
<span class="caption-container">{!! $item->rendered ?? e($item->caption) !!}</span>
@endif
</p>
</div>
</div>
@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>
</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">
</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>
</body> </body>
</html> </html>

54
webpack.mix.js vendored
View File

@ -1,8 +1,10 @@
let mix = require('laravel-mix'); let mix = require('laravel-mix');
const fs = require("fs"); const fs = require("fs");
const path = require("path");
mix.before(() => { mix.before(() => {
fs.rmSync('public/js', { recursive: true, force: true }); fs.rmSync('public/css', { recursive: true, force: true });
fs.rmSync('public/js', { recursive: true, force: true });
}); });
@ -46,31 +48,35 @@ mix.version();
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
mix.options({ mix.options({
processCssUrls: false, processCssUrls: false,
terser: { terser: {
parallel: true, parallel: true,
terserOptions: { terserOptions: {
compress: true, compress: true,
output: { output: {
comments: false comments: false
} }
} }
} }
}) })
mix.alias({
'@': path.join(__dirname, 'resources/assets/components'),
'~': path.join(__dirname, 'resources/assets/js/components'),
});
mix.webpackConfig({ mix.webpackConfig({
optimization: { optimization: {
providedExports: false, providedExports: false,
sideEffects: false, sideEffects: false,
usedExports: false, usedExports: false,
minimize: true, minimize: true,
minimizer: [ new TerserPlugin({ minimizer: [ new TerserPlugin({
extractComments: false, extractComments: false,
})] })]
}, },
output: { output: {
chunkFilename: 'js/[name].[chunkhash].js', chunkFilename: 'js/[name].[chunkhash].js',
} }
}); });
mix.autoload({ mix.autoload({
jquery: ['$', 'jQuery', 'window.jQuery'] jquery: ['$', 'jQuery', 'window.jQuery']
}); });