[ui] Add auto-mount on first launch functionality #52
This commit is contained in:
@@ -10,3 +10,5 @@ rsync -av --progress ${CURRENT_DIR}/${PROJECT_NAME}/${PROJECT_NAME}_test/test_in
|
|||||||
${PROJECT_DIST_DIR}/test_input/
|
${PROJECT_DIST_DIR}/test_input/
|
||||||
|
|
||||||
rsync -av --progress ${CURRENT_DIR}/icon.ico ${PROJECT_DIST_DIR}/icon.ico
|
rsync -av --progress ${CURRENT_DIR}/icon.ico ${PROJECT_DIST_DIR}/icon.ico
|
||||||
|
|
||||||
|
rsync -av --progress ${CURRENT_DIR}/assets/blue/logo.iconset/icon_128x128.png ${PROJECT_DIST_DIR}/repertory.png
|
||||||
|
@@ -28,6 +28,17 @@
|
|||||||
namespace repertory {
|
namespace repertory {
|
||||||
class i_provider;
|
class i_provider;
|
||||||
|
|
||||||
|
struct create_autostart_opts final {
|
||||||
|
std::string app_name;
|
||||||
|
std::optional<std::string> comment;
|
||||||
|
bool enabled{true};
|
||||||
|
std::vector<std::string> exec_args;
|
||||||
|
std::string exec_path;
|
||||||
|
std::optional<std::string> icon_path;
|
||||||
|
std::vector<std::string> only_show_in;
|
||||||
|
bool terminal{false};
|
||||||
|
};
|
||||||
|
|
||||||
class lock_data final {
|
class lock_data final {
|
||||||
public:
|
public:
|
||||||
lock_data(provider_type prov, std::string_view unique_id);
|
lock_data(provider_type prov, std::string_view unique_id);
|
||||||
@@ -71,6 +82,8 @@ public:
|
|||||||
-> bool;
|
-> bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] auto create_autostart_entry(create_autostart_opts opts) -> bool;
|
||||||
|
|
||||||
[[nodiscard]] auto create_meta_attributes(
|
[[nodiscard]] auto create_meta_attributes(
|
||||||
std::uint64_t accessed_date, std::uint32_t attributes,
|
std::uint64_t accessed_date, std::uint32_t attributes,
|
||||||
std::uint64_t changed_date, std::uint64_t creation_date, bool directory,
|
std::uint64_t changed_date, std::uint64_t creation_date, bool directory,
|
||||||
@@ -81,6 +94,8 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] auto provider_meta_handler(i_provider &provider, bool directory,
|
[[nodiscard]] auto provider_meta_handler(i_provider &provider, bool directory,
|
||||||
const api_file &file) -> api_error;
|
const api_file &file) -> api_error;
|
||||||
|
|
||||||
|
[[nodiscard]] auto remove_autostart_entry(std::string_view name) -> bool;
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|
||||||
#endif // !defined(_WIN32)
|
#endif // !defined(_WIN32)
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#include <utils/config.hpp>
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
@@ -34,6 +35,79 @@
|
|||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
#include "utils/unix.hpp"
|
#include "utils/unix.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
[[nodiscard]] auto get_autostart_dir() -> std::string {
|
||||||
|
auto config = repertory::utils::get_environment_variable("XDG_CONFIG_HOME");
|
||||||
|
if (config.empty()) {
|
||||||
|
config = repertory::utils::path::combine(
|
||||||
|
repertory::utils::get_environment_variable("HOME"), {".config"});
|
||||||
|
}
|
||||||
|
|
||||||
|
return repertory::utils::path::combine(config, {"autostart"});
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto join_args_for_exec(const std::vector<std::string> &args)
|
||||||
|
-> std::string {
|
||||||
|
std::string str;
|
||||||
|
for (const auto &arg : args) {
|
||||||
|
if (not str.empty()) {
|
||||||
|
str += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
auto needs_quotes = arg.find_first_of(" \t\"'\\$`") != std::string::npos;
|
||||||
|
if (needs_quotes) {
|
||||||
|
str += '"';
|
||||||
|
for (const auto &cur_ch : arg) {
|
||||||
|
if (cur_ch == '"' || cur_ch == '\\') {
|
||||||
|
str += '\\';
|
||||||
|
}
|
||||||
|
str += cur_ch;
|
||||||
|
}
|
||||||
|
str += '"';
|
||||||
|
} else {
|
||||||
|
str += arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto sanitize_basename(std::string_view app_name) -> std::string {
|
||||||
|
std::string out;
|
||||||
|
out.reserve(app_name.size());
|
||||||
|
for (const auto &cur_ch : app_name) {
|
||||||
|
if ((cur_ch >= 'a' && cur_ch <= 'z') || (cur_ch >= '0' && cur_ch <= '9') ||
|
||||||
|
(cur_ch == '-' || cur_ch == '_')) {
|
||||||
|
out.push_back(cur_ch);
|
||||||
|
} else if (cur_ch >= 'A' && cur_ch <= 'Z') {
|
||||||
|
out.push_back(static_cast<char>(cur_ch - 'A' + 'a'));
|
||||||
|
} else {
|
||||||
|
out.push_back('-'); // replace spaces/symbols
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string collapsed;
|
||||||
|
collapsed.reserve(out.size());
|
||||||
|
bool prev_dash = false;
|
||||||
|
for (const auto &cur_ch : out) {
|
||||||
|
if (cur_ch == '-') {
|
||||||
|
if (not prev_dash) {
|
||||||
|
collapsed.push_back(cur_ch);
|
||||||
|
}
|
||||||
|
prev_dash = true;
|
||||||
|
} else {
|
||||||
|
collapsed.push_back(cur_ch);
|
||||||
|
prev_dash = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collapsed.empty()) {
|
||||||
|
collapsed = "app";
|
||||||
|
}
|
||||||
|
return collapsed;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace repertory {
|
namespace repertory {
|
||||||
lock_data::lock_data(provider_type prov, std::string_view unique_id)
|
lock_data::lock_data(provider_type prov, std::string_view unique_id)
|
||||||
: mutex_id_(create_lock_id(prov, unique_id)) {
|
: mutex_id_(create_lock_id(prov, unique_id)) {
|
||||||
@@ -254,6 +328,102 @@ auto provider_meta_handler(i_provider &provider, bool directory,
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static auto desktop_file_path_for(std::string_view app_name)
|
||||||
|
-> std::string {
|
||||||
|
return utils::path::combine(get_autostart_dir(),
|
||||||
|
{sanitize_basename(app_name) + ".desktop"});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto create_autostart_entry(create_autostart_opts opts) -> bool {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
auto file = desktop_file_path_for(opts.app_name);
|
||||||
|
if (utils::file::file{file}.exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = get_autostart_dir();
|
||||||
|
if (dir.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not utils::file::directory{dir}.create_directory()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto exec_line = opts.exec_path;
|
||||||
|
if (not opts.exec_args.empty()) {
|
||||||
|
exec_line += ' ';
|
||||||
|
exec_line += join_args_for_exec(opts.exec_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream out(file, std::ios::binary | std::ios::trunc);
|
||||||
|
if (not out) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "[Desktop Entry]\n";
|
||||||
|
out << "Type=Application\n";
|
||||||
|
out << "Version=1.0\n";
|
||||||
|
out << "Name=" << opts.app_name << "\n";
|
||||||
|
out << "Exec=" << exec_line << "\n";
|
||||||
|
out << "Terminal=" << (opts.terminal ? "true" : "false") << "\n";
|
||||||
|
|
||||||
|
if (opts.comment && !opts.comment->empty()) {
|
||||||
|
out << "Comment=" << *opts.comment << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.icon_path && !opts.icon_path->empty()) {
|
||||||
|
out << "Icon=" << *opts.icon_path << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts.only_show_in.empty()) {
|
||||||
|
out << "OnlyShowIn=";
|
||||||
|
for (std::size_t idx = 0U; idx < opts.only_show_in.size(); ++idx) {
|
||||||
|
if (idx != 0U) {
|
||||||
|
out << ';';
|
||||||
|
}
|
||||||
|
out << opts.only_show_in[idx];
|
||||||
|
}
|
||||||
|
out << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not opts.enabled) {
|
||||||
|
out << "X-GNOME-Autostart-enabled=false\n";
|
||||||
|
out << "Hidden=true\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
if (not out) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
chmod(file.c_str(), 0644);
|
||||||
|
#endif // defined(__linux__) || defined(__APPLE__)
|
||||||
|
|
||||||
|
utils::error::handle_info(
|
||||||
|
function_name, fmt::format("created auto-start entry|path|{}", file));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto remove_autostart_entry(std::string_view name) -> bool {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
auto file = desktop_file_path_for(name);
|
||||||
|
if (not utils::file::file{file}.exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ret = utils::file::file{file}.remove();
|
||||||
|
if (ret) {
|
||||||
|
utils::error::handle_info(
|
||||||
|
function_name, fmt::format("removed auto-start entry|path|{}", file));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|
||||||
#endif //_WIN32
|
#endif //_WIN32
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#include "ui/mgmt_app_config.hpp"
|
#include "ui/mgmt_app_config.hpp"
|
||||||
|
|
||||||
#include "app_config.hpp"
|
#include "app_config.hpp"
|
||||||
|
#include "platform/platform.hpp"
|
||||||
#include "utils/error_utils.hpp"
|
#include "utils/error_utils.hpp"
|
||||||
#include "utils/file.hpp"
|
#include "utils/file.hpp"
|
||||||
#include "utils/path.hpp"
|
#include "utils/path.hpp"
|
||||||
@@ -103,6 +104,7 @@ mgmt_app_config::mgmt_app_config(bool hidden, bool launch_only)
|
|||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_auto_start(get_auto_start());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +112,8 @@ mgmt_app_config::mgmt_app_config(bool hidden, bool launch_only)
|
|||||||
function_name, utils::get_last_error_code(),
|
function_name, utils::get_last_error_code(),
|
||||||
fmt::format("failed to read file|{}", config_file));
|
fmt::format("failed to read file|{}", config_file));
|
||||||
save();
|
save();
|
||||||
|
|
||||||
|
set_auto_start(get_auto_start());
|
||||||
} catch (const std::exception &ex) {
|
} catch (const std::exception &ex) {
|
||||||
utils::error::raise_error(
|
utils::error::raise_error(
|
||||||
function_name, ex, fmt::format("failed to read file|{}", config_file));
|
function_name, ex, fmt::format("failed to read file|{}", config_file));
|
||||||
@@ -207,6 +211,37 @@ void mgmt_app_config::set_mount_location(provider_type prov,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mgmt_app_config::set_auto_start(bool auto_start) {
|
void mgmt_app_config::set_auto_start(bool auto_start) {
|
||||||
|
REPERTORY_USES_FUNCTION_NAME();
|
||||||
|
|
||||||
|
auto current_directory = std::filesystem::current_path();
|
||||||
|
if (utils::file::change_to_process_directory()) {
|
||||||
|
#if defined(__linux__)
|
||||||
|
if (auto_start) {
|
||||||
|
create_autostart_opts opts{};
|
||||||
|
opts.app_name = "repertory";
|
||||||
|
opts.comment = "Mount utility for AWS S3 and Sia";
|
||||||
|
opts.exec_args = {"-ui -lo"};
|
||||||
|
opts.exec_path = utils::path::combine(".", {"repertory"});
|
||||||
|
opts.icon_path = utils::path::combine(".", {"repertory.png"});
|
||||||
|
opts.terminal = true;
|
||||||
|
|
||||||
|
if (not create_autostart_entry(opts)) {
|
||||||
|
utils::error::raise_error(
|
||||||
|
function_name, utils::get_last_error_code(),
|
||||||
|
fmt::format("failed to create auto-start entry"));
|
||||||
|
}
|
||||||
|
} else if (not remove_autostart_entry("repertory")) {
|
||||||
|
utils::error::raise_error(
|
||||||
|
function_name, utils::get_last_error_code(),
|
||||||
|
fmt::format("failed to remove auto-start entry"));
|
||||||
|
}
|
||||||
|
#endif // defined(__linux__)
|
||||||
|
} else {
|
||||||
|
utils::error::raise_error(function_name, utils::get_last_error_code(),
|
||||||
|
fmt::format("failed to change directory"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::current_path(current_directory);
|
||||||
if (auto_start_ == auto_start) {
|
if (auto_start_ == auto_start) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user