Compare commits

...

13 commits

Author SHA1 Message Date
mrilyew
9bef130eba
Merge a4d5bb8088 into 47a61d19d7 2024-11-03 18:20:23 +03:00
veselcraft
47a61d19d7
fix(al_feed): fix selectors for feed settings 2024-11-03 18:15:13 +03:00
veselcraft
4040a303ae
fix(feed_settings): it didn't work lol 2024-11-03 17:55:20 +03:00
507bbfaba5
fix(sqls): add NOT NULL to id in 00049-ignored-sources.sql 2024-11-03 17:52:09 +03:00
veselcraft
f4c0805a6a
fix(feed_settings): make settings layout better again 2024-11-03 17:39:37 +03:00
76dedd6b82
build(js): remove yarn support, and update lock-file (#1142)
* build(js): remove yarn.lock

Yarn had changed its methods of packaging long ago and it hasn't been supported ever since. It's much better and easier to just use the original NPM.

* fix(docker): change yarn to npm command

* doc(README): change yarn mentions to npm

* fix(docker): use npm ci instead of npm install

* build(package-lock): update js dependencies

* build(docker): use node:20 image when building
2024-11-03 17:37:35 +03:00
Vladimir Barinov
8f7a98c59e
fix(feed_settings): make settings layout better 2024-11-03 17:21:22 +03:00
veselcraft
299dbd0755
feat(friends): forgot to upd func on mini menu 2024-11-03 17:18:16 +03:00
mrilyew
a60c990838
feat(users): ignore users and groups (#987)
* rewrite

* Update install/sqls/00049-ignored-sources.sql

Co-authored-by: Alexander Minkin <weryskok@gmail.com>

---------

Co-authored-by: Alexander Minkin <weryskok@gmail.com>
2024-11-03 17:17:34 +03:00
mrilyew
a4d5bb8088 Merge branch 'master' into infinityscroll2 2024-11-02 14:16:42 +03:00
mrilyew
c4fec2bf97 rework to up button 2024-11-02 13:02:34 +03:00
mrilyew
389f0b4bb4 allow comments scroll 2024-11-02 12:13:17 +03:00
mrilyew
8fc47ff6cd rewrite 2024-11-02 11:16:25 +03:00
47 changed files with 1367 additions and 964 deletions

View file

@ -36,7 +36,7 @@ Here is our minimum hardware recommendation:
### Installation procedure
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
1. Install PHP 7.4, web-server, Composer, Node.js, NPM and [Chandler](https://github.com/openvk/chandler)
* PHP 8 is still being tested; the functionality of the engine on this version of PHP is not yet guaranteed.
@ -65,7 +65,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
7. Copy `openvk-example.yml` to `openvk.yml` and change options to your liking
8. Run `composer install` in OpenVK directory
9. Run `composer install` in commitcaptcha directory
10. Move to `Web/static/js` and execute `yarn install`
10. Move to `Web/static/js` and execute `npm install`
11. Set `openvk` as your root app in `chandler.yml`
Once you are done, you can login as a system administrator on the network itself (no registration required):

View file

@ -28,7 +28,7 @@ _[English](README.md)_
### Процедура установки
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, NPM и [Chandler](https://github.com/openvk/chandler)
* PHP 8 пока ещё тестируется, работоспособность движка на этой версии PHP пока не гарантируется.
@ -57,7 +57,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
7. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры под свои нужды
8. Запустите `composer install` в директории OpenVK
9. Запустите `composer install` в директории commitcaptcha
10. Перейдите в `Web/static/js` и выполните `yarn install`
10. Перейдите в `Web/static/js` и выполните `npm install`
11. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется):

View file

@ -32,6 +32,7 @@ final class Newsfeed extends VKAPIRequestHandler
->select("id")
->where("wall IN (?)", $ids)
->where("deleted", 0)
->where("suggested", 0)
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
->where("? <= created", empty($start_time) ? 0 : $start_time)
->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time)
@ -57,6 +58,15 @@ final class Newsfeed extends VKAPIRequestHandler
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0";
if($return_banned == 0) {
$ignored_sources_ids = $this->getUser()->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
$start_time = empty($start_time) ? 0 : $start_time;
$end_time = empty($end_time) ? PHP_INT_MAX : $end_time;
@ -74,4 +84,152 @@ final class Newsfeed extends VKAPIRequestHandler
return $response;
}
function getByType(string $feed_type = 'top', string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $return_banned = 0)
{
$this->requireUser();
switch($feed_type) {
case 'top':
return $this->getGlobal($fields, $start_from, $start_time, $end_time, $offset, $count, $extended, $return_banned);
break;
default:
return $this->get($fields, $start_from, $start_time, $end_time, $offset, $count, $extended);
break;
}
}
function getBanned(int $extended = 0, string $fields = "", string $name_case = "nom", int $merge = 0): object
{
$this->requireUser();
$offset = 0;
$count = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$banned = $this->getUser()->getIgnoredSources($offset, $count, ($extended != 1));
$return_object = (object) [
'groups' => [],
'members' => [],
];
if($extended == 0) {
foreach($banned as $ban) {
if($ban > 0)
$return_object->members[] = $ban;
else
$return_object->groups[] = $ban;
}
} else {
if($merge == 1) {
$return_object = (object) [
'count' => sizeof($banned),
'items' => [],
];
foreach($banned as $ban) {
$return_object->items[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
} else {
$return_object = (object) [
'groups' => [],
'profiles' => [],
];
foreach($banned as $ban) {
if($ban->getRealId() > 0)
$return_object->profiles[] = $ban->toVkApiStruct($this->getUser(), $fields);
else
$return_object->groups[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
}
}
return $return_object;
}
function addBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
# Formatting input ids
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of 'ids' is 10");
$config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$user_ignores = $this->getUser()->getIgnoredSourcesCount();
if(($user_ignores + sizeof($ids)) > $config_limit) {
$this->fail(-50, "Ignoring limit exceeded");
}
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || $entity->isHideFromGlobalFeedEnabled() || $entity->isIgnoredBy($this->getUser())) continue;
$entity->addIgnore($this->getUser());
$successes += 1;
}
return 1;
}
function deleteBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of ids is 10");
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || !$entity->isIgnoredBy($this->getUser())) continue;
$entity->removeIgnore($this->getUser());
$successes += 1;
}
return 1;
}
}

View file

@ -42,10 +42,11 @@ class Club extends RowModel
return iterator_to_array($avPhotos)[0] ?? NULL;
}
function getAvatarUrl(string $size = "miniscule"): string
function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
{
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
$avPhoto = $this->getAvatarPhoto();
if(!$avPhoto)
$avPhoto = $this->getAvatarPhoto();
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
}
@ -443,30 +444,57 @@ class Club extends RowModel
$res->id = $this->getId();
$res->name = $this->getName();
$res->screen_name = $this->getShortCode();
$res->is_closed = 0;
$res->screen_name = $this->getShortCode() ?? "club".$this->getId();
$res->is_closed = false;
$res->type = 'group';
$res->is_member = $user ? (int)$this->getSubscriptionStatus($user) : 0;
$res->deactivated = NULL;
$res->is_admin = $user && $this->canBeModifiedBy($user);
$res->can_access_closed = true;
if($user && $this->canBeModifiedBy($user)) {
$res->admin_level = 3;
if(!is_array($fields))
$fields = explode(',', $fields);
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) {
switch($field) {
case 'verified':
$res->verified = (int)$this->isVerified();
break;
case 'site':
$res->site = $this->getWebsite();
break;
case 'description':
$res->description = $this->getDescription();
break;
case 'background':
$res->background = $this->getBackDropPictureURLs();
break;
case 'photo_50':
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
break;
case 'photo_100':
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
break;
case 'photo_200':
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
break;
case 'photo_max':
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
break;
case 'members_count':
$res->members_count = $this->getFollowersCount();
break;
case 'real_id':
$res->real_id = $this->getRealId();
break;
}
}
$res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0;
$res->type = "group";
$res->photo_50 = $this->getAvatarUrl("miniscule");
$res->photo_100 = $this->getAvatarUrl("tiny");
$res->photo_200 = $this->getAvatarUrl("normal");
$res->can_create_topic = $user && $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
$res->can_post = $user && $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
return $res;
}
use Traits\TBackDrops;
use Traits\TSubscribable;
use Traits\TAudioStatuses;
use Traits\TIgnorable;
}

