mirror of https://github.com/pixelfed/pixelfed.git
commit
3160f719b6
|
@ -2459,4 +2459,126 @@ class ApiV1Controller extends Controller
|
|||
->values();
|
||||
return response()->json(compact('posts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v2/statuses/{id}/replies
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function statusReplies(Request $request, $id)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'int|min:1|max:10',
|
||||
'sort' => 'in:all,newest,popular'
|
||||
]);
|
||||
|
||||
$limit = $request->input('limit', 3);
|
||||
$pid = $request->user()->profile_id;
|
||||
$status = StatusService::get($id);
|
||||
|
||||
abort_if(!in_array($status['visibility'], ['public', 'unlisted']), 404);
|
||||
|
||||
$sortBy = $request->input('sort', 'all');
|
||||
|
||||
if($sortBy == 'all' && !$request->has('cursor')) {
|
||||
$ids = Cache::remember('status:replies:all:' . $id, 900, function() use($id) {
|
||||
return DB::table('statuses')
|
||||
->where('in_reply_to_id', $id)
|
||||
->orderBy('id')
|
||||
->cursorPaginate(3);
|
||||
});
|
||||
} else {
|
||||
$ids = DB::table('statuses')
|
||||
->where('in_reply_to_id', $id)
|
||||
->when($sortBy, function($q, $sortBy) {
|
||||
if($sortBy === 'all') {
|
||||
return $q->orderBy('id');
|
||||
}
|
||||
|
||||
if($sortBy === 'newest') {
|
||||
return $q->orderByDesc('created_at');
|
||||
}
|
||||
|
||||
if($sortBy === 'popular') {
|
||||
return $q->orderByDesc('likes_count');
|
||||
}
|
||||
})
|
||||
->cursorPaginate($limit);
|
||||
}
|
||||
|
||||
$data = $ids->map(function($post) use($pid) {
|
||||
$status = StatusService::get($post->id);
|
||||
|
||||
if(!$status || !isset($status['id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$status['favourited'] = LikeService::liked($pid, $post->id);
|
||||
return $status;
|
||||
})
|
||||
->filter(function($post) {
|
||||
return $post && isset($post['id']);
|
||||
})
|
||||
->values();
|
||||
|
||||
$res = [
|
||||
'data' => $data,
|
||||
'next' => $ids->nextPageUrl()
|
||||
];
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v2/statuses/{id}/state
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function statusState(Request $request, $id)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$status = Status::findOrFail($id);
|
||||
$pid = $request->user()->profile_id;
|
||||
abort_if(!in_array($status->scope, ['public', 'unlisted', 'private']), 404);
|
||||
|
||||
return StatusService::getState($status->id, $pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/discover/accounts/popular
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function discoverAccountsPopular(Request $request)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
$pid = $request->user()->profile_id;
|
||||
|
||||
$ids = DB::table('profiles')
|
||||
->where('is_private', false)
|
||||
->whereNull('status')
|
||||
->orderByDesc('profiles.followers_count')
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
$ids = $ids->map(function($profile) {
|
||||
return AccountService::get($profile->id);
|
||||
})
|
||||
->filter(function($profile) use($pid) {
|
||||
return $profile &&
|
||||
isset($profile['id']) &&
|
||||
!FollowerService::follows($pid, $profile['id']) &&
|
||||
$profile['id'] != $pid;
|
||||
})
|
||||
->take(6)
|
||||
->values();
|
||||
|
||||
return response()->json($ids, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Cache;
|
||||
use DB;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\StatusService;
|
||||
use App\Services\SnowflakeService;
|
||||
|
||||
class SpaController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
abort_unless(config('exp.spa'), 404);
|
||||
return view('layouts.spa');
|
||||
}
|
||||
|
||||
public function webPost(Request $request, $id)
|
||||
{
|
||||
abort_unless(config('exp.spa'), 404);
|
||||
if($request->user()) {
|
||||
return view('layouts.spa');
|
||||
}
|
||||
|
||||
if(SnowflakeService::byDate(now()->subDays(30)) > $id) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$post = StatusService::get($id);
|
||||
|
||||
if(
|
||||
$post &&
|
||||
isset($post['url']) &&
|
||||
isset($post['local']) &&
|
||||
$post['local'] === true
|
||||
) {
|
||||
return redirect($post['url']);
|
||||
}
|
||||
|
||||
abort(404);
|
||||
}
|
||||
|
||||
public function webProfile(Request $request, $id)
|
||||
{
|
||||
abort_unless(config('exp.spa'), 404);
|
||||
if($request->user()) {
|
||||
if(substr($id, 0, 1) == '@') {
|
||||
$id = AccountService::usernameToId(substr($id, 1));
|
||||
return redirect("/i/web/profile/{$id}");
|
||||
}
|
||||
return view('layouts.spa');
|
||||
}
|
||||
|
||||
$account = AccountService::get($id);
|
||||
|
||||
if($account && isset($account['url'])) {
|
||||
return redirect($account['url']);
|
||||
}
|
||||
|
||||
return redirect('404');
|
||||
}
|
||||
|
||||
public function getPrivacy()
|
||||
{
|
||||
$body = $this->markdownToHtml('views/page/privacy.md');
|
||||
return [
|
||||
'body' => $body
|
||||
];
|
||||
}
|
||||
|
||||
public function getTerms()
|
||||
{
|
||||
$body = $this->markdownToHtml('views/page/terms.md');
|
||||
return [
|
||||
'body' => $body
|
||||
];
|
||||
}
|
||||
|
||||
protected function markdownToHtml($src, $ttl = 600)
|
||||
{
|
||||
return Cache::remember(
|
||||
'pf:doc_cache:markdown:' . $src,
|
||||
$ttl,
|
||||
function() use($src) {
|
||||
$path = resource_path($src);
|
||||
$file = file_get_contents($path);
|
||||
$converter = new CommonMarkConverter();
|
||||
return (string) $converter->convertToHtml($file);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -41,6 +41,25 @@ class StatusService
|
|||
});
|
||||
}
|
||||
|
||||
public static function getState($id, $pid)
|
||||
{
|
||||
$status = self::get($id, false);
|
||||
|
||||
if(!$status) {
|
||||
return [
|
||||
'liked' => false,
|
||||
'shared' => false,
|
||||
'bookmarked' => false
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'liked' => LikeService::liked($pid, $id),
|
||||
'shared' => self::isShared($id, $pid),
|
||||
'bookmarked' => self::isBookmarked($id, $pid)
|
||||
];
|
||||
}
|
||||
|
||||
public static function getFull($id, $pid, $publicOnly = true)
|
||||
{
|
||||
$res = self::get($id, $publicOnly);
|
||||
|
@ -89,4 +108,24 @@ class StatusService
|
|||
self::get($id, false);
|
||||
self::get($id, true);
|
||||
}
|
||||
|
||||
public static function isShared($id, $pid = null)
|
||||
{
|
||||
return $pid ?
|
||||
DB::table('statuses')
|
||||
->where('reblog_of_id', $id)
|
||||
->where('profile_id', $pid)
|
||||
->exists() :
|
||||
false;
|
||||
}
|
||||
|
||||
public static function isBookmarked($id, $pid = null)
|
||||
{
|
||||
return $pid ?
|
||||
DB::table('bookmarks')
|
||||
->where('status_id', $id)
|
||||
->where('profile_id', $pid)
|
||||
->exists() :
|
||||
false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,6 @@ return [
|
|||
'top' => env('EXP_TOP', false),
|
||||
'polls' => env('EXP_POLLS', false),
|
||||
'cached_public_timeline' => env('EXP_CPT', false),
|
||||
'gps' => env('EXP_GPS', false),
|
||||
'spa' => env('EXP_SPA', false),
|
||||
];
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/zYXgKVElMYYaJe8bpLHnCwDKhdzeFaxOedfTDw.woff2) format("woff2");unicode-range:U+0460-052f,U+1c80-1c88,U+20b4,U+2de0-2dff,U+a640-a69f,U+fe2e-fe2f}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/zYXgKVElMYYaJe8bpLHnCwDKhdXeFaxOedfTDw.woff2) format("woff2");unicode-range:U+0400-045f,U+0490-0491,U+04b0-04b1,U+2116}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/zYXgKVElMYYaJe8bpLHnCwDKhdLeFaxOedfTDw.woff2) format("woff2");unicode-range:U+0370-03ff}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/zYXgKVElMYYaJe8bpLHnCwDKhd7eFaxOedfTDw.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01a0-01a1,U+01af-01b0,U+1ea0-1ef9,U+20ab}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/zYXgKVElMYYaJe8bpLHnCwDKhd_eFaxOedfTDw.woff2) format("woff2");unicode-range:U+0100-024f,U+0259,U+1e??,U+2020,U+20a0-20ab,U+20ad-20cf,U+2113,U+2c60-2c7f,U+a720-a7ff}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/zYXgKVElMYYaJe8bpLHnCwDKhdHeFaxOedc.woff2) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/zYX9KVElMYYaJe8bpLHnCwDKjQ76AIxsdP3pBmtF8A.woff2) format("woff2");unicode-range:U+0460-052f,U+1c80-1c88,U+20b4,U+2de0-2dff,U+a640-a69f,U+fe2e-fe2f}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/zYX9KVElMYYaJe8bpLHnCwDKjQ76AIVsdP3pBmtF8A.woff2) format("woff2");unicode-range:U+0400-045f,U+0490-0491,U+04b0-04b1,U+2116}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/zYX9KVElMYYaJe8bpLHnCwDKjQ76AIJsdP3pBmtF8A.woff2) format("woff2");unicode-range:U+0370-03ff}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/zYX9KVElMYYaJe8bpLHnCwDKjQ76AI5sdP3pBmtF8A.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01a0-01a1,U+01af-01b0,U+1ea0-1ef9,U+20ab}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/zYX9KVElMYYaJe8bpLHnCwDKjQ76AI9sdP3pBmtF8A.woff2) format("woff2");unicode-range:U+0100-024f,U+0259,U+1e??,U+2020,U+20a0-20ab,U+20ad-20cf,U+2113,U+2c60-2c7f,U+a720-a7ff}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/zYX9KVElMYYaJe8bpLHnCwDKjQ76AIFsdP3pBms.woff2) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}body{background:#f3f4f6;font-family:IBM Plex Sans,sans-serif}.primary{color:#3b82f6}.web-wrapper{margin-bottom:10rem}.jumbotron{border-radius:18px}.doc-body p:last-child{margin-bottom: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
|
@ -1,21 +1,22 @@
|
|||
{
|
||||
"/js/manifest.js": "/js/manifest.js?id=7db827d654313dce4250",
|
||||
"/js/vendor.js": "/js/vendor.js?id=e95a4fe1c5a9580438d3",
|
||||
"/js/vendor.js": "/js/vendor.js?id=131c5e4445728ce4224a",
|
||||
"/js/activity.js": "/js/activity.js?id=1d855c03383566536e13",
|
||||
"/js/admin.js": "/js/admin.js?id=05c8d6766650050d8ef0",
|
||||
"/js/app.js": "/js/app.js?id=cc182786e3a5693b1433",
|
||||
"/js/app.js": "/js/app.js?id=26f7c3ba07877a3e7953",
|
||||
"/css/app.css": "/css/app.css?id=70554ace925b5a7ecef2",
|
||||
"/css/appdark.css": "/css/appdark.css?id=ccdcd84f1087f89bd7b2",
|
||||
"/css/admin.css": "/css/admin.css?id=8884d2f48c1dd491ba04",
|
||||
"/css/spa.css": "/css/spa.css?id=0dbac8e067414fa2195e",
|
||||
"/css/landing.css": "/css/landing.css?id=b6a06b969dd1c3c274e6",
|
||||
"/js/collectioncompose.js": "/js/collectioncompose.js?id=684458578e59365b9b7f",
|
||||
"/js/collections.js": "/js/collections.js?id=0d1fc5ad7d4a47a8adbb",
|
||||
"/js/components.js": "/js/components.js?id=56aa48f8042553148a78",
|
||||
"/js/compose.js": "/js/compose.js?id=fe56d0c40817ec2b0ec7",
|
||||
"/js/components.js": "/js/components.js?id=2978883dc0ca9cd1681c",
|
||||
"/js/compose.js": "/js/compose.js?id=76bc7fa5a894c0cac4c5",
|
||||
"/js/compose-classic.js": "/js/compose-classic.js?id=ee4ad4759a55261c429c",
|
||||
"/js/developers.js": "/js/developers.js?id=d578157a93aaa9070669",
|
||||
"/js/direct.js": "/js/direct.js?id=76bdac276fca6ac60320",
|
||||
"/js/discover.js": "/js/discover.js?id=4230e32e8f7d7413b6c4",
|
||||
"/js/discover.js": "/js/discover.js?id=32ad2e3e43a9b9c6d2c4",
|
||||
"/js/hashtag.js": "/js/hashtag.js?id=5dfd9c4873cc9d78727c",
|
||||
"/js/loops.js": "/js/loops.js?id=4ae79e81965c4fd1ae66",
|
||||
"/js/profile.js": "/js/profile.js?id=a2e8bd5346850f805b5c",
|
||||
|
@ -23,8 +24,9 @@
|
|||
"/js/rempos.js": "/js/rempos.js?id=2ef46b249c6390aa4ef8",
|
||||
"/js/rempro.js": "/js/rempro.js?id=3971b84aef886204d286",
|
||||
"/js/search.js": "/js/search.js?id=dc888b173463dc3894ba",
|
||||
"/js/status.js": "/js/status.js?id=4493959f638ad286630c",
|
||||
"/js/stories.js": "/js/stories.js?id=d52c02c0e4970022d76c",
|
||||
"/js/story-compose.js": "/js/story-compose.js?id=d60103d1ed56705af2dc",
|
||||
"/js/timeline.js": "/js/timeline.js?id=3adb0631a435da363ba7"
|
||||
"/js/spa.js": "/js/spa.js?id=432974792792e560cf8d",
|
||||
"/js/status.js": "/js/status.js?id=49fd7ac5b254220021f9",
|
||||
"/js/stories.js": "/js/stories.js?id=ca9d1db6b472a5d58392",
|
||||
"/js/story-compose.js": "/js/story-compose.js?id=0f9d2f7ce5e63ef14c6a",
|
||||
"/js/timeline.js": "/js/timeline.js?id=9957922a584a21f8f74f"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<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="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<title>{{ $title ?? config_cache('app.name') }}</title>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta property="og:site_name" content="{{ config_cache('app.name') }}">
|
||||
<meta property="og:title" content="{{ $title ?? config_cache('app.name') }}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="{{url(request()->url())}}">
|
||||
@stack('meta')
|
||||
<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 rel="canonical" href="{{url(request()->url())}}">
|
||||
<link href="{{ mix('css/app.css') }}" rel="stylesheet" data-stylesheet="light">
|
||||
<link href="{{ mix('css/spa.css') }}" rel="stylesheet" data-stylesheet="light">
|
||||
@auth
|
||||
<script type="text/javascript">
|
||||
window._sharedData = {
|
||||
curUser: {},
|
||||
user: {!! json_encode(\App\Services\ProfileService::get(request()->user()->profile_id)) !!},
|
||||
version: 0
|
||||
};
|
||||
window.App = {
|
||||
config: {!!App\Util\Site\Config::json()!!}
|
||||
};
|
||||
</script>
|
||||
@endauth
|
||||
</head>
|
||||
<body class="loggedIn">
|
||||
<main id="content">
|
||||
<noscript>
|
||||
<div class="container">
|
||||
<p class="pt-5 text-center lead">Please enable javascript to view this content.</p>
|
||||
</div>
|
||||
</noscript>
|
||||
<navbar></navbar>
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
<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/spa.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -85,6 +85,8 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
|
|||
|
||||
Route::group(['prefix' => 'v2'], function() use($middleware) {
|
||||
Route::get('search', 'Api\ApiV1Controller@searchV2')->middleware($middleware);
|
||||
Route::get('statuses/{id}/replies', 'Api\ApiV1Controller@statusReplies')->middleware($middleware);
|
||||
Route::get('statuses/{id}/state', 'Api\ApiV1Controller@statusState')->middleware($middleware);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -333,6 +333,11 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::get('warning', 'AccountInterstitialController@get');
|
||||
Route::post('warning', 'AccountInterstitialController@read');
|
||||
Route::get('my2020', 'SeasonalController@yearInReview');
|
||||
|
||||
Route::get('web/post/{id}', 'SpaController@webPost');
|
||||
Route::get('web/profile/{id}', 'SpaController@webProfile');
|
||||
Route::get('web/{q}', 'SpaController@index')->where('q', '.*');
|
||||
Route::get('web', 'SpaController@index');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'account'], function () {
|
||||
|
|
Loading…
Reference in New Issue