Merge pull request #2220 from pixelfed/staging

Update to Laravel 7.0
This commit is contained in:
daniel 2020-06-16 15:31:51 -06:00 committed by GitHub
commit e6984451dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1819 additions and 2487 deletions

View file

@ -8,6 +8,7 @@
- Added Bookmarks to v1 api ([99cb48c5](https://github.com/pixelfed/pixelfed/commit/99cb48c5)) - Added Bookmarks to v1 api ([99cb48c5](https://github.com/pixelfed/pixelfed/commit/99cb48c5))
- Added New Post notification to Timeline ([a0e7c4d5](https://github.com/pixelfed/pixelfed/commit/a0e7c4d5)) - Added New Post notification to Timeline ([a0e7c4d5](https://github.com/pixelfed/pixelfed/commit/a0e7c4d5))
- Add Instagram Import ([e2a6bdd0](https://github.com/pixelfed/pixelfed/commit/e2a6bdd0)) - Add Instagram Import ([e2a6bdd0](https://github.com/pixelfed/pixelfed/commit/e2a6bdd0))
- Add notification preview to NotificationCard ([28445e27](https://github.com/pixelfed/pixelfed/commit/28445e27))
### Updated ### Updated
- Updated PostComponent, fix remote urls ([42716ccc](https://github.com/pixelfed/pixelfed/commit/42716ccc)) - Updated PostComponent, fix remote urls ([42716ccc](https://github.com/pixelfed/pixelfed/commit/42716ccc))
@ -43,6 +44,11 @@
- Updated PostComponent, improve embed model. Fixes ([#2189](https://github.com/pixelfed/pixelfed/issues/2189)) ([b12e504e](https://github.com/pixelfed/pixelfed/commit/b12e504e)) - Updated PostComponent, improve embed model. Fixes ([#2189](https://github.com/pixelfed/pixelfed/issues/2189)) ([b12e504e](https://github.com/pixelfed/pixelfed/commit/b12e504e))
- Updated PostComponent, hide edit button after 24 hours. Fixes ([#2188](https://github.com/pixelfed/pixelfed/issues/2188)) ([a1fee6a2](https://github.com/pixelfed/pixelfed/commit/a1fee6a2)) - Updated PostComponent, hide edit button after 24 hours. Fixes ([#2188](https://github.com/pixelfed/pixelfed/issues/2188)) ([a1fee6a2](https://github.com/pixelfed/pixelfed/commit/a1fee6a2))
- Updated AP Inbox, add follow notifications ([b8819fbb](https://github.com/pixelfed/pixelfed/commit/b8819fbb)) - Updated AP Inbox, add follow notifications ([b8819fbb](https://github.com/pixelfed/pixelfed/commit/b8819fbb))
- Updated Api Transformers, fixes ([#2234](https://github.com/pixelfed/pixelfed/issues/2234)) ([63007891](https://github.com/pixelfed/pixelfed/commit/63007891))
- Updated ApiV1Controller, fix instance endpoint ([#2233](https://github.com/pixelfed/pixelfed/issues/2233)) ([b7ee9981](https://github.com/pixelfed/pixelfed/commit/b7ee9981))
- Updated AP Inbox, remove trailing comma ([5c443548](https://github.com/pixelfed/pixelfed/commit/5c443548))
- Updated AP Helpers, update bio + name ([4bee8397](https://github.com/pixelfed/pixelfed/commit/4bee8397))
- Updated Profile component, add bookmark loader ([c8d5edc9](https://github.com/pixelfed/pixelfed/commit/c8d5edc9))
## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9) ## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)

View file

@ -2,8 +2,8 @@
namespace App\Exceptions; namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
@ -33,7 +33,7 @@ class Handler extends ExceptionHandler
* *
* @return void * @return void
*/ */
public function report(Exception $exception) public function report(Throwable $exception)
{ {
parent::report($exception); parent::report($exception);
} }
@ -46,7 +46,7 @@ class Handler extends ExceptionHandler
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function render($request, Exception $exception) public function render($request, Throwable $exception)
{ {
return parent::render($request, $exception); return parent::render($request, $exception);
} }

View file

@ -938,7 +938,7 @@ class ApiV1Controller extends Controller
'description' => 'Pixelfed - Photo sharing for everyone', 'description' => 'Pixelfed - Photo sharing for everyone',
'email' => config('instance.email'), 'email' => config('instance.email'),
'languages' => ['en'], 'languages' => ['en'],
'max_toot_chars' => config('pixelfed.max_caption_length'), 'max_toot_chars' => (int) config('pixelfed.max_caption_length'),
'registrations' => config('pixelfed.open_registration'), 'registrations' => config('pixelfed.open_registration'),
'stats' => [ 'stats' => [
'user_count' => 0, 'user_count' => 0,
@ -951,11 +951,11 @@ class ApiV1Controller extends Controller
'urls' => [], 'urls' => [],
'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') . ')', 'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') . ')',
'environment' => [ 'environment' => [
'max_photo_size' => config('pixelfed.max_photo_size'), 'max_photo_size' => (int) config('pixelfed.max_photo_size'),
'max_avatar_size' => config('pixelfed.max_avatar_size'), 'max_avatar_size' => (int) config('pixelfed.max_avatar_size'),
'max_caption_length' => config('pixelfed.max_caption_length'), 'max_caption_length' => (int) config('pixelfed.max_caption_length'),
'max_bio_length' => config('pixelfed.max_bio_length'), 'max_bio_length' => (int) config('pixelfed.max_bio_length'),
'max_album_length' => config('pixelfed.max_album_length'), 'max_album_length' => (int) config('pixelfed.max_album_length'),
'mobile_apis' => config('pixelfed.oauth_enabled') 'mobile_apis' => config('pixelfed.oauth_enabled')
] ]

View file

@ -36,11 +36,12 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class, \App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
// 'restricted',
], ],
'api' => [ 'api' => [
'bindings', 'bindings',
\Barryvdh\Cors\HandleCors::class, \Fruitcake\Cors\HandleCors::class,
], ],
]; ];
@ -65,5 +66,6 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'twofactor' => \App\Http\Middleware\TwoFactorAuth::class, 'twofactor' => \App\Http\Middleware\TwoFactorAuth::class,
'validemail' => \App\Http\Middleware\EmailVerificationCheck::class, 'validemail' => \App\Http\Middleware\EmailVerificationCheck::class,
// 'restricted' => \App\Http\Middleware\RestrictedAccess::class,
]; ];
} }

View file

@ -27,11 +27,13 @@ class AuthServiceProvider extends ServiceProvider
$this->registerPolicies(); $this->registerPolicies();
if(config('pixelfed.oauth_enabled')) { if(config('pixelfed.oauth_enabled')) {
Passport::routes(null, ['middleware' => ['twofactor', \Barryvdh\Cors\HandleCors::class]]); Passport::routes(null, ['middleware' => ['twofactor', \Fruitcake\Cors\HandleCors::class]]);
Passport::tokensExpireIn(now()->addDays(15)); Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::enableImplicitGrant(); Passport::enableImplicitGrant();
if(config('instance.oauth.pat.enabled')) {
Passport::personalAccessClientId(config('instance.oauth.pat.id'));
}
Passport::setDefaultScope([ Passport::setDefaultScope([
'read', 'read',
'write', 'write',

View file

@ -37,10 +37,10 @@ class NotificationTransformer extends Fractal\TransformerAbstract
if($status) { if($status) {
return $this->item($status, new StatusTransformer()); return $this->item($status, new StatusTransformer());
} else { } else {
return null; return $this->collection([], new StatusTransformer());
} }
} else { } else {
return null; return $this->collection([], new StatusTransformer());
} }
} }

View file

@ -63,6 +63,8 @@ class StatusTransformer extends Fractal\TransformerAbstract
if(in_array($status->type, ['photo', 'video', 'photo:album', 'loop', 'photo:video:album'])) { if(in_array($status->type, ['photo', 'video', 'photo:album', 'loop', 'photo:video:album'])) {
$media = $status->media()->orderBy('order')->get(); $media = $status->media()->orderBy('order')->get();
return $this->collection($media, new MediaTransformer()); return $this->collection($media, new MediaTransformer());
} else {
return $this->collection([], new MediaTransformer());
} }
}); });
} }

View file

@ -131,6 +131,10 @@ class Helpers {
public static function validateUrl($url) public static function validateUrl($url)
{ {
if(is_array($url)) {
$url = $url[0];
}
$localhosts = [ $localhosts = [
'127.0.0.1', 'localhost', '::1' '127.0.0.1', 'localhost', '::1'
]; ];
@ -433,6 +437,16 @@ class Helpers {
// RemoteFollowImportRecent::dispatch($res, $profile); // RemoteFollowImportRecent::dispatch($res, $profile);
CreateAvatar::dispatch($profile); CreateAvatar::dispatch($profile);
} }
} else {
// Update info after 24 hours
if($profile->last_fetched_at == null ||
$profile->last_fetched_at->lt(now()->subHours(24)) == true
) {
$profile->name = isset($res['name']) ? Purify::clean($res['name']) : 'user';
$profile->bio = isset($res['summary']) ? Purify::clean($res['summary']) : null;
$profile->last_fetched_at = now();
$profile->save();
}
} }
return $profile; return $profile;
} }

View file

@ -299,7 +299,7 @@ class Inbox
{ {
if(!isset( if(!isset(
$this->payload['actor'], $this->payload['actor'],
$this->payload['object'], $this->payload['object']
)) { )) {
return; return;
} }

View file

@ -452,6 +452,9 @@ class Extractor extends Regex
$start_position = $at[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $at[1])) : $at[1]; $start_position = $at[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $at[1])) : $at[1];
$end_position = $start_position + StringUtils::strlen($at[0]) + StringUtils::strlen($username[0]); $end_position = $start_position + StringUtils::strlen($at[0]) + StringUtils::strlen($username[0]);
$screenname = trim($all[0]) == '@'.$username[0] ? $username[0] : trim($all[0]); $screenname = trim($all[0]) == '@'.$username[0] ? $username[0] : trim($all[0]);
if(config('app.env') == 'production' && \App\Profile::whereUsername($screenname)->exists() == false) {
continue;
}
$entity = [ $entity = [
'screen_name' => $screenname, 'screen_name' => $screenname,
'list_slug' => $list_slug[0], 'list_slug' => $list_slug[0],

View file

@ -13,22 +13,23 @@
"ext-json": "*", "ext-json": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-openssl": "*", "ext-openssl": "*",
"barryvdh/laravel-cors": "^0.11.4",
"beyondcode/laravel-self-diagnosis": "^1.0.2", "beyondcode/laravel-self-diagnosis": "^1.0.2",
"brick/math": "^0.8",
"doctrine/dbal": "^2.7", "doctrine/dbal": "^2.7",
"fideloper/proxy": "^4.0", "fideloper/proxy": "^4.0",
"fruitcake/laravel-cors": "^2.0",
"intervention/image": "^2.4", "intervention/image": "^2.4",
"jenssegers/agent": "^2.6", "jenssegers/agent": "^2.6",
"laravel/framework": "^6.0", "laravel/framework": "^7.0",
"laravel/helpers": "^1.1", "laravel/helpers": "^1.1",
"laravel/horizon": "^3.3", "laravel/horizon": "^4.0",
"laravel/passport": "^7.0", "laravel/passport": "^7.0",
"laravel/tinker": "^1.0", "laravel/tinker": "^2.0",
"laravel/ui": "^2.0",
"league/flysystem-aws-s3-v3": "~1.0", "league/flysystem-aws-s3-v3": "~1.0",
"league/flysystem-cached-adapter": "~1.0", "league/flysystem-cached-adapter": "~1.0",
"league/iso3166": "^2.1", "league/iso3166": "^2.1",
"brick/math": "^0.8", "pbmedia/laravel-ffmpeg": "^7.0",
"pbmedia/laravel-ffmpeg": "5.0.*",
"phpseclib/phpseclib": "~2.0", "phpseclib/phpseclib": "~2.0",
"pixelfed/bacon-qr-code": "^3.0", "pixelfed/bacon-qr-code": "^3.0",
"pixelfed/fractal": "^0.18.0", "pixelfed/fractal": "^0.18.0",
@ -41,13 +42,11 @@
"stevebauman/purify": "3.0.*" "stevebauman/purify": "3.0.*"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "dev-master", "facade/ignition": "^2.0",
"facade/ignition": "^1.4",
"fzaninotto/faker": "^1.4", "fzaninotto/faker": "^1.4",
"mockery/mockery": "^1.0", "mockery/mockery": "^1.0",
"nunomaduro/collision": "^3.0", "nunomaduro/collision": "^4.1",
"nunomaduro/phpinsights": "^1.9", "phpunit/phpunit": "^8.5"
"phpunit/phpunit": "^8.0"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [

4058
composer.lock generated

File diff suppressed because it is too large Load diff

60
config/cors.php Normal file
View file

@ -0,0 +1,60 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Laravel CORS Options
|--------------------------------------------------------------------------
|
| The allowed_methods and allowed_headers options are case-insensitive.
|
| You don't need to provide both allowed_origins and allowed_origins_patterns.
| If one of the strings passed matches, it is considered a valid origin.
|
| If array('*') is provided to allowed_methods, allowed_origins or allowed_headers
| all methods / origins / headers are allowed.
|
*/
/*
* You can enable CORS for 1 or multiple paths.
* Example: ['api/*']
*/
'paths' => [],
/*
* Matches the request method. `[*]` allows all methods.
*/
'allowed_methods' => ['*'],
/*
* Matches the request origin. `[*]` allows all origins. Wildcards can be used, eg `*.mydomain.com`
*/
'allowed_origins' => ['*'],
/*
* Patterns that can be used with `preg_match` to match the origin.
*/
'allowed_origins_patterns' => [],
/*
* Sets the Access-Control-Allow-Headers response header. `[*]` allows all headers.
*/
'allowed_headers' => ['*'],
/*
* Sets the Access-Control-Expose-Headers response header with these headers.
*/
'exposed_headers' => [],
/*
* Sets the Access-Control-Max-Age response header when > 0.
*/
'max_age' => 0,
/*
* Sets the Access-Control-Allow-Credentials header.
*/
'supports_credentials' => false,
];

BIN
public/js/profile.js vendored

Binary file not shown.

BIN
public/js/timeline.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -20,7 +20,16 @@
<div class="media-body font-weight-light small"> <div class="media-body font-weight-light small">
<div v-if="n.type == 'favourite'"> <div v-if="n.type == 'favourite'">
<p class="my-0"> <p class="my-0">
<a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> liked your <a class="font-weight-bold" v-bind:href="n.status.url">post</a>. <a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> liked your
<span v-if="n.status.hasOwnProperty('media_attachments')">
<a class="font-weight-bold" v-bind:href="n.status.url" :id="'fvn-' + n.id">post</a>.
<b-popover :target="'fvn-' + n.id" title="" triggers="hover" placement="top" boundary="window">
<img :src="notificationPreview(n)" width="100px" height="100px">
</b-popover>
</span>
<span v-else>
<a class="font-weight-bold" v-bind:href="n.status.url">post</a>.
</span>
</p> </p>
</div> </div>
<div v-else-if="n.type == 'comment'"> <div v-else-if="n.type == 'comment'">
@ -219,6 +228,13 @@
redirect(url) { redirect(url) {
window.location.href = url; window.location.href = url;
},
notificationPreview(n) {
if(!n.status.hasOwnProperty('media_attachments') || !n.status.media_attachments.length) {
return '/storage/no-preview.png';
}
return n.status.media_attachments[0].preview_url;
} }
} }
} }

View file

@ -210,35 +210,46 @@
</infinite-loading> </infinite-loading>
</div> </div>
<div v-if="mode == 'bookmarks'"> <div v-if="mode == 'bookmarks'">
<div v-if="bookmarks.length" class="row"> <div v-if="bookmarksLoading">
<div class="col-4 p-1 p-sm-2 p-md-3" v-for="(s, index) in bookmarks"> <div class="row">
<a class="card info-overlay card-md-border-0" :href="s.url"> <div class="col-12">
<div class="square"> <div class="p-1 p-sm-2 p-md-3 d-flex justify-content-center align-items-center" style="height: 30vh;">
<span v-if="s.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span> <img src="/img/pixelfed-icon-grey.svg" class="">
<span v-if="s.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
<span v-if="s.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
<div class="square-content" v-bind:style="previewBackground(s)">
</div>
<div class="info-overlay-text">
<h5 class="text-white m-auto font-weight-bold">
<span>
<span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{s.favourites_count}}</span>
</span>
<span>
<span class="fas fa-retweet fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{s.reblogs_count}}</span>
</span>
</h5>
</div>
</div> </div>
</a> </div>
</div> </div>
</div> </div>
<div v-else class="col-12"> <div v-else>
<div class="py-5 text-center text-muted"> <div v-if="bookmarks.length" class="row">
<p><i class="fas fa-bookmark fa-2x"></i></p> <div class="col-4 p-1 p-sm-2 p-md-3" v-for="(s, index) in bookmarks">
<p class="h2 font-weight-light pt-3">No saved bookmarks</p> <a class="card info-overlay card-md-border-0" :href="s.url">
<div class="square">
<span v-if="s.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span>
<span v-if="s.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
<span v-if="s.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
<div class="square-content" v-bind:style="previewBackground(s)">
</div>
<div class="info-overlay-text">
<h5 class="text-white m-auto font-weight-bold">
<span>
<span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{s.favourites_count}}</span>
</span>
<span>
<span class="fas fa-retweet fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{s.reblogs_count}}</span>
</span>
</h5>
</div>
</div>
</a>
</div>
</div>
<div v-else class="col-12">
<div class="py-5 text-center text-muted">
<p><i class="fas fa-bookmark fa-2x"></i></p>
<p class="h2 font-weight-light pt-3">No saved bookmarks</p>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -641,6 +652,7 @@
followingModalSearch: null, followingModalSearch: null,
followingModalSearchCache: null, followingModalSearchCache: null,
followingModalTab: 'following', followingModalTab: 'following',
bookmarksLoading: true,
} }
}, },
beforeMount() { beforeMount() {
@ -789,7 +801,8 @@
if(this.mode == 'bookmarks' && this.bookmarks.length == 0) { if(this.mode == 'bookmarks' && this.bookmarks.length == 0) {
axios.get('/api/local/bookmarks') axios.get('/api/local/bookmarks')
.then(res => { .then(res => {
this.bookmarks = res.data this.bookmarks = res.data;
this.bookmarksLoading = false;
}); });
} }
if(this.mode == 'collections' && this.collections.length == 0) { if(this.mode == 'collections' && this.collections.length == 0) {

View file

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<div v-if="stories.length != 0"> <div v-if="show" class="card card-body p-0 border mt-4 mb-3 shadow-none">
<div id="storyContainer" :class="[list == true ? 'mt-1 mr-3 mb-0 ml-1':'m-3']"></div> <div id="storyContainer" :class="[list == true ? 'mt-1 mr-3 mb-0 ml-1':'mx-3 mt-3 mb-0 pb-0']"></div>
</div> </div>
</div> </div>
</template> </template>
@ -21,6 +21,7 @@
props: ['list'], props: ['list'],
data() { data() {
return { return {
show: false,
stories: {}, stories: {},
} }
}, },
@ -34,6 +35,10 @@
axios.get('/api/stories/v0/recent') axios.get('/api/stories/v0/recent')
.then(res => { .then(res => {
let data = res.data; let data = res.data;
if(!res.data.length) {
this.show = false;
return;
}
let stories = new Zuck('storyContainer', { let stories = new Zuck('storyContainer', {
list: this.list == true ? true : false, list: this.list == true ? true : false,
stories: data, stories: data,
@ -70,6 +75,7 @@
}); });
}); });
}); });
this.show = true;
} }
} }
} }
@ -79,17 +85,17 @@
#storyContainer .story { #storyContainer .story {
margin-right: 2rem; margin-right: 2rem;
width: 100%; width: 100%;
max-width: 64px; max-width: 60px;
} }
.stories.carousel .story > .item-link > .item-preview { .stories.carousel .story > .item-link > .item-preview {
height: 64px; height: 60px;
} }
#zuck-modal.with-effects { #zuck-modal.with-effects {
width: 100%; width: 100%;
} }
.stories.carousel .story > .item-link > .info .name { .stories.carousel .story > .item-link > .info .name {
font-weight: 600; font-weight: 500;
font-size: 12px; font-size: 11px;
} }
.stories.carousel .story > .item-link > .info { .stories.carousel .story > .item-link > .info {
} }