View file

@ -0,0 +1,52 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Entities\User;
trait TIgnorable
{
function isIgnoredBy(User $user): bool
{
$ctx = DatabaseConnection::i()->getContext();
$data = [
"owner" => $user->getId(),
"source" => $this->getRealId(),
];
$sub = $ctx->table("ignored_sources")->where($data);
return $sub->count() > 0;
}
function addIgnore(User $for_user): bool
{
DatabaseConnection::i()->getContext()->table("ignored_sources")->insert([
"owner" => $for_user->getId(),
"source" => $this->getRealId(),
]);
return true;
}
function removeIgnore(User $for_user): bool
{
DatabaseConnection::i()->getContext()->table("ignored_sources")->where([
"owner" => $for_user->getId(),
"source" => $this->getRealId(),
])->delete();
return true;
}
function toggleIgnore(User $for_user): bool
{
if($this->isIgnoredBy($for_user)) {
$this->removeIgnore($for_user);
return false;
} else {
$this->addIgnore($for_user);
return true;
}
}
}

View file

@ -112,7 +112,7 @@ class User extends RowModel
return "/id" . $this->getId();
}
function getAvatarUrl(string $size = "miniscule"): string
function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
{
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
@ -121,7 +121,9 @@ class User extends RowModel
else if($this->isBanned())
return "$serverUrl/assets/packages/static/openvk/img/banned.jpg";
$avPhoto = $this->getAvatarPhoto();
if(!$avPhoto)
$avPhoto = $this->getAvatarPhoto();
if(is_null($avPhoto))
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
else
@ -1344,11 +1346,6 @@ class User extends RowModel
$res->first_name = $this->getFirstName();
$res->last_name = $this->getLastName();
$res->deactivated = $this->isDeactivated();
$res->photo_50 = $this->getAvatarURL();
$res->photo_100 = $this->getAvatarURL("tiny");
$res->photo_200 = $this->getAvatarURL("normal");
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
$res->is_closed = $this->isClosed();
if(!is_null($user))
@ -1357,10 +1354,53 @@ class User extends RowModel
if(!is_array($fields))
$fields = explode(',', $fields);
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) {
switch($field) {
case 'is_dead':
$res->is_dead = $user->isDead();
$res->is_dead = $this->isDead();
break;
case 'verified':
$res->verified = (int)$this->isVerified();
break;
case 'sex':
$res->sex = $this->isFemale() ? 1 : ($this->isNeutral() ? 0 : 2);
break;
case 'photo_50':
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
break;
case 'photo_100':
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
break;
case 'photo_200':
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
break;
case 'photo_max':
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
break;
case 'photo_id':
$res->photo_id = $avatar_photo ? $avatar_photo->getPrettyId() : NULL;
break;
case 'background':
$res->background = $this->getBackDropPictureURLs();
break;
case 'reg_date':
$res->reg_date = $this->getRegistrationTime()->timestamp();
break;
case 'nickname':
$res->nickname = $this->getPseudo();
break;
case 'rating':
$res->rating = $this->getRating();
break;
case 'status':
$res->status = $this->getStatus();
break;
case 'screen_name':
$res->screen_name = $this->getShortCode() ?? "id".$this->getId();
break;
case 'real_id':
$res->real_id = $this->getRealId();
break;
}
}
@ -1407,7 +1447,40 @@ class User extends RowModel
return $returnArr;
}
function getIgnoredSources(int $offset = 0, int $limit = 10, bool $onlyIds = false)
{
$sources = DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->limit($limit, $offset)->order('id DESC');
$output_array = [];
foreach($sources as $source) {
if($onlyIds) {
$output_array[] = (int)$source->source;
} else {
$ignored_source_model = NULL;
$ignored_source_id = (int)$source->source;
if($ignored_source_id > 0)
$ignored_source_model = (new Users)->get($ignored_source_id);
else
$ignored_source_model = (new Clubs)->get(abs($ignored_source_id));
if(!$ignored_source_model)
continue;
$output_array[] = $ignored_source_model;
}
}
return $output_array;
}
function getIgnoredSourcesCount()
{
return DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->count();
}
use Traits\TBackDrops;
use Traits\TSubscribable;
use Traits\TAudioStatuses;
use Traits\TIgnorable;
}

View file

@ -43,6 +43,18 @@ class Clubs
return $this->toClub($this->clubs->get($id));
}
function getByIds(array $ids = []): array
{
$clubs = $this->clubs->select('*')->where('id IN (?)', $ids);
$clubs_array = [];
foreach($clubs as $club) {
$clubs_array[] = $this->toClub($club);
}
return $clubs_array;
}
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable
{
$query = "%$query%";

View file

@ -29,6 +29,18 @@ class Users
return $this->toUser($this->users->get($id));
}
function getByIds(array $ids = []): array
{
$users = $this->users->select('*')->where('id IN (?)', $ids);
$users_array = [];
foreach($users as $user) {
$users_array[] = $this->toUser($user);
}
return $users_array;
}
function getByShortURL(string $url): ?User
{
$shortcode = $this->toUser($this->users->where("shortcode", $url)->fetch());

View file

@ -43,6 +43,7 @@ final class GroupPresenter extends OpenVKPresenter
}
$this->template->club = $club;
$this->template->ignore_status = $club->isIgnoredBy($this->user->identity);
}
}

View file

@ -22,15 +22,10 @@ final class NotesPresenter extends OpenVKPresenter
if(!$user->getPrivacyPermission('notes.read', $this->user->identity ?? NULL))
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
$this->template->notes = $this->notes->getUserNotes($user, (int)($this->queryParam("p") ?? 1));
$this->template->page = (int)($this->queryParam("p") ?? 1);
$this->template->notes = $this->notes->getUserNotes($user, $this->template->page);
$this->template->count = $this->notes->getUserNotesCount($user);
$this->template->owner = $user;
$this->template->paginatorConf = (object) [
"count" => $this->template->count,
"page" => $this->queryParam("p") ?? 1,
"amount" => NULL,
"perPage" => OPENVK_DEFAULT_PER_PAGE,
];
}
function renderView(int $owner, int $note_id): void

View file

@ -125,5 +125,6 @@ final class SearchPresenter extends OpenVKPresenter
];
$this->template->extendedPaginatorConf = clone $this->template->paginatorConf;
$this->template->extendedPaginatorConf->space = 11;
$this->template->paginatorConf->atTop = true;
}
}

View file

@ -54,6 +54,10 @@ final class UserPresenter extends OpenVKPresenter
$this->template->audioStatus = $user->getCurrentAudioStatus();
$this->template->user = $user;
if($id !== $this->user->id) {
$this->template->ignore_status = $user->isIgnoredBy($this->user->identity);
}
}
}

View file

@ -197,6 +197,16 @@ final class WallPresenter extends OpenVKPresenter
if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0";
if(((int)$this->queryParam('return_banned')) == 0) {
$ignored_sources_ids = $this->user->identity->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " ORDER BY `created` DESC LIMIT " . $pPage . " OFFSET " . ($page - 1) * $pPage);
$count = DatabaseConnection::i()->getConnection()->query("SELECT COUNT(*) " . $queryBase)->fetch()->{"COUNT(*)"};

View file

@ -82,7 +82,14 @@
{/if}
<div class="toTop">
⬆ {_to_top}
<div id='to_up'>
<svg id="to_up_icon" viewBox="0 0 10 6"><polygon points="0 6 5 0 10 6 0 6"/></svg>
<span>{_to_top}</span>
</div>
<div id='to_back'>
<svg id="to_back_icon" viewBox="0 0 10 6"><polygon points="0 0 5 6 10 0 0 0"/></svg>
</div>
</div>
<div class="layout">
@ -238,9 +245,9 @@
<div class="floating_sidebar">
<a id="minilink-friends" class="minilink" href="/friends{$thisUser->getId()}">
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
<object type="internal/link" n:if="$thisUser->getRequestsCount() > 0">
<div class="counter">
+{$thisUser->getFollowersCount()}
+{$thisUser->getRequestsCount()}
</div>
</object>
<img src="/assets/packages/static/openvk/img/friends.svg">
@ -382,6 +389,7 @@
{ifset $thisUser}
{script "js/al_notifs.js"}
{script "js/al_feed.js"}
{/ifset}
<script>bsdnHydrate();</script>

