Use PeerList for sessions list (wip).

This commit is contained in:
John Preston 2021-11-30 20:38:47 +04:00
parent 7bf557971e
commit 168711b352
2 changed files with 316 additions and 230 deletions

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/algorithm.h" #include "base/algorithm.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
#include "boxes/self_destruction_box.h" #include "boxes/self_destruction_box.h"
#include "boxes/peer_lists_box.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -57,6 +58,50 @@ enum class Type {
Other, Other,
}; };
class Row;
class RowDelegate {
public:
virtual void rowUpdateRow(not_null<Row*> row) = 0;
};
class Row final : public PeerListRow {
public:
Row(not_null<RowDelegate*> delegate, const EntryData &data);
void update(const EntryData &data);
void updateName(const QString &name);
[[nodiscard]] EntryData data() const;
QString generateName() override;
QString generateShortName() override;
PaintRoundImageCallback generatePaintUserpicCallback() override;
int elementsCount() const override;
QRect elementGeometry(int element, int outerWidth) const override;
bool elementDisabled(int element) const override;
bool elementOnlySelect(int element) const override;
void elementAddRipple(
int element,
QPoint point,
Fn<void()> updateCallback) override;
void elementsStopLastRipple() override;
void elementsPaint(
Painter &p,
int outerWidth,
bool selected,
int selectedElement) override;
private:
const not_null<RowDelegate*> _delegate;
Ui::Text::String _location;
Type _type = Type::Other;
EntryData _data;
QImage _userpic;
};
void RenameBox(not_null<Ui::GenericBox*> box) { void RenameBox(not_null<Ui::GenericBox*> box) {
box->setTitle(tr::lng_settings_rename_device_title()); box->setTitle(tr::lng_settings_rename_device_title());
@ -255,7 +300,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
} }
[[nodiscard]] QImage GenerateUserpic(Type type) { [[nodiscard]] QImage GenerateUserpic(Type type) {
const auto size = st::sessionUserpicSize; const auto size = st::sessionListItem.photoSize;
const auto full = size * style::DevicePixelRatio(); const auto full = size * style::DevicePixelRatio();
const auto rect = QRect(0, 0, size, size); const auto rect = QRect(0, 0, size, size);
@ -440,6 +485,125 @@ void SessionInfoBox(
} }
} }
Row::Row(not_null<RowDelegate*> delegate, const EntryData &data)
: PeerListRow(data.hash)
, _delegate(delegate)
, _location(st::defaultTextStyle, LocationAndDate(data))
, _type(TypeFromEntry(data))
, _data(data)
, _userpic(GenerateUserpic(_type)) {
setCustomStatus(_data.info);
}
void Row::update(const EntryData &data) {
_data = data;
setCustomStatus(_data.info);
refreshName(st::sessionListItem);
_location.setText(st::defaultTextStyle, LocationAndDate(_data));
_delegate->rowUpdateRow(this);
}
void Row::updateName(const QString &name) {
_data.name = name;
refreshName(st::sessionListItem);
_delegate->rowUpdateRow(this);
}
EntryData Row::data() const {
return _data;
}
QString Row::generateName() {
return _data.name;
}
QString Row::generateShortName() {
return generateName();
}
PaintRoundImageCallback Row::generatePaintUserpicCallback() {
return [=](
Painter &p,
int x,
int y,
int outerWidth,
int size) {
p.drawImage(x, y, _userpic);
};
}
int Row::elementsCount() const {
return 2;
}
QRect Row::elementGeometry(int element, int outerWidth) const {
switch (element) {
case 1: {
return QRect(
st::sessionListItem.namePosition.x(),
st::sessionLocationTop,
outerWidth,
st::normalFont->height);
} break;
case 2: {
const auto size = QSize(
st::sessionListThreeDotsIcon.width(),
st::sessionListThreeDotsIcon.height());
const auto margins = QMargins(
0,
(st::sessionListItem.height - size.height()) / 2,
st::sessionListThreeDotsSkip,
0);
const auto right = margins.right();
const auto top = margins.top();
const auto left = outerWidth - right - size.width();
return QRect(QPoint(left, top), size);
} break;
}
return QRect();
}
bool Row::elementDisabled(int element) const {
return !id() || (element == 1);
}
bool Row::elementOnlySelect(int element) const {
return false;
}
void Row::elementAddRipple(
int element,
QPoint point,
Fn<void()> updateCallback) {
}
void Row::elementsStopLastRipple() {
}
void Row::elementsPaint(
Painter &p,
int outerWidth,
bool selected,
int selectedElement) {
if (id()) {
const auto geometry = elementGeometry(2, outerWidth);
const auto &icon = (selectedElement == 2)
? st::sessionListThreeDotsIconOver
: st::sessionListThreeDotsIcon;
icon.paint(p, geometry.x(), geometry.y(), outerWidth);
}
p.setFont(st::msgFont);
p.setPen(st::sessionInfoFg);
const auto locationLeft = st::sessionListItem.namePosition.x();
const auto available = outerWidth - locationLeft;
_location.drawLeftElided(
p,
locationLeft,
st::sessionLocationTop,
available,
outerWidth);
}
} // namespace } // namespace
class SessionsContent : public Ui::RpWidget { class SessionsContent : public Ui::RpWidget {
@ -455,25 +619,13 @@ protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
private: private:
struct Entry {
Entry() = default;
explicit Entry(const EntryData &entry);
EntryData data;
bool incomplete = false;
Type type = Type::Other;
TimeId activeTime = 0;
Ui::Text::String name, info, location;
QImage userpic;
};
struct Full { struct Full {
Entry current; EntryData current;
std::vector<Entry> incomplete; std::vector<EntryData> incomplete;
std::vector<Entry> list; std::vector<EntryData> list;
}; };
class Inner; class Inner;
class List; class ListController;
void shortPollSessions(); void shortPollSessions();
void parse(const Api::Authorizations::List &list); void parse(const Api::Authorizations::List &list);
@ -495,44 +647,39 @@ private:
}; };
class SessionsContent::List : public Ui::RpWidget { class SessionsContent::ListController final
: public PeerListController
, public RowDelegate
, public base::has_weak_ptr {
public: public:
List(QWidget *parent); explicit ListController(not_null<Main::Session*> session);
void showData(gsl::span<const Entry> items); Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowElementClicked(not_null<PeerListRow*> row, int element) override;
void rowUpdateRow(not_null<Row*> row) override;
void showData(gsl::span<const EntryData> items);
rpl::producer<int> itemsCount() const; rpl::producer<int> itemsCount() const;
rpl::producer<uint64> terminateRequests() const; rpl::producer<uint64> terminateRequests() const;
[[nodiscard]] rpl::producer<EntryData> showRequests() const; [[nodiscard]] rpl::producer<EntryData> showRequests() const;
void terminating(uint64 hash, bool terminating); [[nodiscard]] static std::unique_ptr<ListController> Add(
not_null<Ui::VerticalLayout*> container,
protected: not_null<Main::Session*> session,
void resizeEvent(QResizeEvent *e) override; style::margins margins = {});
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
int resizeGetHeight(int newWidth) override;
private: private:
struct RowWidth {
int available = 0;
int info = 0;
};
void subscribeToCustomDeviceModel(); void subscribeToCustomDeviceModel();
void computeRowWidth();
RowWidth _rowWidth; const not_null<Main::Session*> _session;
std::vector<Entry> _items;
std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons;
rpl::event_stream<uint64> _terminateRequests; rpl::event_stream<uint64> _terminateRequests;
rpl::event_stream<int> _itemsCount; rpl::event_stream<int> _itemsCount;
rpl::event_stream<EntryData> _showRequests; rpl::event_stream<EntryData> _showRequests;
int _pressed = -1;
}; };
class SessionsContent::Inner : public Ui::RpWidget { class SessionsContent::Inner : public Ui::RpWidget {
@ -546,32 +693,20 @@ public:
[[nodiscard]] rpl::producer<EntryData> showRequests() const; [[nodiscard]] rpl::producer<EntryData> showRequests() const;
[[nodiscard]] rpl::producer<uint64> terminateOne() const; [[nodiscard]] rpl::producer<uint64> terminateOne() const;
[[nodiscard]] rpl::producer<> terminateAll() const; [[nodiscard]] rpl::producer<> terminateAll() const;
[[nodiscard]] rpl::producer<> renameCurrentRequests() const;
void terminatingOne(uint64 hash, bool terminating);
private: private:
void setupContent(); void setupContent();
const not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
QPointer<List> _current; std::unique_ptr<ListController> _current;
QPointer<Ui::SettingsButton> _terminateAll; QPointer<Ui::SettingsButton> _terminateAll;
QPointer<List> _incomplete; std::unique_ptr<ListController> _incomplete;
QPointer<List> _list; std::unique_ptr<ListController> _list;
rpl::variable<int> _ttlDays; rpl::variable<int> _ttlDays;
}; };
SessionsContent::Entry::Entry(const EntryData &entry) //, location(st::sessionInfoStyle, LocationAndDate(entry))
: data(entry)
, incomplete(entry.incomplete)
, type(TypeFromEntry(entry))
, activeTime(entry.activeTime)
, name(st::sessionNameStyle, entry.name)
, info(st::sessionInfoStyle, entry.info)
, location(st::sessionInfoStyle, LocationAndDate(entry))
, userpic(GenerateUserpic(type)) {
};
SessionsContent::SessionsContent( SessionsContent::SessionsContent(
QWidget*, QWidget*,
@ -629,20 +764,19 @@ void SessionsContent::parse(const Api::Authorizations::List &list) {
} }
_data = Full(); _data = Full();
for (const auto &auth : list) { for (const auto &auth : list) {
auto entry = Entry(auth); if (!auth.hash) {
if (!entry.data.hash) { _data.current = auth;
_data.current = std::move(entry); } else if (auth.incomplete) {
} else if (entry.incomplete) { _data.incomplete.push_back(auth);
_data.incomplete.push_back(std::move(entry));
} else { } else {
_data.list.push_back(std::move(entry)); _data.list.push_back(auth);
} }
} }
_loading = false; _loading = false;
ranges::sort(_data.list, std::greater<>(), &Entry::activeTime); ranges::sort(_data.list, std::greater<>(), &EntryData::activeTime);
ranges::sort(_data.incomplete, std::greater<>(), &Entry::activeTime); ranges::sort(_data.incomplete, std::greater<>(), &EntryData::activeTime);
_inner->showData(_data); _inner->showData(_data);
@ -710,13 +844,12 @@ void SessionsContent::terminateOne(uint64 hash) {
if (mtpIsFalse(result)) { if (mtpIsFalse(result)) {
return; return;
} }
_inner->terminatingOne(hash, false); const auto removeByHash = [&](std::vector<EntryData> &list) {
const auto removeByHash = [&](std::vector<Entry> &list) {
list.erase( list.erase(
ranges::remove( ranges::remove(
list, list,
hash, hash,
[](const Entry &entry) { return entry.data.hash; }), [](const EntryData &entry) { return entry.hash; }),
end(list)); end(list));
}; };
removeByHash(_data.incomplete); removeByHash(_data.incomplete);
@ -724,13 +857,11 @@ void SessionsContent::terminateOne(uint64 hash) {
_inner->showData(_data); _inner->showData(_data);
}); });
auto fail = crl::guard(weak, [=](const MTP::Error &error) { auto fail = crl::guard(weak, [=](const MTP::Error &error) {
_inner->terminatingOne(hash, false);
}); });
_authorizations->requestTerminate( _authorizations->requestTerminate(
std::move(done), std::move(done),
std::move(fail), std::move(fail),
hash); hash);
_inner->terminatingOne(hash, true);
}; };
terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now)); terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now));
} }
@ -788,8 +919,10 @@ void SessionsContent::Inner::setupContent() {
Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther); Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther);
}); });
_current = content->add( const auto session = &_controller->session();
object_ptr<List>(content), _current = ListController::Add(
content,
session,
style::margins{ 0, 0, 0, st::sessionCurrentSkip }); style::margins{ 0, 0, 0, st::sessionCurrentSkip });
const auto terminateWrap = content->add( const auto terminateWrap = content->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
@ -814,7 +947,7 @@ void SessionsContent::Inner::setupContent() {
const auto incompleteInner = incompleteWrap->entity(); const auto incompleteInner = incompleteWrap->entity();
AddSkip(incompleteInner, st::sessionSubtitleSkip); AddSkip(incompleteInner, st::sessionSubtitleSkip);
AddSubsectionTitle(incompleteInner, tr::lng_sessions_incomplete()); AddSubsectionTitle(incompleteInner, tr::lng_sessions_incomplete());
_incomplete = incompleteInner->add(object_ptr<List>(incompleteInner)); _incomplete = ListController::Add(incompleteInner, session);
AddSkip(incompleteInner); AddSkip(incompleteInner);
AddDividerText(incompleteInner, tr::lng_sessions_incomplete_about()); AddDividerText(incompleteInner, tr::lng_sessions_incomplete_about());
@ -825,7 +958,7 @@ void SessionsContent::Inner::setupContent() {
const auto listInner = listWrap->entity(); const auto listInner = listWrap->entity();
AddSkip(listInner, st::sessionSubtitleSkip); AddSkip(listInner, st::sessionSubtitleSkip);
AddSubsectionTitle(listInner, tr::lng_sessions_other_header()); AddSubsectionTitle(listInner, tr::lng_sessions_other_header());
_list = listInner->add(object_ptr<List>(listInner)); _list = ListController::Add(listInner, session);
AddSkip(listInner); AddSkip(listInner);
AddDividerText(listInner, tr::lng_sessions_about_apps()); AddDividerText(listInner, tr::lng_sessions_about_apps());
@ -897,165 +1030,113 @@ rpl::producer<EntryData> SessionsContent::Inner::showRequests() const {
_list->showRequests()); _list->showRequests());
} }
void SessionsContent::Inner::terminatingOne(uint64 hash, bool terminating) { SessionsContent::ListController::ListController(
_incomplete->terminating(hash, terminating); not_null<Main::Session*> session)
_list->terminating(hash, terminating); : _session(session) {
} }
SessionsContent::List::List(QWidget *parent) : RpWidget(parent) { Main::Session &SessionsContent::ListController::session() const {
setAttribute(Qt::WA_OpaquePaintEvent); return *_session;
} }
void SessionsContent::List::resizeEvent(QResizeEvent *e) { void SessionsContent::ListController::subscribeToCustomDeviceModel() {
RpWidget::resizeEvent(e);
computeRowWidth();
}
void SessionsContent::List::subscribeToCustomDeviceModel() {
Core::App().settings().deviceModelChanges( Core::App().settings().deviceModelChanges(
) | rpl::start_with_next([=](const QString &model) { ) | rpl::start_with_next([=](const QString &model) {
for (auto &entry : _items) { for (auto i = 0; i != delegate()->peerListFullRowsCount(); ++i) {
if (!entry.data.hash) { const auto row = delegate()->peerListRowAt(i);
entry.name.setText(st::sessionNameStyle, model); if (!row->id()) {
static_cast<Row*>(row.get())->updateName(model);
} }
} }
update();
}, lifetime()); }, lifetime());
} }
void SessionsContent::List::showData(gsl::span<const Entry> items) { void SessionsContent::ListController::prepare() {
computeRowWidth(); }
auto buttons = base::take(_terminateButtons); void SessionsContent::ListController::rowClicked(
_items.clear(); not_null<PeerListRow*> row) {
_items.insert(begin(_items), items.begin(), items.end()); _showRequests.fire_copy(static_cast<Row*>(row.get())->data());
for (const auto &entry : _items) { }
const auto hash = entry.data.hash;
if (!hash) { void SessionsContent::ListController::rowElementClicked(
not_null<PeerListRow*> row,
int element) {
if (element == 1) {
if (const auto hash = static_cast<Row*>(row.get())->data().hash) {
_terminateRequests.fire_copy(hash);
}
}
}
void SessionsContent::ListController::rowUpdateRow(not_null<Row*> row) {
delegate()->peerListUpdateRow(row);
}
void SessionsContent::ListController::showData(
gsl::span<const EntryData> items) {
auto index = 0;
auto positions = base::flat_map<uint64, int>();
positions.reserve(items.size());
for (const auto &entry : items) {
const auto id = entry.hash;
positions.emplace(id, index++);
if (const auto row = delegate()->peerListFindRow(id)) {
static_cast<Row*>(row)->update(entry);
} else {
delegate()->peerListAppendRow(
std::make_unique<Row>(this, entry));
}
}
for (auto i = 0; i != delegate()->peerListFullRowsCount();) {
const auto row = delegate()->peerListRowAt(i);
if (positions.contains(row->id())) {
++i;
continue; continue;
} }
const auto button = [&] { delegate()->peerListRemoveRow(row);
const auto i = buttons.find(hash);
return _terminateButtons.emplace(
hash,
(i != end(buttons)
? std::move(i->second)
: std::make_unique<Ui::IconButton>(
this,
st::sessionTerminate))).first->second.get();
}();
button->setClickedCallback([=] {
_terminateRequests.fire_copy(hash);
});
button->show();
const auto number = _terminateButtons.size() - 1;
widthValue(
) | rpl::start_with_next([=] {
button->moveToRight(
st::sessionTerminateSkip,
(number * st::sessionHeight + st::sessionTerminateTop));
}, lifetime());
} }
resizeToWidth(width()); delegate()->peerListSortRows([&](
_itemsCount.fire(_items.size()); const PeerListRow &a,
const PeerListRow &b) {
return positions[a.id()] < positions[b.id()];
});
delegate()->peerListRefreshRows();
_itemsCount.fire(delegate()->peerListFullRowsCount());
} }
rpl::producer<EntryData> SessionsContent::List::showRequests() const { rpl::producer<int> SessionsContent::ListController::itemsCount() const {
return _showRequests.events(); return _itemsCount.events_starting_with(
delegate()->peerListFullRowsCount());
} }
rpl::producer<int> SessionsContent::List::itemsCount() const { rpl::producer<uint64> SessionsContent::ListController::terminateRequests() const {
return _itemsCount.events_starting_with(_items.size());
}
rpl::producer<uint64> SessionsContent::List::terminateRequests() const {
return _terminateRequests.events(); return _terminateRequests.events();
} }
void SessionsContent::List::terminating(uint64 hash, bool terminating) { rpl::producer<EntryData> SessionsContent::ListController::showRequests() const {
const auto i = _terminateButtons.find(hash); return _showRequests.events();
if (i != _terminateButtons.cend()) {
if (terminating) {
i->second->clearState();
i->second->hide();
} else {
i->second->show();
}
}
} }
int SessionsContent::List::resizeGetHeight(int newWidth) { auto SessionsContent::ListController::Add(
return _items.size() * st::sessionHeight; not_null<Ui::VerticalLayout*> container,
} not_null<Main::Session*> session,
style::margins margins)
void SessionsContent::List::computeRowWidth() { -> std::unique_ptr<ListController> {
const auto available = width() auto &lifetime = container->lifetime();
- st::sessionPadding.left() const auto delegate = lifetime.make_state<
- st::sessionTerminateSkip; PeerListContentDelegateSimple
_rowWidth = { >();
.available = available, auto controller = std::make_unique<ListController>(session);
.info = available - st::sessionTerminate.width, controller->setStyleOverrides(&st::sessionList);
}; const auto content = container->add(
} object_ptr<PeerListContent>(
container,
void SessionsContent::List::paintEvent(QPaintEvent *e) { controller.get()),
QRect r(e->rect()); margins);
Painter p(this); delegate->setContent(content);
controller->setDelegate(delegate);
p.fillRect(r, st::boxBg); return controller;
p.setFont(st::linkFont);
const auto count = int(_items.size());
const auto from = floorclamp(r.y(), st::sessionHeight, 0, count);
const auto till = ceilclamp(
r.y() + r.height(),
st::sessionHeight,
0,
count);
const auto available = _rowWidth.available;
const auto x = st::sessionPadding.left();
const auto y = st::sessionPadding.top();
const auto w = width();
p.translate(0, from * st::sessionHeight);
for (auto i = from; i != till; ++i) {
const auto &entry = _items[i];
p.drawImage(st::sessionUserpicPosition, entry.userpic);
const auto nameW = _rowWidth.info;
const auto infoW = entry.data.hash ? _rowWidth.info : available;
p.setPen(st::sessionNameFg);
entry.name.drawLeftElided(p, x, y, nameW, w);
p.setPen(st::boxTextFg);
entry.info.drawLeftElided(p, x, y + st::sessionInfoTop, infoW, w);
p.setPen(st::sessionInfoFg);
entry.location.drawLeftElided(
p,
x,
y + st::sessionLocationTop,
available,
w);
p.translate(0, st::sessionHeight);
}
}
void SessionsContent::List::mousePressEvent(QMouseEvent *e) {
const auto index = e->pos().y() / st::sessionHeight;
_pressed = (index >= 0 && index < _items.size()) ? index : -1;
}
void SessionsContent::List::mouseReleaseEvent(QMouseEvent *e) {
const auto index = e->pos().y() / st::sessionHeight;
const auto released = (index >= 0 && index < _items.size()) ? index : -1;
if (released == _pressed && released >= 0) {
_showRequests.fire_copy(_items[released].data);
}
_pressed = -1;
} }
SessionsBox::SessionsBox( SessionsBox::SessionsBox(

View file

@ -249,23 +249,13 @@ sessionsTerminateAll: SettingsButton(defaultSettingsButton) {
} }
sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }};
sessionsTerminateAllIconLeft: 30px; sessionsTerminateAllIconLeft: 30px;
sessionHeight: 84px; sessionLocationTop: 54px;
sessionInfoTop: 21px;
sessionLocationTop: 43px;
sessionCurrentSkip: 8px; sessionCurrentSkip: 8px;
sessionSubtitleSkip: 14px; sessionSubtitleSkip: 14px;
sessionPadding: margins(77px, 11px, 22px, 0px); sessionLocationFg: windowSubTextFg;
sessionNameFont: msgNameFont;
sessionNameFg: boxTextFg;
sessionWhenFont: msgDateFont;
sessionWhenFg: windowSubTextFg;
sessionInfoFont: msgFont;
sessionInfoFg: windowSubTextFg; sessionInfoFg: windowSubTextFg;
sessionTerminateTop: 9px; sessionTerminateTop: 9px;
sessionTerminateSkip: 22px; sessionTerminateSkip: 22px;
sessionUserpicSize: 42px;
sessionUserpicPosition: point(21px, 10px);
sessionNamePadding: margins(0px, 0px, 5px, 0px);
sessionTerminate: IconButton { sessionTerminate: IconButton {
width: 20px; width: 20px;
height: 20px; height: 20px;
@ -280,15 +270,6 @@ sessionTerminate: IconButton {
color: windowBgOver; color: windowBgOver;
} }
} }
sessionNameStyle: TextStyle(defaultTextStyle) {
font: sessionNameFont;
}
sessionWhenStyle: TextStyle(defaultTextStyle) {
font: sessionWhenFont;
}
sessionInfoStyle: TextStyle(defaultTextStyle) {
font: sessionInfoFont;
}
sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }};
sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }};
sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }};
@ -327,3 +308,27 @@ sessionValueLabel: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg; textFg: windowSubTextFg;
} }
sessionValueSkip: 8px; sessionValueSkip: 8px;
sessionListItem: PeerListItem(defaultPeerListItem) {
button: OutlineButton(defaultPeerListButton) {
font: normalFont;
padding: margins(11px, 5px, 11px, 5px);
}
height: 84px;
photoPosition: point(21px, 10px);
nameStyle: TextStyle(defaultTextStyle) {
font: msgNameFont;
}
namePosition: point(77px, 11px);
statusPosition: point(77px, 32px);
photoSize: 42px;
statusFg: boxTextFg;
statusFgOver: boxTextFg;
}
sessionList: PeerList(defaultPeerList) {
item: sessionListItem;
padding: margins(0px, 4px, 0px, 0px);
}
sessionListThreeDotsIcon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }};
sessionListThreeDotsIconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }};
sessionListThreeDotsSkip: 12px;