View file

@ -7,7 +7,7 @@
</p> </p>
</div> </div>
<div :class="[modes.distractionFree ? 'col-md-8 col-lg-8 offset-md-2 px-0 mb-sm-3 timeline order-2 order-md-1':'col-md-8 col-lg-8 px-0 mb-sm-3 timeline order-2 order-md-1']"> <div :class="[modes.distractionFree ? 'col-md-8 col-lg-8 offset-md-2 px-0 mb-sm-3 timeline order-2 order-md-1':'col-md-8 col-lg-8 px-0 mb-sm-3 timeline order-2 order-md-1']">
<div v-if="config.features.stories"> <div style="margin-top:32px;">
<story-component v-if="config.features.stories"></story-component> <story-component v-if="config.features.stories"></story-component>
</div> </div>
<div> <div>
@ -1533,8 +1533,9 @@
} }
}).then(res => { }).then(res => {
let self = this; let self = this;
let tids = this.feed.map(status => status.id);
let data = res.data.filter(d => { let data = res.data.filter(d => {
return d.id > self.min_id return d.id > self.min_id && tids.indexOf(d.id) == -1;
}); });
let ids = data.map(status => status.id); let ids = data.map(status => status.id);
let max = Math.max(...ids).toString(); let max = Math.max(...ids).toString();