View file

@ -19,7 +19,7 @@
{ifset specpage}
{include specpage, x => $dat}
{else}
<div class="container_gray">
<div class="container_gray {ifset noscroll}no_scroll_container{else}scroll_container{/ifset}">
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
{ifset top}
@ -27,7 +27,7 @@
{/ifset}
{if sizeof($data) > 0}
<div class="content" n:foreach="$data as $dat">
<div class="scroll_node content" n:foreach="$data as $dat">
<table>
<tbody n:attr="id => is_null($table_body_id) ? NULL : $table_body_id">
<tr>

View file

@ -64,8 +64,8 @@
<div n:if="$audiosCount <= 0" style='height: 50%;'>
{include "../components/content_error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_audios_thisuser") : tr("no_audios_user")) : tr("no_audios_club")}
</div>
<div n:if="$audiosCount > 0" class="infContainer">
<div class="infObj" n:foreach="$audios as $audio">
<div n:if="$audiosCount > 0" class="scroll_container infContainer">
<div class="scroll_node infObj" n:foreach="$audios as $audio">
{include "player.xml", audio => $audio, club => $club}
</div>
</div>
@ -86,10 +86,10 @@
{include "../components/content_error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_playlists_thisuser") : tr("no_playlists_user")) : tr("no_playlists_club")}
</div>
<div class="infContainer playlistContainer" n:if="$playlistsCount > 0">
{foreach $playlists as $playlist}
<div class="scroll_container infContainer playlistContainer" n:if="$playlistsCount > 0">
<div class='scroll_node' n:foreach='$playlists as $playlist'>
{include 'playlistListView.xml', playlist => $playlist}
{/foreach}
</div>
</div>
<div>

View file

@ -67,11 +67,11 @@
<hr style="color: #f7f7f7;">
</div>
</div>
<div class="audiosContainer infContainer" style="margin-top: 14px;">
<div class="audiosContainer scroll_container infContainer" style="margin-top: 14px;">
{if $count < 1}
{_empty_playlist}
{else}
<div class="infObj" n:foreach="$audios as $audio">
<div class="scroll_node" n:foreach="$audios as $audio">
{include "player.xml", audio => $audio}
</div>

View file

@ -12,8 +12,8 @@
{/block}
{block content}
<div class="gift_grid">
<div n:foreach="$gifts as $gift" n:class="gift_sel, !$gift->canUse($thisUser) ? disabled" data-gift="{$gift->getId()}">
<div class="gift_grid scroll_container">
<div n:foreach="$gifts as $gift" n:class="scroll_node, gift_sel, !$gift->canUse($thisUser) ? disabled" data-gift="{$gift->getId()}">
<img class="gift_pic" src="{$gift->getImage(2)}" alt="{_gift}" loading=lazy />
<strong class="gift_price">

View file

@ -12,9 +12,9 @@
{include "../components/error.xml", title => "", description => $type == "my" ? tr("no_suggested_posts_by_you") : tr("no_suggested_posts_by_people")}
{else}
<h4 id="cound">{if $type == "my"}{tr("suggested_posts_in_group_by_you", $count)}{else}{tr("suggested_posts_in_group", $count)}{/if}</h4>
<div id="postz" class="infContainer">
<div id="postz" class="infContainer scroll_container">
{var $microblog = $thisUser->hasMicroblogEnabled()}
<div class="infObj" n:foreach="$posts as $post">
<div class="infObj scroll_node" n:foreach="$posts as $post">
{if $microblog}
{include "../components/post/microblogpost.xml", post => $post, commentSection => false, suggestion => true, forceNoCommentsLink => true, forceNoPinLink => true, forceNoLike => true, forceNoShareLink => true, forceNoDeleteLink => false}
{else}

View file

@ -194,6 +194,9 @@
}
</script>
{/if}
<a n:if="!$club->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$club->getRealId()}">
{if !$ignore_status}{_ignore_club}{else}{_unignore_club}{/if}
</a>
</div>
<div>
<div class="content_title_expanded" onclick="hidePanel(this);">

View file

@ -17,9 +17,9 @@
</div>
{if sizeof($corresps) > 0}
<div class="crp-list">
<div class="crp-list scroll_container">
<div n:foreach="$corresps as $coresp"
class="crp-entry"
class="scroll_node crp-entry"
onmousedown="window.location.href = {$coresp->getURL()};" >
{var $recipient = $coresp->getCorrespondents()[1]}
{var $lastMsg = $coresp->getPreviewMessage()}

View file

@ -1,6 +1,5 @@
{extends "../@listView.xml"}
{var $iterator = iterator_to_array($notes)}
{var $page = $paginatorConf->page}
{block title}{_notes}{/block}
@ -60,12 +59,12 @@
}
</style>
<div class="container_gray" style="background: white; border-top: none;">
<div class="container_gray scroll_container" style="background: white; border-top: none;">
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
{if sizeof($data) > 0}
<div n:foreach="$data as $dat">
<div class='scroll_node' n:foreach="$data as $dat">
<div class="profile_thumb">
<a href="{$owner->getURL()}">
<img src="{$owner->getAvatarUrl('miniscule')}" style="width: 50px;">
@ -107,6 +106,14 @@
</article>
</div>
{include "../components/paginator.xml", conf => (object) [
"page" => $page,
"count" => $count,
"amount" => sizeof($data),
"perPage" => 10,
"atBottom" => true,
]}
{else}
{if isset($thisUser) && $thisUser->getId() == $owner->getId()}

View file

@ -21,8 +21,8 @@
</div>
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
{if sizeof($data) > 0}
<div>
<table class="post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat">
<div n:class="$mode !== 'new' ? scroll_container">
<table class="scroll_node post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat">
<tbody>
<tr>
{var $sxModel = $dat->getModel(1)}

View file

@ -30,10 +30,10 @@
{/if}
<br/><br/>
{if $album->getPhotosCount() > 0}
<div class="container_gray album-flex">
<div class="container_gray scroll_container album-flex">
{foreach $photos as $photo}
{php if($photo->isDeleted()) continue; }
<div class="album-photo">
<div class="album-photo scroll_node">
<a
n:if="!is_null($thisUser) && $album->canBeModifiedBy($thisUser)"
href="/album{$album->getPrettyId()}/remove_photo/{$photo->getId()}" class="album-photo--delete">

View file

