mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-26 16:23:16 +00:00
Merge pull request #1209 from pixelfed/frontend-ui-refactor
Labs + Profile Suggestions Experiment
This commit is contained in:
commit
ccef4d0939
17 changed files with 389 additions and 39 deletions
|
@ -3,10 +3,14 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Api\BaseApiController;
|
||||
use App\Like;
|
||||
use App\{
|
||||
Like,
|
||||
Profile
|
||||
};
|
||||
use Auth;
|
||||
use Cache;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\SuggestionService;
|
||||
|
||||
class ApiController extends BaseApiController
|
||||
{
|
||||
|
@ -39,11 +43,51 @@ class ApiController extends BaseApiController
|
|||
],
|
||||
|
||||
'ab' => [
|
||||
'lc' => config('exp.lc')
|
||||
'lc' => config('exp.lc'),
|
||||
'rec' => config('exp.rec'),
|
||||
],
|
||||
];
|
||||
});
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
public function userRecommendations(Request $request)
|
||||
{
|
||||
abort_if(!Auth::check(), 403);
|
||||
abort_if(!config('exp.rec'), 400);
|
||||
|
||||
$id = Auth::user()->profile->id;
|
||||
|
||||
$following = Cache::get('profile:following:'.$id, []);
|
||||
$ids = SuggestionService::get();
|
||||
|
||||
$res = Cache::remember('api:local:exp:rec:'.$id, now()->addMinutes(5), function() use($id, $following, $ids) {
|
||||
|
||||
array_push($following, $id);
|
||||
|
||||
return Profile::select(
|
||||
'id',
|
||||
'username'
|
||||
)
|
||||
->whereNotIn('id', $following)
|
||||
->whereIn('id', $ids)
|
||||
->whereIsPrivate(0)
|
||||
->whereNull('status')
|
||||
->whereNull('domain')
|
||||
->inRandomOrder()
|
||||
->take(4)
|
||||
->get()
|
||||
->map(function($item, $key) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'avatar' => $item->avatarUrl(),
|
||||
'username' => $item->username,
|
||||
'message' => 'Recommended for You'
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
return response()->json($res->all());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ class FollowerController extends Controller
|
|||
'follower_id' => $user->id,
|
||||
'following_id' => $target->id
|
||||
]);
|
||||
if($remote == true) {
|
||||
|
||||
}
|
||||
} elseif ($isFollowing == 0) {
|
||||
$follower = new Follower();
|
||||
$follower->profile_id = $user->id;
|
||||
|
@ -72,5 +75,6 @@ class FollowerController extends Controller
|
|||
Cache::forget('profile:followers:'.$target->id);
|
||||
Cache::forget('profile:following:'.$user->id);
|
||||
Cache::forget('profile:followers:'.$user->id);
|
||||
Cache::forget('api:local:exp:rec:'.$user->id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ trait HomeSettings
|
|||
]);
|
||||
|
||||
$changes = false;
|
||||
$name = strip_tags($request->input('name'));
|
||||
$name = strip_tags(Purify::clean($request->input('name')));
|
||||
$bio = $request->filled('bio') ? strip_tags(Purify::clean($request->input('bio'))) : null;
|
||||
$website = $request->input('website');
|
||||
$email = $request->input('email');
|
||||
|
|
83
app/Http/Controllers/Settings/LabsSettings.php
Normal file
83
app/Http/Controllers/Settings/LabsSettings.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Cookie, Redis;
|
||||
use App\Services\SuggestionService;
|
||||
|
||||
trait LabsSettings {
|
||||
|
||||
public function __constructor()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function labs(Request $request)
|
||||
{
|
||||
$profile = $request->user()->profile;
|
||||
return view('settings.labs', compact('profile'));
|
||||
}
|
||||
|
||||
public function labsStore(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'profile_layout' => 'nullable',
|
||||
'dark_mode' => 'nullable',
|
||||
'profile_suggestions' => 'nullable'
|
||||
]);
|
||||
|
||||
$changes = false;
|
||||
|
||||
$profile = $request->user()->profile;
|
||||
|
||||
$cookie = Cookie::forget('dark-mode');
|
||||
if($request->has('dark_mode') && $profile->profile_layout != 'moment') {
|
||||
if($request->dark_mode == 'on') {
|
||||
$cookie = Cookie::make('dark-mode', true, 43800);
|
||||
}
|
||||
}
|
||||
|
||||
if($request->has('profile_layout')) {
|
||||
if($profile->profile_layout != 'moment') {
|
||||
$profile->profile_layout = 'moment';
|
||||
$changes = true;
|
||||
} else {
|
||||
$profile->profile_layout = null;
|
||||
$changes = true;
|
||||
}
|
||||
} else {
|
||||
if($profile->profile_layout == 'moment') {
|
||||
$profile->profile_layout = null;
|
||||
$changes = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($request->has('profile_suggestions')) {
|
||||
if($profile->is_suggestable == false) {
|
||||
$profile->is_suggestable = true;
|
||||
$changes = true;
|
||||
SuggestionService::set($profile->id);
|
||||
} else {
|
||||
$profile->is_suggestable = false;
|
||||
$changes = true;
|
||||
SuggestionService::del($profile->id);
|
||||
}
|
||||
} else {
|
||||
if($profile->is_suggestable == true) {
|
||||
$profile->is_suggestable = false;
|
||||
$changes = true;
|
||||
SuggestionService::del($profile->id);
|
||||
}
|
||||
}
|
||||
|
||||
if($changes == true) {
|
||||
$profile->save();
|
||||
}
|
||||
|
||||
return redirect(route('settings.labs'))
|
||||
->with('status', 'Labs preferences successfully updated!')
|
||||
->cookie($cookie);
|
||||
}
|
||||
|
||||
}
|
49
app/Services/SuggestionService.php
Normal file
49
app/Services/SuggestionService.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Redis;
|
||||
use App\Profile;
|
||||
|
||||
class SuggestionService {
|
||||
|
||||
const CACHE_KEY = 'pf:services:suggestion:ids';
|
||||
|
||||
public static function get($start = 0, $stop = -1)
|
||||
{
|
||||
return Redis::zrange(self::CACHE_KEY, $start, $stop);
|
||||
}
|
||||
|
||||
public static function set($val)
|
||||
{
|
||||
return Redis::zadd(self::CACHE_KEY, 1, $val);
|
||||
}
|
||||
|
||||
public static function del($val)
|
||||
{
|
||||
return Redis::zrem(self::CACHE_KEY, $val);
|
||||
}
|
||||
|
||||
public static function add($val)
|
||||
{
|
||||
return self::set($val);
|
||||
}
|
||||
|
||||
public static function rem($val)
|
||||
{
|
||||
return self::del($val);
|
||||
}
|
||||
|
||||
public static function warmCache($force = false)
|
||||
{
|
||||
if(Redis::zcount(self::CACHE_KEY, '-inf', '+inf') == 0 || $force == true) {
|
||||
$ids = Profile::whereNull('domain')
|
||||
->whereIsSuggestable(true)
|
||||
->whereIsPrivate(false)
|
||||
->pluck('id');
|
||||
foreach($ids as $id) {
|
||||
self::set($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
return [
|
||||
|
||||
'lc' => env('EXP_LC', false)
|
||||
'lc' => env('EXP_LC', false),
|
||||
'rec' => env('EXP_REC', false)
|
||||
|
||||
];
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddSuggestionsToProfilesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('profiles', function (Blueprint $table) {
|
||||
$table->boolean('is_suggestable')->default(false)->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('profiles', function (Blueprint $table) {
|
||||
$table->dropColumn('is_suggestable');
|
||||
});
|
||||
}
|
||||
}
|
BIN
public/js/timeline.js
vendored
BIN
public/js/timeline.js
vendored
Binary file not shown.
Binary file not shown.
|
@ -3,16 +3,16 @@
|
|||
<div class="card notification-card">
|
||||
<div class="card-header bg-white">
|
||||
<p class="mb-0 d-flex align-items-center justify-content-between">
|
||||
<span class="text-muted font-weight-bold">Notifications</span>
|
||||
<span class="text-muted">Notifications</span>
|
||||
<a class="text-dark small" href="/account/activity">See All</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body loader text-center" style="height: 270px;">
|
||||
<div class="card-body loader text-center" style="height: 230px;">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body pt-2 contents" style="max-height: 270px; overflow-y: scroll;">
|
||||
<div class="card-body pt-2 contents" style="max-height: 230px; overflow-y: scroll;">
|
||||
<div v-if="notifications.length > 0" class="media mb-3 align-items-center" v-for="(n, index) in notifications">
|
||||
<img class="mr-2 rounded-circle" style="border:1px solid #ccc" :src="n.account.avatar" alt="" width="32px" height="32px">
|
||||
<div class="media-body font-weight-light small">
|
||||
|
|
|
@ -213,10 +213,7 @@
|
|||
</div>
|
||||
<hr>
|
||||
<p class="font-weight-bold">BETA FEATURES</p>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="mode-dark" v-on:click="modeDarkToggle()" v-model="modes.dark">
|
||||
<label class="custom-control-label font-weight-bold" for="mode-dark">Dark Mode</label>
|
||||
</div>
|
||||
<div class="alert alert-primary font-weight-bold text-center">Experimental features have been moved to the <a href="/settings/labs">Labs</a> settings page.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -225,6 +222,31 @@
|
|||
<notification-card></notification-card>
|
||||
</div>
|
||||
|
||||
<div v-show="suggestions.length && config.ab && config.ab.rec == true" class="mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-white text-muted d-flex justify-content-between align-items-center">
|
||||
<div>Suggestions For You</div>
|
||||
<div class="small text-dark"></div>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div v-for="(rec, index) in suggestions" class="media align-items-center mt-3">
|
||||
<a :href="'/'+rec.username">
|
||||
<img :src="rec.avatar" width="32px" height="32px" class="rounded-circle mr-3">
|
||||
</a>
|
||||
<div class="media-body">
|
||||
<p class="mb-0 font-weight-bold small">
|
||||
<a :href="'/'+rec.username" class="text-decoration-none text-dark">
|
||||
{{rec.username}}
|
||||
</a>
|
||||
</p>
|
||||
<p class="mb-0 small text-muted">{{rec.message}}</p>
|
||||
</div>
|
||||
<a class="font-weight-bold small" href="#" @click.prevent="expRecFollow(rec.id, index)">Follow</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div class="container pb-5">
|
||||
<p class="mb-0 text-uppercase font-weight-bold text-muted small">
|
||||
|
@ -431,6 +453,7 @@
|
|||
this.max_id = Math.min(...ids);
|
||||
$('.timeline .pagination').removeClass('d-none');
|
||||
this.loading = false;
|
||||
this.expRec();
|
||||
}).catch(err => {
|
||||
});
|
||||
},
|
||||
|
@ -927,6 +950,29 @@
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
expRec() {
|
||||
if(this.config.ab.rec == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.get('/api/local/exp/rec')
|
||||
.then(res => {
|
||||
this.suggestions = res.data;
|
||||
})
|
||||
},
|
||||
|
||||
expRecFollow(id, index) {
|
||||
if(this.config.ab.rec == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/i/follow', {
|
||||
item: id
|
||||
}).then(res => {
|
||||
this.suggestions.splice(index, 1);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<h3 class="font-weight-bold">Data Export</h3>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="alert alert-info font-weight-bold">We generate data exports once per hour, and they may not contain the latest data if you've requested them recently.</div>
|
||||
<div class="alert alert-primary px-3 h6">We generate data exports once per hour, and they may not contain the latest data if you've requested them recently.</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
|
|
|
@ -101,19 +101,7 @@
|
|||
<div class="pt-5">
|
||||
<p class="font-weight-bold text-muted text-center">Layout</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="email" class="col-sm-3 col-form-label font-weight-bold text-right">Profile Layout</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="profileLayout1" name="profile_layout" class="custom-control-input" {{Auth::user()->profile->profile_layout != 'moment' ? 'checked':''}} value="metro">
|
||||
<label class="custom-control-label" for="profileLayout1">MetroUI</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="profileLayout2" name="profile_layout" class="custom-control-input" {{Auth::user()->profile->profile_layout == 'moment' ? 'checked':''}} value="moment">
|
||||
<label class="custom-control-label" for="profileLayout2">MomentUI</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-primary font-weight-bold text-center">Experimental features have been moved to the <a href="/settings/labs">Labs</a> settings page.</div>
|
||||
<hr>
|
||||
@if(config('pixelfed.account_deletion') == true)
|
||||
<div class="form-group row py-3">
|
||||
|
|
63
resources/views/settings/labs.blade.php
Normal file
63
resources/views/settings/labs.blade.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
@extends('settings.template')
|
||||
|
||||
@section('section')
|
||||
<div class="title">
|
||||
<h3 class="font-weight-bold">Labs</h3>
|
||||
<p class="lead">Experimental features</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="alert alert-primary px-3 h6 text-center">
|
||||
<strong>Warning:</strong> Some experimental features may contain bugs or missing functionality
|
||||
</div>
|
||||
<div class="py-3">
|
||||
<p class="font-weight-bold text-muted text-center">UI</p>
|
||||
<hr>
|
||||
</div>
|
||||
<form method="post">
|
||||
@csrf
|
||||
@if(config('exp.lc') == true)
|
||||
<div class="form-check pb-3">
|
||||
<input class="form-check-input" type="checkbox" checked disabled>
|
||||
<label class="form-check-label font-weight-bold">
|
||||
{{__('Hidden like counts on Timelines')}}
|
||||
</label>
|
||||
<p class="text-muted small help-text">Like counts are hidden on timelines. This experiment was enabled for all users and can only be changed by the instance administrator.</p>
|
||||
</div>
|
||||
@endif
|
||||
<div class="form-check pb-3">
|
||||
<input class="form-check-input" type="checkbox" name="profile_layout" id="profile_layout" {{$profile->profile_layout == 'moment' ? 'checked':''}} value="{{$profile->profile_layout}}">
|
||||
<label class="form-check-label font-weight-bold" for="profile_layout">
|
||||
{{__('Use MomentUI for posts and your profile')}}
|
||||
</label>
|
||||
<p class="text-muted small help-text">MomentUI offers an alternative layout for posts and your profile.</p>
|
||||
</div>
|
||||
@if($profile->profile_layout != 'moment')
|
||||
<div class="form-check pb-3">
|
||||
<input class="form-check-input" type="checkbox" name="dark_mode" id="dark_mode" {{request()->hasCookie('dark-mode') ? 'checked':''}}>
|
||||
<label class="form-check-label font-weight-bold" for="dark_mode">
|
||||
{{__('MetroUI Dark Mode')}}
|
||||
</label>
|
||||
<p class="text-muted small help-text">Use dark mode theme.</p>
|
||||
</div>
|
||||
@endif
|
||||
<div class="py-3">
|
||||
<p class="font-weight-bold text-muted text-center">Discovery</p>
|
||||
<hr>
|
||||
</div>
|
||||
@if(config('exp.rec') == true)
|
||||
<div class="form-check pb-3">
|
||||
<input class="form-check-input" type="checkbox" name="profile_suggestions" id="profile_suggestions" {{$profile->is_suggestable ? 'checked' : ''}}>
|
||||
<label class="form-check-label font-weight-bold" for="profile_suggestions">
|
||||
{{__('Visible on Profile Suggestions')}}
|
||||
</label>
|
||||
<p class="text-muted small help-text">Allow your profile to be listed in Profile Suggestions.</p>
|
||||
</div>
|
||||
@endif
|
||||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<hr>
|
||||
<button type="submit" class="btn btn-primary font-weight-bold py-1 btn-block">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
|
@ -6,6 +6,26 @@
|
|||
<li class="nav-item pl-3 {{request()->is('settings/password')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.password')}}">Password</a>
|
||||
</li>
|
||||
{{--
|
||||
<li class="nav-item pl-3 {{request()->is('settings/accessibility')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.accessibility')}}">Accessibility</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/email')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.email')}}">Email</a>
|
||||
</li>
|
||||
@if(config('pixelfed.user_invites.enabled'))
|
||||
<li class="nav-item pl-3 {{request()->is('settings/invites*')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.invites')}}">Invites</a>
|
||||
</li>
|
||||
@endif
|
||||
<li class="nav-item pl-3 {{request()->is('settings/notifications')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.notifications')}}">Notifications</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/reports*')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.reports')}}">Reports</a>
|
||||
</li>
|
||||
--}}
|
||||
|
||||
<li class="nav-item pl-3 {{request()->is('settings/privacy*')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.privacy')}}">Privacy</a>
|
||||
</li>
|
||||
|
@ -15,8 +35,27 @@
|
|||
<li class="nav-item">
|
||||
<hr>
|
||||
</li>
|
||||
{{-- <li class="nav-item pl-3 {{request()->is('*import*')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.import')}}">Import</a>
|
||||
</li> --}}
|
||||
<li class="nav-item pl-3 {{request()->is('settings/data-export')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.dataexport')}}">Data Export</a>
|
||||
</li>
|
||||
{{--
|
||||
<li class="nav-item">
|
||||
<hr>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/applications')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.applications')}}">Applications</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/developers')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.developers')}}">Developers</a>
|
||||
</li> --}}
|
||||
<li class="nav-item">
|
||||
<hr>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/labs*')?'active':''}}">
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.labs')}}">Labs</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,6 +1,18 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
@if (session('status'))
|
||||
<div class="alert alert-primary px-3 h6 text-center">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger px-3 h6 text-center">
|
||||
@foreach($errors->all() as $error)
|
||||
<p class="font-weight-bold mb-1">{{ $error }}</li>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="container">
|
||||
<div class="col-12">
|
||||
|
@ -9,20 +21,6 @@
|
|||
<div class="row">
|
||||
@include('settings.partial.sidebar')
|
||||
<div class="col-12 col-md-9 p-5">
|
||||
@if (session('status'))
|
||||
<div class="alert alert-success font-weight-bold">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session('errors'))
|
||||
<div class="alert alert-danger">
|
||||
<ul class="mb-0">
|
||||
@foreach (session('errors') as $error)
|
||||
<li class="font-weight-bold">{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
@yield('section')
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -99,6 +99,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::group(['prefix' => 'local'], function () {
|
||||
Route::get('i/follow-suggestions', 'ApiController@followSuggestions');
|
||||
Route::post('status/compose', 'InternalApiController@compose');
|
||||
Route::get('exp/rec', 'ApiController@userRecommendations');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -231,6 +232,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::post('data-export/account', 'SettingsController@exportAccount')->middleware('dangerzone');
|
||||
Route::post('data-export/statuses', 'SettingsController@exportStatuses')->middleware('dangerzone');
|
||||
Route::get('developers', 'SettingsController@developers')->name('settings.developers')->middleware('dangerzone');
|
||||
Route::get('labs', 'SettingsController@labs')->name('settings.labs');
|
||||
Route::post('labs', 'SettingsController@labsStore');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'site'], function () {
|
||||
|
|
Loading…
Reference in a new issue