Files
amnezia-client/client/ui/models/serversModel.cpp
T
NickVs2015andvkamn b1a37b3779 feat: add brandings (#2839)
* feat: move amnezia hardcode from sources to cmake

* feat: move amnezia hardcode from sources to cmake

* refactor: remove prem v1 migration

* chore: move more branding

* fix: use host-matching architecture for iOS simulator builds

x86_64 was hardcoded; Apple Silicon Macs run arm64 simulators and Xcode 26
has no Rosetta simulator destinations, so simulators showed as unsupported.

* fix: mark the branded media assets catalog as a bundle resource

MACOSX_PACKAGE_LOCATION was set on the upstream Media.xcassets path while
target_sources uses CLIENT_IOS_MEDIA_ASSETS_PATH, so a branded catalog was
never compiled by actool and the app got a blank icon.

* fix: parametrize the Swift app-group identifier used by Log.swift

Log.swift hardcoded Amnezia's own app-group instead of the branded one,
since Swift -D flags can't carry the string value like the ObjC++ side
does. Generate a small Swift constants file instead, same pattern as
version.h.in, wired into all four targets that compile Log.swift.

* fix: use APPLICATION_NAME instead of hardcoded Amnezia in log line format

Every log line printed "Amnezia" regardless of branding; the log file name
two lines above this already used APPLICATION_NAME correctly.

* fix: restore branding fixes dropped by rebase, rename swiftInterop to swiftBridge

* fix: move more branding variables to cmake

* feat: move macOS branding from sources to cmake

- macos/app/Info.plist.in: app group, camera usage text, UTI ids and
  descriptions now come from CLIENT_APPLICATION_NAME / BUILD_* variables,
  mirroring ios/app/Info.plist.in
- macos/networkextension/Info.plist.in: bundle names and app group ids are
  templated; the plist is configured by CMake into the binary dir because
  Xcode only expands its own $(...) settings
- new knobs: CLIENT_MACOS_MEDIA_ASSETS_PATH, CLIENT_MACOS_APP_ICNS_PATH,
  CLIENT_MACOS_PROVISIONING_PROFILE_SPECIFIER(_DEBUG),
  CLIENT_MACOS_NE_PROVISIONING_PROFILE_SPECIFIER(_DEBUG)

* fix: resolve macOS pf template paths relative to the client source dir

CMAKE_SOURCE_DIR points at the wrapper project when the client is built as a
subproject, so the pf template lookup failed there.

* chore: separate brandings defaults

---------

Co-authored-by: vkamn <vk@amnezia.org>
2026-08-18 20:41:45 +08:00

156 lines
4.3 KiB
C++

#include "serversModel.h"
#include "core/models/serverDescription.h"
#include <QHash>
#include <QSet>
#include <QJsonDocument>
#include "core/utils/serverConfigUtils.h"
#include "core/utils/constants/apiKeys.h"
#include "core/utils/constants/apiConstants.h"
#include "core/utils/selfhosted/sshSession.h"
#include "core/utils/networkUtilities.h"
#if defined(Q_OS_IOS) || defined(MACOS_NE)
#include "core/utils/swiftBridge.h"
#endif
#include "core/utils/api/apiUtils.h"
using namespace amnezia;
namespace {
int rowForServerId(const QVector<ServerDescription> &descriptions, const QString &serverId)
{
if (serverId.isEmpty()) {
return -1;
}
for (int i = 0; i < descriptions.size(); ++i) {
if (descriptions.at(i).serverId == serverId) {
return i;
}
}
return -1;
}
} // namespace
ServersModel::ServersModel(QObject *parent) : QAbstractListModel(parent)
{
}
int ServersModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return static_cast<int>(m_descriptions.size());
}
QVariant ServersModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_descriptions.size())) {
return QVariant();
}
const ServerDescription &row = m_descriptions.at(index.row());
const int configVersion = row.configVersion;
switch (role) {
case NameRole:
return row.serverName;
case ServerDescriptionRole:
return configVersion ? row.baseDescription : (row.baseDescription + row.hostName);
case HostNameRole:
return row.hostName;
case ServerIdRole:
return row.serverId;
case CredentialsLoginRole:
return serverCredentials(index.row()).userName;
case IsDefaultRole:
return row.serverId == m_defaultServerId;
case HasWriteAccessRole:
return row.hasWriteAccess;
case DefaultContainerRole:
return QVariant::fromValue(row.defaultContainer);
case HasInstalledContainers:
return row.hasInstalledVpnContainers;
case IsServerFromGatewayApiRole:
return row.isServerFromGatewayApi;
case IsSubscriptionExpiredRole:
return row.isSubscriptionExpired;
case IsSubscriptionExpiringSoonRole:
return row.isSubscriptionExpiringSoon;
}
return QVariant();
}
QVariant ServersModel::data(const int index, int role) const
{
QModelIndex modelIndex = this->index(index);
return data(modelIndex, role);
}
void ServersModel::updateModel(const QVector<ServerDescription> &descriptions,
const QString &defaultServerId)
{
beginResetModel();
m_descriptions = descriptions;
m_defaultServerId = defaultServerId;
endResetModel();
}
void ServersModel::setDefaultServerId(const QString &serverId)
{
if (m_defaultServerId == serverId) {
return;
}
const int oldIndex = rowForServerId(m_descriptions, m_defaultServerId);
const int newIndex = rowForServerId(m_descriptions, serverId);
m_defaultServerId = serverId;
const QVector<int> roles = { IsDefaultRole };
if (oldIndex >= 0 && oldIndex < m_descriptions.size()) {
emit dataChanged(this->index(oldIndex), this->index(oldIndex), roles);
}
if (newIndex >= 0 && newIndex < m_descriptions.size()) {
emit dataChanged(this->index(newIndex), this->index(newIndex), roles);
}
}
QHash<int, QByteArray> ServersModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[ServerDescriptionRole] = "serverDescription";
roles[HostNameRole] = "hostName";
roles[ServerIdRole] = "serverId";
roles[CredentialsLoginRole] = "credentialsLogin";
roles[IsDefaultRole] = "isDefault";
roles[HasWriteAccessRole] = "hasWriteAccess";
roles[DefaultContainerRole] = "defaultContainer";
roles[HasInstalledContainers] = "hasInstalledContainers";
roles[IsServerFromGatewayApiRole] = "isServerFromGatewayApi";
roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired";
roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon";
return roles;
}
ServerCredentials ServersModel::serverCredentials(int index) const
{
if (index < 0 || index >= m_descriptions.size()) {
return ServerCredentials();
}
return m_descriptions.at(index).selfHostedSshCredentials;
}