@ -29,10 +29,10 @@
</div>
<div class='page_wrap_content' id='search_page'>
<div n:class='page_wrap_content_main, $section == "audios" && $count > 0 ? audios_padding'>
<div n:class='page_wrap_content_main, $section != "posts" ? scroll_container, $section == "audios" && $count > 0 ? audios_padding'>
{if $count > 0}
{if $section === 'users'}
<div class='search_content content def_row_content' n:foreach="$data as $dat">
<div class='scroll_node search_content content def_row_content' n:foreach="$data as $dat">
<table>
<tbody>
<tr>
@ -125,10 +125,14 @@
</div>
<script n:if='$count > 0 && !empty($query)'>
highlightText({$query}, '.page_wrap_content_main', ['text'])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', ['text'])
}
__scrollHook()
</script>
{elseif $section === 'groups'}
<div class='search_content content def_row_content' n:foreach="$data as $dat">
<div class='scroll_node search_content content def_row_content' n:foreach="$data as $dat">
<table>
<tbody>
<tr>
@ -180,10 +184,14 @@
</div>
<script n:if='$count > 0 && !empty($query)'>
highlightText({$query}, '.page_wrap_content_main', ['text', "td[data-highlight='_clubDesc']"])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', ['text', "td[data-highlight='_clubDesc']"])
}
__scrollHook()
</script>
{elseif $section === 'apps'}
<div class='search_content content def_row_content' n:foreach="$data as $dat">
<div class='scroll_node search_content content def_row_content' n:foreach="$data as $dat">
<table>
<tbody>
<tr>
@ -210,10 +218,14 @@
</div>
<script n:if='$count > 0 && !empty($query)'>
highlightText({$query}, '.page_wrap_content_main', ['text', "span[data-highlight='_appDesc']"])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', ['text', "span[data-highlight='_appDesc']"])
}
__scrollHook()
</script>
{elseif $section === 'posts'}
<div class='search_content' n:foreach="$data as $dat">
<div class='scroll_node search_content' n:foreach="$data as $dat">
{if !$dat || $dat->getWallOwner()->isHideFromGlobalFeedEnabled()}
{_closed_group_post}.
{else}
@ -222,31 +234,47 @@
</div>
<script n:if='$count > 0 && !empty($query)'>
highlightText({$query}, '.page_wrap_content_main', [".post:not(.comment) > tbody > tr > td > .post-content > .text .really_text"])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', [".post:not(.comment) > tbody > tr > td > .post-content > .text .really_text"])
}
__scrollHook()
</script>
{elseif $section === 'videos'}
<div class='search_content' n:foreach="$data as $dat">
<div class='scroll_node search_content' n:foreach="$data as $dat">
{include "../components/video.xml", video => $dat}
</div>
<script n:if='$count > 0 && !empty($query)'>
highlightText({$query}, '.page_wrap_content_main', [".video_name", ".video_description"])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', [".video_name", ".video_description"])
}
__scrollHook()
</script>
{elseif $section === 'audios'}
<div class='search_content' n:foreach="$data as $dat">
<div class='scroll_node search_content' n:foreach="$data as $dat">
{include "../Audio/player.xml", audio => $dat}
</div>
<script n:if="$count > 0 && !empty($query) && empty($_REQUEST['only_performers'])">
highlightText({$query}, '.page_wrap_content_main', [".mediaInfo .performer a", ".mediaInfo .title"])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', [".mediaInfo .performer a", ".mediaInfo .title"])
}
__scrollHook()
</script>
{elseif $section === 'audios_playlists'}
<div class='search_content' n:foreach="$data as $dat">
<div class='scroll_node search_content' n:foreach="$data as $dat">
{include "../Audio/playlistListView.xml", playlist => $dat}
</div>
<script n:if="$count > 0 && !empty($query) && empty($_REQUEST['only_performers'])">
highlightText({$query}, '.page_wrap_content_main', [".playlistName", ".playlistDesc"])
function __scrollHook(page) {
highlightText({$query}, '.page_wrap_content_main', [".playlistName", ".playlistDesc"])
}
__scrollHook()
</script>
{/if}
{else}

View file

@ -2,6 +2,7 @@
{var $iterator = $user->getClubs($page, $admin)}
{var $count = $user->getClubCount($admin)}
{block noscroll}{/block}
{block title}
{_groups}
{/block}

View file

@ -166,6 +166,9 @@
{/if}
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a>
<a n:if="!$user->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;width:96%;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$user->getId()}">
{if !$ignore_status}{_ignore_user}{else}{_unignore_user}{/if}
</a>
<script>
function reportUser() {
uReportMsgTxt = tr("going_to_report_user");

View file

@ -15,6 +15,8 @@
<div n:attr="id => (isset($globalFeed) ? 'activetabs' : 'ki')" class="tab">
<a n:attr="id => (isset($globalFeed) ? 'act_tab_a' : 'ki')" href="/feed/all">{_all_news}</a>
</div>
<a href='#' id="__feed_settings_link" data-pagescount='{ceil($paginatorConf->count / $paginatorConf->perPage)}'>{_feed_settings}</a>
</div>
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">

View file

@ -9,9 +9,11 @@
</div>
{if sizeof($comments) > 0}
{foreach $comments as $comment}
{include "comment.xml", comment => $comment}
{/foreach}
<div class='scroll_container'>
<div class='scroll_node' n:foreach="$comments as $comment">
{include "comment.xml", comment => $comment}
</div>
</div>
<div style="margin-top: 11px;">
{include "paginator.xml", conf => (object) ["page" => $page, "count" => $count, "amount" => sizeof($comments), "perPage" => 10]}
</div>

View file

@ -2,7 +2,7 @@
{var $pageCount = ceil($conf->count / $conf->perPage)}
<div n:if="!($conf->page === 1 && $conf->count <= $conf->perPage)" n:attr="style => (!$conf->tidy ? 'padding: 8px;')">
<div n:class="paginator, ($conf->atBottom ?? false) ? paginator-at-bottom, ($conf->tidy ? 'tidy')">
<div n:class="paginator, (($conf->atTop || $atTop) ?? false) ? paginator-at-top, ($conf->atBottom ?? false) ? paginator-at-bottom, ($conf->tidy ? 'tidy')">
<a n:if="$conf->page > $space" n:attr="class => ($conf->page === 1 ? 'active')" href="?{http_build_query(array_merge($_GET, ['p' => 1]), 'k', '&', PHP_QUERY_RFC3986)}">&laquo;</a>
{for $j = $conf->page - ($space-1); $j <= $conf->page + ($space-1); $j++}
<a n:if="$j > 0 && $j <= $pageCount" n:attr="class => ($conf->page === $j ? 'active')" href="?{http_build_query(array_merge($_GET, ['p' => $j]), 'k', '&', PHP_QUERY_RFC3986)}">{$j}</a>

View file

@ -88,3 +88,7 @@ div.ovk-video > div > img
height: 31px;
min-width: 30px;
}
.entity_vertical_list .entity_vertical_list_item img {
border-radius: 20px;
}

View file

@ -621,6 +621,16 @@ input[type=checkbox]:checked:disabled {
background-position: 0 -42px;
}
input[type="number"] {
border: 1px solid #C0CAD5;
padding: 3px;
font-size: 11px;
font-family: tahoma, verdana, arial, sans-serif;
width: 100%;
box-sizing: border-box;
outline: none;
}
#auth {
padding: 10px;
}
@ -1381,7 +1391,7 @@ textarea {
.toTop {
position: fixed;
padding: 20px;
padding: 12px;
width: 100px;
height: 100%;
background-color: #f3f3f3;
@ -1391,9 +1401,34 @@ textarea {
opacity: 0;
transition: .1s all;
z-index: 129;
user-select: none;
}
body.scrolled .toTop:hover {
.toTop > div svg {
display: inline-block;
margin-right: 2px;
width: 8px;
height: 7px;
fill: #3f3f3f;
}
.toTop > div span {
font-weight: bold;
}
.toTop.has_down #to_up, .toTop #to_back {
display: none;
}
.toTop.has_down #to_back {
display: block;
}
.toTop.has_down {
opacity: .3;
}
body.scrolled .toTop:hover, .toTop.has_down:hover {
opacity: .5;
cursor: pointer;
}
@ -2120,6 +2155,10 @@ table td[width="120"] {
border-radius: 2px;
}
.mb_tab:hover {
background: #e2e0e0;
}
.mb_tab div, .mb_tab > a {
padding: 5px 9px;
display: block;
@ -3263,6 +3302,7 @@ hr {
flex-direction: row;
justify-content: space-between;
gap: 4px;
cursor: pointer;
}
.entity_vertical_list .entity_vertical_list_item .first_column {
@ -3300,3 +3340,44 @@ hr {
width: 30px;
height: 7px;
}
#__feed_settings_link {
float: right;
margin-right: 3px;
margin-top: 2px;
}
#__feed_settings_link:hover {
text-decoration: underline;
}
#_feed_settings_container {
height: 100%;
}
#_feed_settings_container #__content {
display: flex;
flex-direction: column;
gap: 4px;
padding: 5px;
height: 100%;
}
#_feed_settings_container #__content .settings_item {
display: flex;
flex-direction: column;
}
#_feed_settings_container #__content .final_settings_item {
align-self: end;
margin-top: 74px;
}
#_feed_settings_container #pageNumber {
width: 70px;
}
#_feed_settings_container .entity_vertical_list {
height: 206px;
overflow-x: hidden;
}

