mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-23 13:33:18 +00:00
commit
d87eb0bfb8
26 changed files with 2902 additions and 565 deletions
|
@ -4,6 +4,7 @@
|
||||||
### Added
|
### Added
|
||||||
- Autocomplete Support (hashtags + mentions) ([de514f7d](https://github.com/pixelfed/pixelfed/commit/de514f7d))
|
- Autocomplete Support (hashtags + mentions) ([de514f7d](https://github.com/pixelfed/pixelfed/commit/de514f7d))
|
||||||
- Creative Commons Licenses ([552e950](https://github.com/pixelfed/pixelfed/commit/552e950))
|
- Creative Commons Licenses ([552e950](https://github.com/pixelfed/pixelfed/commit/552e950))
|
||||||
|
- Add Network Timeline ([af7face4](https://github.com/pixelfed/pixelfed/commit/af7face4))
|
||||||
|
|
||||||
### Updated
|
### Updated
|
||||||
- Updated AdminController, fix variable name in updateSpam method. ([6edaf940](https://github.com/pixelfed/pixelfed/commit/6edaf940))
|
- Updated AdminController, fix variable name in updateSpam method. ([6edaf940](https://github.com/pixelfed/pixelfed/commit/6edaf940))
|
||||||
|
|
|
@ -247,7 +247,7 @@ class AccountController extends Controller
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'user':
|
case 'user':
|
||||||
$profile = Profile::findOrFail($item);
|
$profile = Profile::findOrFail($item);
|
||||||
if ($profile->id == $user->id || $profile->user->is_admin == true) {
|
if ($profile->id == $user->id || ($profile->user && $profile->user->is_admin == true)) {
|
||||||
return abort(403);
|
return abort(403);
|
||||||
}
|
}
|
||||||
$class = get_class($profile);
|
$class = get_class($profile);
|
||||||
|
|
|
@ -351,7 +351,6 @@ class PublicApiController extends Controller
|
||||||
$fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer());
|
$fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer());
|
||||||
$res = $this->fractal->createData($fractal)->toArray();
|
$res = $this->fractal->createData($fractal)->toArray();
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function homeTimelineApi(Request $request)
|
public function homeTimelineApi(Request $request)
|
||||||
|
@ -470,12 +469,96 @@ class PublicApiController extends Controller
|
||||||
$fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer());
|
$fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer());
|
||||||
$res = $this->fractal->createData($fractal)->toArray();
|
$res = $this->fractal->createData($fractal)->toArray();
|
||||||
return response()->json($res);
|
return response()->json($res);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function networkTimelineApi(Request $request)
|
public function networkTimelineApi(Request $request)
|
||||||
{
|
{
|
||||||
return response()->json([]);
|
abort_if(!Auth::check(), 403);
|
||||||
|
abort_if(config('federation.network_timeline') == false, 404);
|
||||||
|
|
||||||
|
$this->validate($request,[
|
||||||
|
'page' => 'nullable|integer|max:40',
|
||||||
|
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||||
|
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||||
|
'limit' => 'nullable|integer|max:30'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$page = $request->input('page');
|
||||||
|
$min = $request->input('min_id');
|
||||||
|
$max = $request->input('max_id');
|
||||||
|
$limit = $request->input('limit') ?? 3;
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
|
$key = 'user:last_active_at:id:'.$user->id;
|
||||||
|
$ttl = now()->addMinutes(5);
|
||||||
|
Cache::remember($key, $ttl, function() use($user) {
|
||||||
|
$user->last_active_at = now();
|
||||||
|
$user->save();
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
if($min || $max) {
|
||||||
|
$dir = $min ? '>' : '<';
|
||||||
|
$id = $min ?? $max;
|
||||||
|
$timeline = Status::select(
|
||||||
|
'id',
|
||||||
|
'uri',
|
||||||
|
'caption',
|
||||||
|
'rendered',
|
||||||
|
'profile_id',
|
||||||
|
'type',
|
||||||
|
'in_reply_to_id',
|
||||||
|
'reblog_of_id',
|
||||||
|
'is_nsfw',
|
||||||
|
'scope',
|
||||||
|
'local',
|
||||||
|
'reply_count',
|
||||||
|
'comments_disabled',
|
||||||
|
'place_id',
|
||||||
|
'likes_count',
|
||||||
|
'reblogs_count',
|
||||||
|
'created_at',
|
||||||
|
'updated_at'
|
||||||
|
)->where('id', $dir, $id)
|
||||||
|
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||||
|
->whereNotNull('uri')
|
||||||
|
->whereScope('public')
|
||||||
|
// ->where('created_at', '>', now()->subMonths(3))
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->limit($limit)
|
||||||
|
->get();
|
||||||
|
} else {
|
||||||
|
$timeline = Status::select(
|
||||||
|
'id',
|
||||||
|
'uri',
|
||||||
|
'caption',
|
||||||
|
'rendered',
|
||||||
|
'profile_id',
|
||||||
|
'type',
|
||||||
|
'in_reply_to_id',
|
||||||
|
'reblog_of_id',
|
||||||
|
'is_nsfw',
|
||||||
|
'scope',
|
||||||
|
'local',
|
||||||
|
'reply_count',
|
||||||
|
'comments_disabled',
|
||||||
|
'created_at',
|
||||||
|
'place_id',
|
||||||
|
'likes_count',
|
||||||
|
'reblogs_count',
|
||||||
|
'updated_at'
|
||||||
|
)->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
|
||||||
|
->with('profile', 'hashtags', 'mentions')
|
||||||
|
->whereNotNull('uri')
|
||||||
|
->whereScope('public')
|
||||||
|
->where('created_at', '>', now()->subMonths(3))
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->simplePaginate($limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer());
|
||||||
|
$res = $this->fractal->createData($fractal)->toArray();
|
||||||
|
return response()->json($res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function relationships(Request $request)
|
public function relationships(Request $request)
|
||||||
|
|
|
@ -2,12 +2,6 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Auth, Cache;
|
|
||||||
use App\Follower;
|
|
||||||
use App\Profile;
|
|
||||||
use App\Status;
|
|
||||||
use App\User;
|
|
||||||
use App\UserFilter;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class TimelineController extends Controller
|
class TimelineController extends Controller
|
||||||
|
@ -29,6 +23,7 @@ class TimelineController extends Controller
|
||||||
|
|
||||||
public function network(Request $request)
|
public function network(Request $request)
|
||||||
{
|
{
|
||||||
|
abort_if(config('federation.network_timeline') == false, 404);
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
'layout' => 'nullable|string|in:grid,feed'
|
'layout' => 'nullable|string|in:grid,feed'
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -10,7 +10,6 @@ return [
|
||||||
| ActivityPub configuration
|
| ActivityPub configuration
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'activitypub' => [
|
'activitypub' => [
|
||||||
'enabled' => env('ACTIVITY_PUB', false),
|
'enabled' => env('ACTIVITY_PUB', false),
|
||||||
'outbox' => env('AP_OUTBOX', true),
|
'outbox' => env('AP_OUTBOX', true),
|
||||||
|
@ -41,4 +40,6 @@ return [
|
||||||
'enabled' => env('WEBFINGER', true)
|
'enabled' => env('WEBFINGER', true)
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'network_timeline' => env('PF_NETWORK_TIMELINE', false)
|
||||||
|
|
||||||
];
|
];
|
BIN
public/js/mode-dot.js
vendored
BIN
public/js/mode-dot.js
vendored
Binary file not shown.
BIN
public/js/network-timeline.js
vendored
Normal file
BIN
public/js/network-timeline.js
vendored
Normal file
Binary file not shown.
BIN
public/js/profile-directory.js
vendored
BIN
public/js/profile-directory.js
vendored
Binary file not shown.
BIN
public/js/profile.js
vendored
BIN
public/js/profile.js
vendored
Binary file not shown.
BIN
public/js/quill.js
vendored
BIN
public/js/quill.js
vendored
Binary file not shown.
BIN
public/js/rempos.js
vendored
BIN
public/js/rempos.js
vendored
Binary file not shown.
BIN
public/js/rempro.js
vendored
BIN
public/js/rempro.js
vendored
Binary file not shown.
BIN
public/js/search.js
vendored
BIN
public/js/search.js
vendored
Binary file not shown.
BIN
public/js/status.js
vendored
BIN
public/js/status.js
vendored
Binary file not shown.
BIN
public/js/story-compose.js
vendored
BIN
public/js/story-compose.js
vendored
Binary file not shown.
BIN
public/js/theme-monokai.js
vendored
BIN
public/js/theme-monokai.js
vendored
Binary file not shown.
BIN
public/js/timeline.js
vendored
BIN
public/js/timeline.js
vendored
Binary file not shown.
BIN
public/js/vendor.js
vendored
BIN
public/js/vendor.js
vendored
Binary file not shown.
Binary file not shown.
2197
resources/assets/js/components/NetworkTimeline.vue
Normal file
2197
resources/assets/js/components/NetworkTimeline.vue
Normal file
File diff suppressed because it is too large
Load diff
49
resources/assets/js/network-timeline.js
vendored
Normal file
49
resources/assets/js/network-timeline.js
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
Vue.component(
|
||||||
|
'notification-card',
|
||||||
|
require('./components/NotificationCard.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'photo-presenter',
|
||||||
|
require('./components/presenter/PhotoPresenter.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'video-presenter',
|
||||||
|
require('./components/presenter/VideoPresenter.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'photo-album-presenter',
|
||||||
|
require('./components/presenter/PhotoAlbumPresenter.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'video-album-presenter',
|
||||||
|
require('./components/presenter/VideoAlbumPresenter.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'mixed-album-presenter',
|
||||||
|
require('./components/presenter/MixedAlbumPresenter.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'post-menu',
|
||||||
|
require('./components/PostMenu.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'network-timeline',
|
||||||
|
require('./components/NetworkTimeline.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'announcements-card',
|
||||||
|
require('./components/AnnouncementsCard.vue').default
|
||||||
|
);
|
||||||
|
|
||||||
|
Vue.component(
|
||||||
|
'story-component',
|
||||||
|
require('./components/StoryTimelineComponent.vue').default
|
||||||
|
);
|
|
@ -64,14 +64,30 @@
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
|
||||||
<a class="dropdown-item font-weight-bold" href="{{route('discover')}}">
|
@if(config('federation.network_timeline'))
|
||||||
<span class="far fa-compass pr-2 text-lighter"></span>
|
<a class="dropdown-item font-weight-bold" href="{{route('timeline.public')}}">
|
||||||
{{__('navmenu.discover')}}
|
<span class="fas fa-stream pr-2 text-lighter"></span>
|
||||||
|
Public
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item font-weight-bold" href="{{route('timeline.network')}}">
|
||||||
|
<span class="fas fa-globe pr-2 text-lighter"></span>
|
||||||
|
Network
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<a class="dropdown-item font-weight-bold" href="/">
|
||||||
|
<span class="fas fa-home pr-2 text-lighter"></span>
|
||||||
|
Home
|
||||||
</a>
|
</a>
|
||||||
<a class="dropdown-item font-weight-bold" href="{{route('timeline.public')}}">
|
<a class="dropdown-item font-weight-bold" href="{{route('timeline.public')}}">
|
||||||
<span class="fas fa-stream pr-2 text-lighter"></span>
|
<span class="fas fa-stream pr-2 text-lighter"></span>
|
||||||
Public
|
Public
|
||||||
</a>
|
</a>
|
||||||
|
@endif
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item font-weight-bold" href="{{route('discover')}}">
|
||||||
|
<span class="far fa-compass pr-2 text-lighter"></span>
|
||||||
|
{{__('navmenu.discover')}}
|
||||||
|
</a>
|
||||||
<a class="dropdown-item font-weight-bold" href="/i/stories/new">
|
<a class="dropdown-item font-weight-bold" href="/i/stories/new">
|
||||||
<span class="fas fa-history text-lighter pr-2"></span>
|
<span class="fas fa-history text-lighter pr-2"></span>
|
||||||
Stories
|
Stories
|
||||||
|
|
|
@ -7,31 +7,31 @@
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="lead">Timelines are chronological feeds of posts.</p>
|
<p class="lead">Timelines are chronological feeds of posts.</p>
|
||||||
<p class="font-weight-bold h5 py-3">Pixelfed has 2 different timelines:</p>
|
{{-- <p class="font-weight-bold h5 py-3">Pixelfed has 3 different timelines:</p> --}}
|
||||||
|
|
||||||
<ul>
|
<ul class="list-unstyled">
|
||||||
<li class="lead">
|
<li class="lead mb-2">
|
||||||
<span class="font-weight-bold"><i class="fas fa-home text-muted mr-2"></i> Personal</span>
|
<span class="font-weight-bold"><i class="fas fa-home mr-2"></i> Home</span>
|
||||||
<span class="px-2">—</span>
|
<span class="px-2">—</span>
|
||||||
<span class="font-weight-light">Timeline with posts from accounts you follow</span>
|
<span class="font-weight-light">Timeline with content from accounts you follow</span>
|
||||||
|
</li>
|
||||||
|
<li class="lead mb-2">
|
||||||
|
<span class="font-weight-bold"><i class="fas fa-stream mr-2"></i> Public</span>
|
||||||
|
<span class="px-2">—</span>
|
||||||
|
<span class="font-weight-light">Timeline with content from other users on this server</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="lead">
|
<li class="lead">
|
||||||
<span class="font-weight-bold"><i class="far fa-map text-muted mr-2"></i> Public</span>
|
<span class="font-weight-bold"><i class="fas fa-globe mr-2"></i> Network</span>
|
||||||
<span class="px-2">—</span>
|
<span class="px-2">—</span>
|
||||||
<span class="font-weight-light">Timeline with posts from other users on the same instance</span>
|
<span class="font-weight-light">Timeline with unmoderated content from other servers</span>
|
||||||
</li>
|
</li>
|
||||||
{{-- <li class="lead text-muted">
|
|
||||||
<span class="font-weight-bold"><i class="fas fa-globe text-muted mr-2"></i> Network</span>
|
|
||||||
<span class="px-2">—</span>
|
|
||||||
<span class="font-weight-light text-muted">Timeline with posts from local and remote accounts - coming soon!</span>
|
|
||||||
</li> --}}
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="py-3"></div>
|
<div class="py-3"></div>
|
||||||
<div class="card bg-primary border-primary" style="box-shadow: none !important;border: 3px solid #08d!important;">
|
<div class="card bg-primary border-primary" style="box-shadow: none !important;border: 3px solid #08d!important;">
|
||||||
<div class="card-header text-light font-weight-bold h4 p-4 bg-primary">Timeline Tips</div>
|
<div class="card-header text-light font-weight-bold h4 p-4 bg-primary">Timeline Tips</div>
|
||||||
<div class="card-body bg-white p-3">
|
<div class="card-body bg-white p-3">
|
||||||
<ul class="pt-3">
|
<ul class="pt-3">
|
||||||
<li class="lead mb-4">You can mute or block accounts to prevent them from appearing in timelines.</li>
|
<li class="lead mb-4">You can mute or block accounts to prevent them from appearing in home and public timelines.</li>
|
||||||
<li class="lead mb-4">You can create <span class="font-weight-bold">Unlisted</span> posts that don't appear in public timelines.</li>
|
<li class="lead mb-4">You can create <span class="font-weight-bold">Unlisted</span> posts that don't appear in public timelines.</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -2,16 +2,12 @@
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<timeline scope="network"></timeline>
|
<network-timeline></network-timeline>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script type="text/javascript" src="{{ mix('js/timeline.js') }}"></script>
|
<script type="text/javascript" src="{{ mix('js/network-timeline.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
|
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">window.App.boot();</script>
|
||||||
new Vue({
|
|
||||||
el: '#content'
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endpush
|
@endpush
|
|
@ -167,6 +167,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
||||||
Route::get('notifications', 'ApiController@notifications');
|
Route::get('notifications', 'ApiController@notifications');
|
||||||
Route::get('timelines/public', 'PublicApiController@publicTimelineApi');
|
Route::get('timelines/public', 'PublicApiController@publicTimelineApi');
|
||||||
Route::get('timelines/home', 'PublicApiController@homeTimelineApi');
|
Route::get('timelines/home', 'PublicApiController@homeTimelineApi');
|
||||||
|
Route::get('timelines/network', 'PublicApiController@networkTimelineApi');
|
||||||
Route::get('newsroom/timeline', 'NewsroomController@timelineApi');
|
Route::get('newsroom/timeline', 'NewsroomController@timelineApi');
|
||||||
Route::post('newsroom/markasread', 'NewsroomController@markAsRead');
|
Route::post('newsroom/markasread', 'NewsroomController@markAsRead');
|
||||||
Route::get('favourites', 'Api\BaseApiController@accountLikes');
|
Route::get('favourites', 'Api\BaseApiController@accountLikes');
|
||||||
|
@ -468,6 +469,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
||||||
Route::group(['prefix' => 'timeline'], function () {
|
Route::group(['prefix' => 'timeline'], function () {
|
||||||
Route::redirect('/', '/');
|
Route::redirect('/', '/');
|
||||||
Route::get('public', 'TimelineController@local')->name('timeline.public');
|
Route::get('public', 'TimelineController@local')->name('timeline.public');
|
||||||
|
Route::get('network', 'TimelineController@network')->name('timeline.network');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group(['prefix' => 'users'], function () {
|
Route::group(['prefix' => 'users'], function () {
|
||||||
|
|
21
webpack.mix.js
vendored
21
webpack.mix.js
vendored
|
@ -1,17 +1,9 @@
|
||||||
let mix = require('laravel-mix');
|
let mix = require('laravel-mix');
|
||||||
|
|
||||||
mix.sass('resources/assets/sass/app.scss', 'public/css', {
|
mix.sass('resources/assets/sass/app.scss', 'public/css')
|
||||||
implementation: require('node-sass')
|
.sass('resources/assets/sass/appdark.scss', 'public/css')
|
||||||
})
|
.sass('resources/assets/sass/landing.scss', 'public/css')
|
||||||
.sass('resources/assets/sass/appdark.scss', 'public/css', {
|
.sass('resources/assets/sass/quill.scss', 'public/css').version();
|
||||||
implementation: require('node-sass')
|
|
||||||
})
|
|
||||||
.sass('resources/assets/sass/landing.scss', 'public/css', {
|
|
||||||
implementation: require('node-sass')
|
|
||||||
})
|
|
||||||
.sass('resources/assets/sass/quill.scss', 'public/css', {
|
|
||||||
implementation: require('node-sass')
|
|
||||||
}).version();
|
|
||||||
|
|
||||||
mix.js('resources/assets/js/app.js', 'public/js')
|
mix.js('resources/assets/js/app.js', 'public/js')
|
||||||
.js('resources/assets/js/activity.js', 'public/js')
|
.js('resources/assets/js/activity.js', 'public/js')
|
||||||
|
@ -41,6 +33,11 @@ mix.js('resources/assets/js/app.js', 'public/js')
|
||||||
.js('resources/assets/js/rempro.js', 'public/js')
|
.js('resources/assets/js/rempro.js', 'public/js')
|
||||||
.js('resources/assets/js/rempos.js', 'public/js')
|
.js('resources/assets/js/rempos.js', 'public/js')
|
||||||
//.js('resources/assets/js/timeline_next.js', 'public/js')
|
//.js('resources/assets/js/timeline_next.js', 'public/js')
|
||||||
|
// .js('resources/assets/js/memoryprofile.js', 'public/js')
|
||||||
|
// .js('resources/assets/js/my2020.js', 'public/js')
|
||||||
|
.js('resources/assets/js/network-timeline.js', 'public/js')
|
||||||
|
// .js('resources/assets/js/drive.js', 'public/js')
|
||||||
|
// .js('resources/assets/js/register.js', 'public/js')
|
||||||
|
|
||||||
.extract([
|
.extract([
|
||||||
'lodash',
|
'lodash',
|
||||||
|
|
Loading…
Reference in a new issue