View file

@ -1,4 +1,4 @@
u(".comment-reply").on("click", function(e) {
u(document).on("click", ".comment-reply", function(e) {
let comment = u(e.target).closest(".post");
let authorId = comment.data("owner-id");
let authorNm = u(".post-author > a > b", comment.first()).text().trim();

270
Web/static/js/al_feed.js Normal file
View file

@ -0,0 +1,270 @@
//u('.postFeedPageSelect').attr('style', 'display:none')
// Source ignoring
u(document).on("click", "#__ignoreSomeone", async (e) => {
e.preventDefault()
const TARGET = u(e.target)
const ENTITY_ID = Number(e.target.dataset.id)
const VAL = Number(e.target.dataset.val)
const ACT = VAL == 1 ? 'ignore' : 'unignore'
const METHOD_NAME = ACT == 'ignore' ? 'addBan' : 'deleteBan'
const PARAM_NAME = ENTITY_ID < 0 ? 'group_ids' : 'user_ids'
const ENTITY_NAME = ENTITY_ID < 0 ? 'club' : 'user'
const URL = `/method/newsfeed.${METHOD_NAME}?auth_mechanism=roaming&${PARAM_NAME}=${Math.abs(ENTITY_ID)}`
TARGET.addClass('lagged')
const REQ = await fetch(URL)
const RES = await REQ.json()
TARGET.removeClass('lagged')
if(RES.error_code) {
switch(RES.error_code) {
case -10:
fastError(';/')
break
case -50:
fastError(tr('ignored_sources_limit'))
break
default:
fastError(res.error_msg)
break
}
return
}
if(RES.response == 1) {
if(ACT == 'unignore') {
TARGET.attr('data-val', '1')
TARGET.html(tr(`ignore_${ENTITY_NAME}`))
} else {
TARGET.attr('data-val', '0')
TARGET.html(tr(`unignore_${ENTITY_NAME}`))
}
}
})
u(document).on('click', '#__feed_settings_link', (e) => {
e.preventDefault()
let current_tab = 'main';
const body = `
<div id='_feed_settings_container'>
<div id='_tabs'>
<div class="mb_tabs">
<div class="mb_tab" data-name='main'>
<a>
${tr('main')}
</a>
</div>
<div class="mb_tab" data-name='ignored'>
<a>
${tr('ignored_sources')}
</a>
</div>
</div>
</div>
<div id='__content'></div>
</div>
`
MessageBox(tr("feed_settings"), body, [tr("close")], [Function.noop])
u('.ovk-diag-body').attr('style', 'padding:0px;height: 255px;')
async function __switchTab(tab)
{
current_tab = tab
u(`#_feed_settings_container .mb_tab`).attr('id', 'ki')
u(`#_feed_settings_container .mb_tab[data-name='${tab}']`).attr('id', 'active')
u(`#_feed_settings_container .mb_tabs input`).remove()
switch(current_tab) {
case 'main':
const __temp_url = new URL(location.href)
const PAGES_COUNT = Number(e.target.dataset.pagescount ?? '10')
const CURRENT_PERPAGE = Number(__temp_url.searchParams.get('posts') ?? 10)
const CURRENT_PAGE = Number(__temp_url.searchParams.get('p') ?? 1)
const CURRENT_RETURN_BANNED = Number(__temp_url.searchParams.get('return_banned') ?? 0)
const COUNT = [1, 5, 10, 20, 30, 40, 50]
u('#_feed_settings_container #__content').html(`
<table cellspacing="7" cellpadding="0" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr('posts_per_page')}</span>
</td>
<td>
<select id="pageSelect"></select>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr('start_from_page')}</span>
</td>
<td>
<input type='number' min='1' max='${PAGES_COUNT}' id='pageNumber' value='${CURRENT_PAGE}' placeholder='${CURRENT_PAGE}'>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">
<input type='checkbox' id="showIgnored" ${CURRENT_RETURN_BANNED == 1 ? 'checked' : ''}>
</span>
</td>
<td>
${tr('show_ignored_sources')}
</td>
</tr>
<tr>
<td width="120" valign="top">
</td>
<td>
<input class='button' type='button' value='${tr('apply')}'>
</td>
</tr>
</tbody>
</table>
`)
u(`#_feed_settings_container #__content input[type='button']`).on('click', (e) => {
const INPUT_PAGES_COUNT = parseInt(u('#_feed_settings_container #__content #pageSelect').nodes[0].selectedOptions[0].value ?? '10')
const INPUT_PAGE = parseInt(u('#_feed_settings_container #__content #pageNumber').nodes[0].value ?? '1')
const INPUT_IGNORED = Number(u('#_feed_settings_container #__content #showIgnored').nodes[0].checked ?? false)
const FINAL_URL = new URL(location.href)
if(CURRENT_PERPAGE != INPUT_PAGES_COUNT) {
FINAL_URL.searchParams.set('posts', INPUT_PAGES_COUNT)
}
if(CURRENT_PAGE != INPUT_PAGE && INPUT_PAGE <= PAGES_COUNT) {
FINAL_URL.searchParams.set('p', Math.max(1, INPUT_PAGE))
}
if(INPUT_IGNORED == 1) {
FINAL_URL.searchParams.set('return_banned', 1)
} else {
FINAL_URL.searchParams.delete('return_banned')
}
window.location.assign(FINAL_URL.href)
})
COUNT.forEach(item => {
u('#_feed_settings_container #pageSelect').append(`
<option value="${item}" ${item == CURRENT_PERPAGE ? 'selected' : ''}>${item}</option>
`)
})
break
case 'ignored':
u('#_feed_settings_container #__content').html(`
<div id='gif_loader'></div>
`)
if(!window.openvk.ignored_list) {
const IGNORED_RES = await fetch('/method/newsfeed.getBanned?auth_mechanism=roaming&extended=1&fields=real_id,screen_name,photo_50&merge=1')
const IGNORED_LIST = await IGNORED_RES.json()
window.openvk.ignored_list = IGNORED_LIST
}
u('#_feed_settings_container #__content').html(`
<div class='entity_vertical_list mini'></div>
`)
u('#_feed_settings_container .mb_tabs').append(`
<input class='button lagged' id='_remove_ignores' type='button' value='${tr('stop_ignore')}'>
`)
if(window.openvk.ignored_list.error_code) {
fastError(IGNORED_LIST.error_msg)
return
}
if(window.openvk.ignored_list.response.items.length < 1) {
u('#_feed_settings_container #__content').html(`
<div class="information">
${tr('no_ignores_count')}
</div>`)
u('#_remove_ignores').remove()
}
window.openvk.ignored_list.response.items.forEach(ignore_item => {
let name = ignore_item.name
if(!name) {
name = ignore_item.first_name + ' ' + ignore_item.last_name
}
u('#_feed_settings_container #__content .entity_vertical_list').append(`
<label class='entity_vertical_list_item with_third_column' data-id='${ignore_item.real_id}'>
<div class='first_column'>
<a href='/${ignore_item.screen_name}' class='avatar'>
<img src='${ignore_item.photo_50}'>
</a>
<div class='info'>
<b class='noOverflow'>
<a href="/${ignore_item.screen_name}">
${ovk_proc_strtr(escapeHtml(name), 100)}
</a>
</b>
</div>
</div>
<div class='third_column' style="display: grid; align-items: center;">
<input type='checkbox' name='remove_me'>
</div>
</label>
`)
})
u("#_feed_settings_container").on("click", "input[name='remove_me']", async (e) => {
const checks_count = u(`input[name='remove_me']:checked`).length
if(checks_count > 0) {
u('.mb_tabs #_remove_ignores').removeClass('lagged')
} else {
u('.mb_tabs #_remove_ignores').addClass('lagged')
}
if(checks_count > 10) {
e.preventDefault()
}
})
u('#_feed_settings_container').on('click', '#_remove_ignores', async (e) => {
e.target.classList.add('lagged')
const ids = []
u('#__content .entity_vertical_list label').nodes.forEach(item => {
const _checkbox = item.querySelector(`input[type='checkbox'][name='remove_me']`)
if(_checkbox.checked) {
ids.push(item.dataset.id)
}
})
const user_ids = []
const group_ids = []
ids.forEach(id => {
id > 0 ? user_ids.push(id) : group_ids.push(Math.abs(id))
})
const res = await fetch(`/method/newsfeed.deleteBan?auth_mechanism=roaming&user_ids=${user_ids.join(',')}&group_ids=${group_ids.join(',')}`)
const resp = await res.json()
if(resp.error_code) {
console.error(resp.error_msg)
return
}
window.openvk.ignored_list = null
__switchTab('ignored')
})
break
}
}
u("#_feed_settings_container").on("click", ".mb_tab a", async (e) => {
await __switchTab(u(e.target).closest('.mb_tab').attr('data-name'))
})
__switchTab('main')
})

View file

@ -641,6 +641,35 @@ class bigPlayer {
duration: this.tracks["currentTrack"].length
})
}
loadContextPage(page, lesser = false) {
const formdata = new FormData()
formdata.append("context", this.context["context_type"])
formdata.append("context_entity", this.context["context_id"])
formdata.append("hash", u("meta[name=csrf]").attr("value"))
formdata.append("page", page)
ky.post("/audios/context", {
hooks: {
afterResponse: [
async (_request, _options, response) => {
const newArr = await response.json()
if(lesser)
this.tracks["tracks"] = newArr["items"].concat(this.tracks["tracks"])
else
this.tracks["tracks"] = this.tracks["tracks"].concat(newArr["items"])
this.context["playedPages"].push(String(newArr["page"]))
this.updateButtons()
console.info("Loaded context for page " + page)
}
]
},
body: formdata
})
}
}
document.addEventListener("DOMContentLoaded", function() {

View file

@ -201,7 +201,7 @@ $(document).on("click", ".sugglist a", (e) => {
})
// нажатие на пагинатор у постов предложки
$(document).on("click", "#postz .paginator a", (e) => {
/*$(document).on("click", "#postz .paginator a", (e) => {
e.preventDefault()
ky(e.currentTarget.href, {
@ -228,4 +228,4 @@ $(document).on("click", "#postz .paginator a", (e) => {
]
}
})
})
})*/

View file

@ -1644,6 +1644,79 @@ $(document).on("click", ".avatarDelete", (e) => {
]);
})
async function __processPaginatorNextPage(page)
{
const container = u('.scroll_container')
const container_node = '.scroll_node'
const parser = new DOMParser
const replace_url = new URL(location.href)
replace_url.searchParams.set('p', page)
const new_content = await fetch(replace_url.href)
const new_content_response = await new_content.text()
const parsed_content = parser.parseFromString(new_content_response, 'text/html')
const nodes = parsed_content.querySelectorAll(container_node)
nodes.forEach(node => {
container.append(node)
})
u(`.paginator:not(.paginator-at-top)`).html(parsed_content.querySelector('.paginator:not(.paginator-at-top)').innerHTML)
// fffffuck
if(u(`.paginator:not(.paginator-at-top)`).nodes[0].closest('.scroll_container')) {
container.nodes[0].append(u(`.paginator:not(.paginator-at-top)`).nodes[0].parentNode)
}
if(window.player) {
window.player.loadContextPage(page)
}
if(typeof __scrollHook != 'undefined') {
__scrollHook(page)
}
}
const showMoreObserver = new IntersectionObserver(entries => {
entries.forEach(async x => {
if(x.isIntersecting) {
if(u('.scroll_container').length < 1) {
return
}
const target = u(x.target)
if(target.length < 1 || target.hasClass('paginator-at-top')) {
return
}
const current_url = new URL(location.href)
if(current_url.searchParams && !isNaN(parseInt(current_url.searchParams.get('p')))) {
return
}
target.addClass('lagged')
const active_tab = target.find('.active')
const next_page = u(active_tab.nodes[0] ? active_tab.nodes[0].nextElementSibling : null)
if(next_page.length < 1) {
u('.paginator:not(.paginator-at-top)').removeClass('lagged')
return
}
const page_number = Number(next_page.html())
await __processPaginatorNextPage(page_number)
u('.paginator:not(.paginator-at-top)').removeClass('lagged')
}
})
}, {
root: null,
rootMargin: '0px',
threshold: 0,
})
if(u('.paginator:not(.paginator-at-top)').length > 0) {
showMoreObserver.observe(u('.paginator:not(.paginator-at-top)').nodes[0])
}
u(document).on('click', '#__sourceAttacher', (e) => {
MessageBox(tr('add_source'), `
<div id='source_flex_kunteynir'>

View file

@ -90,7 +90,7 @@ document.addEventListener("DOMContentLoaded", function() { //BEGIN
});
/* @rem-pai why this func wasn't named as "#_deleteDialog"? It looks universal IMO */
u("#_noteDelete").on("click", function(e) {
u(document).on("click", "#_noteDelete", function(e) {
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
formHtml += "</form>";
@ -167,7 +167,7 @@ document.addEventListener("DOMContentLoaded", function() { //BEGIN
return false;
});
u("#_submitUserSubscriptionAction").handle("submit", async function(e) {
u(document).handle("submit", "#_submitUserSubscriptionAction", async function(e) {
u(this).nodes[0].parentElement.classList.add('loading');
u(this).nodes[0].parentElement.classList.add('disable');
console.log(e.target);
@ -518,7 +518,7 @@ function highlightText(searchText, container_selector, selectors = []) {
node.parentNode.insertBefore(tempDiv.firstChild, node)
}
node.parentNode.removeChild(node)
} else if(node.nodeType === 1 && node.tagName !== 'SCRIPT' && node.tagName !== 'BR' && node.tagName !== 'STYLE') {
} else if(node.nodeType === 1 && node.tagName !== 'SCRIPT' && node.tagName !== 'BR' && node.tagName !== 'STYLE' && !node.classList.contains('highlight')) {
Array.from(node.childNodes).forEach(highlightNode);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,35 @@
window.addEventListener("scroll", function(e) {
if(window.scrollY < 100) {
if(window.temp_y_scroll) {
u('.toTop').addClass('has_down')
}
document.body.classList.toggle("scrolled", false);
} else {
document.body.classList.toggle("scrolled", true);
u('.toTop').removeClass('has_down')
}
});
u(".toTop").on("click", function(e) {
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
const y_scroll = window.scrollY
const scroll_margin = 20
if(y_scroll > 100) {
window.temp_y_scroll = y_scroll
window.scrollTo(0, scroll_margin)
window.scrollTo({
top: 0,
behavior: "smooth"
})
} else {
if(window.temp_y_scroll) {
window.scrollTo(0, window.temp_y_scroll - scroll_margin)
window.scrollTo({
top: window.temp_y_scroll,
behavior: "smooth"
})
}
}
u(document).trigger('scroll')
})

View file

@ -1,424 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@atlassian/aui@^9.6.0":
version "9.6.0"
resolved "https://registry.yarnpkg.com/@atlassian/aui/-/aui-9.6.0.tgz#9f35e67359022f1e6d5efa2653e79aeec95b9e77"
integrity sha512-o/bCufj0tUU6pRk3AWoXlcyVMTMx4QswB1UY5oJWSjopA+z/QUx0fhc4rRIIbxP0MrJMNRDpgPyuzkoPb7Z7ow==
dependencies:
"@atlassian/tipsy" "1.3.3"
"@popperjs/core" "2.11.6"
backbone "1.4.1"
css.escape "1.5.1"
dompurify "2.4.5"
fancy-file-input "2.0.4"
jquery-ui "1.13.2"
skatejs "0.13.17"
skatejs-template-html "0.0.0"
trim-extra-html-whitespace "1.3.0"
underscore "1.13.6"
"@atlassian/tipsy@1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@atlassian/tipsy/-/tipsy-1.3.3.tgz#3f77754c4c70324c5c938e41abaa2ca682f22036"
integrity sha512-6jd9wdoiPdCbwsNi1Xrn/oMdGz22dKPeCoZ/cCGKqjnh+UYkBKb5W3spW+WNqRSxGvVtfUEEg6TXotRK/FPDaw==
"@popperjs/core@2.11.6", "@popperjs/core@^2.9.0":
version "2.11.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
backbone@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.1.tgz#099a78184bc07b034048a8332229c2ccca1e3e62"
integrity sha512-ADy1ztN074YkWbHi8ojJVFe3vAanO/lrzMGZWUClIP7oDD/Pjy2vrASraUP+2EVCfIiTtCW4FChVow01XneivA==
dependencies:
underscore ">=1.8.3"
codem-isoboxer@0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/codem-isoboxer/-/codem-isoboxer-0.3.6.tgz#867f670459b881d44f39168d5ff2a8f14c16151d"
integrity sha512-LuO8/7LW6XuR5ERn1yavXAfodGRhuY2yP60JTZIw5yNYMCE5lUVbk3NFUCJxjnphQH+Xemp5hOGb1LgUXm00Xw==
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
create-react-class@^15.7.0:
version "15.7.0"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e"
integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
cropperjs@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.6.1.tgz#fd132021d93b824b1b0f2c2c3b763419fb792d89"
integrity sha512-F4wsi+XkDHCOMrHMYjrTEE4QBOrsHHN5/2VsVAaRq8P7E5z7xQpT75S+f/9WikmBEailas3+yo+6zPIomW+NOA==
css.escape@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
dashjs@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/dashjs/-/dashjs-4.3.0.tgz#cccda5a490cabf6c3b48aa887ec8c8ac0df1a233"
integrity sha512-cqpnJaPQpEY4DsEdF9prwD00+5dp5EGHCFc7yo9n2uuAH9k4zPkZJwXQ8dXmVRhPf3M89JfKSoAYIP3dbXmqcg==
dependencies:
codem-isoboxer "0.3.6"
es6-promise "^4.2.8"
fast-deep-equal "2.0.1"
html-entities "^1.2.1"
imsc "^1.0.2"
localforage "^1.7.1"
dompurify@2.4.5:
version "2.4.5"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.5.tgz#0e89a27601f0bad978f9a924e7a05d5d2cccdd87"
integrity sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==
encoding@^0.1.11:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
dependencies:
iconv-lite "^0.6.2"
es6-promise@^4.2.8:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
event-lite@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.2.tgz#838a3e0fdddef8cc90f128006c8e55a4e4e4c11b"
integrity sha512-HnSYx1BsJ87/p6swwzv+2v6B4X+uxUteoDfRxsAb1S1BePzQqOLevVmkdA15GHJVd9A9Ok6wygUR18Hu0YeV9g==
fancy-file-input@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/fancy-file-input/-/fancy-file-input-2.0.4.tgz#698c216482e07649a827681c4db3054fddc9a32b"
integrity sha512-l+J0WwDl4nM/zMJ/C8qleYnXMUJKsLng7c5uWH/miAiHoTvPDtEoLW1tmVO6Cy2O8i/1VfA+2YOwg/Q3+kgO6w==
fast-deep-equal@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fbjs@^0.8.0:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
source-map "^0.6.1"
wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"
html-entities@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
iconv-lite@^0.6.2:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
id3js@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/id3js/-/id3js-2.1.1.tgz#0c307d0d2f194bc5fa7a809bbed0b1a93577f16d"
integrity sha512-9Gi+sG0RHSa5qn8hkwi2KCl+2jV8YrtiZidXbOO3uLfRAxc2jilRg0fiQ3CbeoAmR7G7ap3RVs1kqUVhIyZaog==
ieee754@^1.1.8:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
imsc@^1.0.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/imsc/-/imsc-1.1.3.tgz#e96a60a50d4000dd7b44097272768b9fd6a4891d"
integrity sha512-IY0hMkVTNoqoYwKEp5UvNNKp/A5jeJUOrIO7judgOyhHT+xC6PA4VBOMAOhdtAYbMRHx9DTgI8p6Z6jhYQPFDA==
dependencies:
sax "1.2.1"
int64-buffer@^0.1.9:
version "0.1.10"
resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423"
integrity sha1-J3siiofZWtd30HwTgyAiQGpHNCM=
is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
isarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
jquery-ui@1.13.2:
version "1.13.2"
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.2.tgz#de03580ae6604773602f8d786ad1abfb75232034"
integrity sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==
dependencies:
jquery ">=1.8.0 <4.0.0"
"jquery@>=1.8.0 <4.0.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
jquery@^3.0.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.0.tgz#fe2c01a05da500709006d8790fe21c8a39d75612"
integrity sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
knockout@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf"
integrity sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q==
ky@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/ky/-/ky-0.19.0.tgz#d6ad117e89efe2d85a1c2e91462d48ca1cda1f7a"
integrity sha512-RkDgbg5ahMv1MjHfJI2WJA2+Qbxq0iNSLWhreYiCHeHry9Q12sedCnP5KYGPt7sydDvsyH+8UcG6Kanq5mpsyw==
lie@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
dependencies:
immediate "~3.0.5"
literallycanvas@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/literallycanvas/-/literallycanvas-0.5.2.tgz#7d4800a8d9c4b38a593e91695d52466689586abd"
integrity sha512-SPxZ0DfzUWOXBPnsFD/Y1OulGKpv33IKGB+DCoUkNKQsc38kRDy1mGjbWbs/JAAyIGM1C9Y0CYI23SQmtXR5aw==
dependencies:
react-addons-pure-render-mixin "^15.1"
localforage@^1.7.1:
version "1.10.0"
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
dependencies:
lie "3.1.1"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
minimist@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
monaco-editor@^0.20.0:
version "0.20.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
msgpack-lite@^0.1.26:
version "0.1.26"
resolved "https://registry.yarnpkg.com/msgpack-lite/-/msgpack-lite-0.1.26.tgz#dd3c50b26f059f25e7edee3644418358e2a9ad89"
integrity sha1-3TxQsm8FnyXn7e42REGDWOKprYk=
dependencies:
event-lite "^0.1.1"
ieee754 "^1.1.8"
int64-buffer "^0.1.9"
isarray "^1.0.0"
neo-async@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
plotly.js-dist@^1.52.3:
version "1.52.3"
resolved "https://registry.yarnpkg.com/plotly.js-dist/-/plotly.js-dist-1.52.3.tgz#4c16c6da6adab6cdba169087b5005bdddbf10834"
integrity sha512-kpuNwveRk6M/5cCW1ZgJTbMLD0bRZhJlLK0cUHVkTsP/PWKCVJqO3QiiqrypFGE/xEhWfHCY+YKAKjMmEbo4Gw==
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
react-addons-pure-render-mixin@^15.1:
version "15.6.3"
resolved "https://registry.yarnpkg.com/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.3.tgz#5dc73af0fa32186dbc4887f20667b46d3f9caed7"
integrity sha512-e7F2OsLiyYGr9SHWHGlI/FfHRh+kbYx0hNfdN5zivHIf4vzeno7gsRJKXg71E35CpUCnre+JfM6UgWWgsvJBzA==
dependencies:
object-assign "^4.1.0"
react-dom-factories@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0"
integrity sha1-63cFxNs2+1AbOqOP91lhaqD/luA=
react-dom@15.1:
version "15.1.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.1.0.tgz#d0c2b24c8b47a41a2b9ec766662d4e686f353153"
integrity sha1-0MKyTItHpBornsdmZi1OaG81MVM=
react@15.1:
version "15.1.0"
resolved "https://registry.yarnpkg.com/react/-/react-15.1.0.tgz#5f7a9f085a00509898efd2b24cb12ea1dfaf8b40"
integrity sha1-X3qfCFoAUJiY79KyTLEuod+vi0A=
dependencies:
fbjs "^0.8.0"
loose-envify "^1.1.0"
object-assign "^4.1.0"
requirejs@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9"
integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==
"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
skatejs-template-html@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/skatejs-template-html/-/skatejs-template-html-0.0.0.tgz#e990c1a7d4b58b7305ffcc3338939bf402023df7"
integrity sha1-6ZDBp9S1i3MF/8wzOJOb9AICPfc=
skatejs@0.13.17:
version "0.13.17"
resolved "https://registry.yarnpkg.com/skatejs/-/skatejs-0.13.17.tgz#7a21fbb3434da45e52b47b61647168ee9e778071"
integrity sha1-eiH7s0NNpF5StHthZHFo7p53gHE=
soundjs@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/soundjs/-/soundjs-1.0.1.tgz#99970542d28d0df2a1ebd061ae75c961a98c8180"
integrity sha512-MgFPvmKYfpcNiE3X5XybNvScie3DMQlZgmNzUn4puBcpw64f4LqjH/fhM8Sb/eTJ8hK57Crr7mWy0bfJOqPj6Q==
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
textfit@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/textfit/-/textfit-2.4.0.tgz#80cba8006bfb9c3d9d552739257957bdda95c79c"
integrity sha512-/x4aoY5+/tJmu+iwpBH1yw75TFp86M6X15SvaaY/Eep7YySQYtqdOifEtfvVyMwzl7SZ+G4RQw00FD9g5R6i1Q==
tippy.js@^6.3.7:
version "6.3.7"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
dependencies:
"@popperjs/core" "^2.9.0"
trim-extra-html-whitespace@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/trim-extra-html-whitespace/-/trim-extra-html-whitespace-1.3.0.tgz#b47efb0d1a5f2a56a85cc45cea525651e93404cf"
integrity sha1-tH77DRpfKlaoXMRc6lJWUek0BM8=
ua-parser-js@^0.7.18:
version "0.7.33"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532"
integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==
uglify-js@^3.1.4:
version "3.17.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.3.tgz#f0feedf019c4510f164099e8d7e72ff2d7304377"
integrity sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==
umbrellajs@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/umbrellajs/-/umbrellajs-3.1.0.tgz#a4e6f0f6381f9d93110b5eee962e0e0864b10bd0"
integrity sha512-3qichMg1Q6EetLweBAT0L55O2W6CJe9qyiSt1RBnf+bcOqwJ4R7e2PDcoIUrCsg+uRo3DXOvurWdklBu0ia7fg==
underscore@1.13.6:
version "1.13.6"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441"
integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==
underscore@>=1.8.3:
version "1.13.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
whatwg-fetch@>=0.10.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==

View file

@ -277,6 +277,45 @@ function parseAttachments(string $attachments): array
return $returnArr;
}
function get_entity_by_id(int $id)
{
if($id > 0)
return (new openvk\Web\Models\Repositories\Users)->get($id);
return (new openvk\Web\Models\Repositories\Clubs)->get(abs($id));
}
function get_entities(array $ids = []): array
{
$main_result = [];
$users = [];
$clubs = [];
foreach($ids as $id) {
$id = (int)$id;
if($id < 0)
$clubs[] = abs($id);
if($id > 0)
$users[] = $id;
}
if(sizeof($users) > 0) {
$users_tmp = (new openvk\Web\Models\Repositories\Users)->getByIds($users);
foreach($users_tmp as $user) {
$main_result[] = $user;
}
}
if(sizeof($clubs) > 0) {
$clubs_tmp = (new openvk\Web\Models\Repositories\Clubs)->getByIds($clubs);
foreach($clubs_tmp as $club) {
$main_result[] = $club;
}
}
return $main_result;
}
function ovk_scheme(bool $with_slashes = false): string
{
$scheme = ovk_is_ssl() ? "https" : "http";

View file

@ -27,15 +27,15 @@ ADD composer.* .
RUN composer install
FROM docker.io/node:14 as nodejs
FROM docker.io/node:20 as nodejs
COPY --from=builder /opt/chandler /opt/chandler
WORKDIR /opt/chandler/extensions/available/openvk/Web/static/js
ADD Web/static/js/package.json Web/static/js/package-lock.json Web/static/js/yarn.lock ./
ADD Web/static/js/package.json Web/static/js/package-lock.json ./
RUN yarn install
RUN npm ci
WORKDIR /opt/chandler/extensions/available/openvk

View file

@ -0,0 +1,2 @@
CREATE TABLE `ignored_sources` (`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT , `owner` BIGINT(20) UNSIGNED NOT NULL , `source` BIGINT(20) NOT NULL , PRIMARY KEY (`id`), INDEX (`owner`)) ENGINE = InnoDB;
ALTER TABLE `ignored_sources` ADD INDEX `owner_source` (`owner`, `source`);

View file

@ -219,6 +219,7 @@
"my_news" = "My news";
"all_news" = "All news";
"posts_per_page" = "Number of posts per page";
"show_ignored_sources" = "Show ignored sources";
"attachment" = "Attachment";
"post_as_group" = "Post as group";
@ -251,6 +252,17 @@
"edited_short" = "edited";
"feed_settings" = "Feed settings";
"ignored_sources" = "Ignored sources";
"ignore_user" = "Ignore user";
"unignore_user" = "Unignore user";
"ignore_club" = "Ignore club";
"unignore_club" = "Unignore club";
"no_ignores_count" = "No ignored sources.";
"stop_ignore" = "Unignore";
"start_from_page" = "Start from page";
"all_posts" = "All posts";
"users_posts" = "Posts by $1";
"clubs_posts" = "Group's posts";
@ -1544,6 +1556,9 @@
"group_is_banned" = "Group was successfully banned";
"description_too_long" = "Description is too long.";
"invalid_club" = "This group does not exists.";
"invalid_user" = "This user does not exists.";
"ignored_sources_limit" = "Limit of ignored sources has exceed";
"invalid_audio" = "Invalid audio.";
"do_not_have_audio" = "You don't have this audio.";
@ -1762,6 +1777,7 @@
"question_confirm" = "This action can't be undone. Do you really wanna do it?";
"confirm_m" = "Confirm";
"action_successfully" = "Success";
"apply" = "Apply";
/* User Alerts */

View file

@ -204,6 +204,7 @@
"my_news" = "Мои новости";
"all_news" = "Все новости";
"posts_per_page" = "Количество записей на странице";
"show_ignored_sources" = "Показывать игнорируемые источники";
"attachment" = "Вложение";
"post_as_group" = "От имени сообщества";
"comment_as_group" = "От имени сообщества";
@ -231,6 +232,17 @@
"post_is_ad" = "Этот пост был размещён за взятку.";
"edited_short" = "ред.";
"feed_settings" = "Настройки ленты";
"ignored_sources" = "Игнорируемые источники";
"ignore_user" = "Игнорировать пользователя";
"unignore_user" = "Не игнорировать пользователя";
"ignore_club" = "Игнорировать группу";
"unignore_club" = "Не игнорировать группу";
"no_ignores_count" = "Игнорируемых источников нет.";
"stop_ignore" = "Не игнорировать";
"start_from_page" = "Начинать со страницы";
"all_posts" = "Все записи";
"users_posts" = "Записи $1";
"clubs_posts" = "Записи сообщества";
@ -1445,6 +1457,11 @@
"group_owner_is_banned" = "Создатель сообщества успешно забанен.";
"group_is_banned" = "Сообщество успешно забанено";
"description_too_long" = "Описание слишком длинное.";
"invalid_club" = "Такой группы не существует.";
"invalid_user" = "Такого пользователя не существует.";
"ignored_sources_limit" = "Превышен лимит игнорируемых источников.";
"invalid_audio" = "Такой аудиозаписи не существует.";
"do_not_have_audio" = "У вас нет этой аудиозаписи.";
"do_have_audio" = "У вас уже есть эта аудиозапись.";
@ -1652,6 +1669,7 @@
"question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?";
"confirm_m" = "Подтвердить";
"action_successfully" = "Операция успешна";
"apply" = "Применить";
/* User alerts */

View file

@ -52,6 +52,8 @@ openvk:
strict: false
music:
exposeOriginalURLs: true
newsfeed:
ignoredSourcesLimit: 50
wall:
christian: false
anonymousPosting: