From 3aab04a2452b126adf6e9d5d793d8484aa0073f7 Mon Sep 17 00:00:00 2001 From: Nariman Jelveh Date: Sun, 24 Mar 2024 00:41:27 -0700 Subject: [PATCH] First version of Settings --- src/UI/Settings/UIWindowSettings.js | 415 ++++++++++++++++++++++++++++ src/UI/UIDesktop.js | 12 +- src/css/style.css | 297 +++++++++++++++++++- src/globals.js | 5 +- src/helpers.js | 2 +- src/i18n/i18n.js | 2 +- src/i18n/i18nChangeLanguage.js | 4 +- src/i18n/translations/ar.js | 1 + src/i18n/translations/bn.js | 1 + src/i18n/translations/da.js | 1 + src/i18n/translations/de.js | 1 + src/i18n/translations/emoji.js | 1 + src/i18n/translations/en.js | 5 + src/i18n/translations/es.js | 1 + src/i18n/translations/fa.js | 1 + src/i18n/translations/fi.js | 1 + src/i18n/translations/fr.js | 1 + src/i18n/translations/hy.js | 1 + src/i18n/translations/it.js | 1 + src/i18n/translations/ko.js | 1 + src/i18n/translations/nb.js | 1 + src/i18n/translations/nn.js | 1 + src/i18n/translations/ro.js | 1 + src/i18n/translations/sv.js | 1 + src/i18n/translations/th.js | 1 + src/i18n/translations/ur.js | 1 + src/i18n/translations/zh.js | 1 + src/icons/cube-outline.svg | 1 + src/icons/language.svg | 1 + src/icons/logo-outline.svg | 13 + src/icons/palette-fill.svg | 3 + src/icons/palette-outline.svg | 1 + src/icons/speedometer-outline.svg | 4 + src/icons/user.svg | 1 + src/images/logo.png | Bin 0 -> 10365 bytes 35 files changed, 772 insertions(+), 13 deletions(-) create mode 100644 src/UI/Settings/UIWindowSettings.js create mode 100644 src/icons/cube-outline.svg create mode 100644 src/icons/language.svg create mode 100644 src/icons/logo-outline.svg create mode 100644 src/icons/palette-fill.svg create mode 100644 src/icons/palette-outline.svg create mode 100644 src/icons/speedometer-outline.svg create mode 100644 src/icons/user.svg create mode 100644 src/images/logo.png diff --git a/src/UI/Settings/UIWindowSettings.js b/src/UI/Settings/UIWindowSettings.js new file mode 100644 index 00000000..ae53dbb0 --- /dev/null +++ b/src/UI/Settings/UIWindowSettings.js @@ -0,0 +1,415 @@ +/** + * Copyright (C) 2024 Puter Technologies Inc. + * + * This file is part of Puter. + * + * Puter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import UIWindow from '../UIWindow.js' +import UIWindowChangePassword from '../UIWindowChangePassword.js' +// import UIWindowChangeEmail from './UIWindowChangeEmail.js' +// import UIWindowDeleteAccount from './UIWindowDeleteAccount.js' +import UIWindowChangeUsername from '../UIWindowChangeUsername.js' +import changeLanguage from "../../i18n/i18nchangeLanguage.js" + + +async function UIWindowSettings(options){ + return new Promise(async (resolve) => { + options = options ?? {}; + + let h = ''; + + h += `
`; + h += `
`; + // side bar + h += `
`; + h += `
${i18n('about')}
`; + h += `
${i18n('usage')}
`; + h += `
${i18n('account')}
`; + h += `
${i18n('language')}
`; + h += `
`; + + // content + h += `
`; + // About + h += `
`; + h += `
` + h += `
+ +

Puter is a privacy-first personal cloud to keep all your files, apps, and games in one + secure place, accessible from anywhere at any time.

+ + +
+
+ + +
+

Open Source Software and Content

+
+ +
+
+
+ `; + h += `
`; + h += `
`; + + // Usage + h += `
`; + h += `

Usage

`; + h += `
+

Storage Usage

+
+ + used of + +
+
+ +
+
+
` + h += `
`; + + // Account + h += `
`; + h += `

Account

`; + // change password button + h += `
`; + h += `Password`; + h += `
`; + h += ``; + h += `
`; + h += `
`; + + // change email button + if(user.email){ + h += `
`; + h += `${user.email}`; + h += `
`; + h += ``; + h += `
`; + h += `
`; + } + + // change username button + h += `
`; + h += `
`; + h += `Username`; + h += `${user.username}`; + h += `
`; + h += `
`; + h += ``; + h += `
` + h += `
`; + + // delete account button + h += `
`; + h += `Delete Account`; + h += `
`; + h += ``; + h += `
`; + h += `
`; + + h += `
`; + + // Language + h += `
`; + h += `

Language

`; + // search + h += `
`; + h += ``; + h += `
`; + // list of languages + const available_languages = listSupportedLanguages(); + h += `
`; + for (let lang of available_languages) { + h += `
${lang.name}
`; + } + h += `
`; + + h += `
`; + + h += `
`; + h += `
`; + h += `
`; + + h += ``; + + const el_window = await UIWindow({ + title: 'Settings', + app: 'settings', + single_instance: true, + icon: null, + uid: null, + is_dir: false, + body_content: h, + has_head: true, + selectable_body: false, + allow_context_menu: false, + is_resizable: false, + is_droppable: false, + init_center: true, + allow_native_ctxmenu: true, + allow_user_select: true, + backdrop: false, + width: 800, + height: 500, + height: 'auto', + dominant: true, + show_in_taskbar: false, + draggable_body: false, + onAppend: function(this_window){ + }, + window_class: 'window-settings', + body_css: { + width: 'initial', + height: '100%', + overflow: 'auto' + } + }); + + $.ajax({ + url: api_origin + "/drivers/usage", + type: 'GET', + async: true, + contentType: "application/json", + headers: { + "Authorization": "Bearer " + auth_token + }, + statusCode: { + 401: function () { + logout(); + }, + }, + success: function (res) { + let h = ''; // Initialize HTML string for driver usage bars + + // Loop through user services + res.user.forEach(service => { + const { monthly_limit, monthly_usage } = service; + let usageDisplay = ``; + + if (monthly_limit !== null) { + let usage_percentage = (monthly_usage / monthly_limit * 100).toFixed(0); + usage_percentage = usage_percentage > 100 ? 100 : usage_percentage; // Cap at 100% + usageDisplay = ` +
+

${service.service['driver.interface']} (${service.service['driver.method']}):

+ ${monthly_usage} used of ${monthly_limit} +
+
${usage_percentage}%
+
+
+ `; + } + else { + usageDisplay = ` +
+

${service.service['driver.interface']} (${service.service['driver.method']}):

+ Usage: ${monthly_usage} (Unlimited) +
+ `; + } + h += usageDisplay; + }); + + // Append driver usage bars to the container + $('.settings-content[data-settings="usage"]').append(`
${h}
`); + } + }) + + // df + $.ajax({ + url: api_origin + "/df", + type: 'GET', + async: true, + contentType: "application/json", + headers: { + "Authorization": "Bearer " + auth_token + }, + statusCode: { + 401: function () { + logout(); + }, + }, + success: function (res) { + let usage_percentage = (res.used / res.capacity * 100).toFixed(0); + usage_percentage = usage_percentage > 100 ? 100 : usage_percentage; + + $('#storage-used').html(byte_format(res.used)); + $('#storage-capacity').html(byte_format(res.capacity)); + $('#storage-used-percent').html(usage_percentage + '%'); + $('#storage-bar').css('width', usage_percentage + '%'); + if (usage_percentage >= 100) { + $('#storage-bar').css({ + 'border-top-right-radius': '3px', + 'border-bottom-right-radius': '3px', + }); + } + } + }) + + // version + $.ajax({ + url: api_origin + "/version", + type: 'GET', + async: true, + contentType: "application/json", + headers: { + "Authorization": "Bearer " + auth_token + }, + statusCode: { + 401: function () { + logout(); + }, + }, + success: function (res) { + var d = new Date(0); + $('.version').html('Version: ' + res.version + ' • ' + 'Server: ' + res.location + ' • ' + 'Deployed: ' + new Date(res.deploy_timestamp)); + } + }) + + $(el_window).find('.credits').on('click', function (e) { + if($(e.target).hasClass('credits')){ + $('.credits').get(0).close(); + } + }); + + $(el_window).find('.show-credits').on('click', function (e) { + $('.credits').get(0).showModal(); + }) + + $(el_window).find('.change-password').on('click', function (e) { + UIWindowChangePassword(); + }) + + $(el_window).find('.change-email').on('click', function (e) { + UIWindowChangeEmail(); + }) + + $(el_window).find('.delete-account').on('click', function (e) { + UIWindowDeleteAccount(); + }) + + $(el_window).find('.change-username').on('click', function (e) { + UIWindowChangeUsername(); + }) + + $(el_window).on('click', '.settings-sidebar-item', function(){ + const $this = $(this); + const settings = $this.attr('data-settings'); + const $container = $this.closest('.settings').find('.settings-content-container'); + const $content = $container.find(`.settings-content[data-settings="${settings}"]`); + // add active class to sidebar item + $this.siblings().removeClass('active'); + $this.addClass('active'); + // add active class to content + $container.find('.settings-content').removeClass('active'); + $content.addClass('active'); + // if language, focus on search + if(settings === 'language'){ + $content.find('.search').first().focus(); + // make sure all language items are visible + $content.find('.language-item').show(); + // empty search + $content.find('.search').val(''); + } + }) + + $(el_window).on('click', '.language-item', function(){ + const $this = $(this); + const lang = $this.attr('data-lang'); + changeLanguage(lang); + $this.siblings().removeClass('active'); + $this.addClass('active'); + // make sure all other language items are visible + $this.closest('.language-list').find('.language-item').show(); + }) + + $(el_window).on('input', '.search', function(){ + const $this = $(this); + const search = $this.val().toLowerCase(); + const $container = $this.closest('.settings').find('.settings-content-container'); + const $content = $container.find('.settings-content.active'); + const $list = $content.find('.language-list'); + const $items = $list.find('.language-item'); + $items.each(function(){ + const $item = $(this); + const lang = $item.attr('data-lang'); + const name = $item.text().toLowerCase(); + const english_name = $item.attr('data-english-name').toLowerCase(); + if(name.includes(search) || lang.includes(search) || english_name.includes(search)){ + $item.show(); + }else{ + $item.hide(); + } + }) + }); + + resolve(el_window); + }); +} + + +export default UIWindowSettings \ No newline at end of file diff --git a/src/UI/UIDesktop.js b/src/UI/UIDesktop.js index 18ad3251..4281fb18 100644 --- a/src/UI/UIDesktop.js +++ b/src/UI/UIDesktop.js @@ -34,7 +34,8 @@ import UIWindowQR from "./UIWindowQR.js" import UIWindowRefer from "./UIWindowRefer.js" import UITaskbar from "./UITaskbar.js" import new_context_menu_item from "../helpers/new_context_menu_item.js" -import ChangeLanguage from "../i18n/i18nChangeLanguage.js" +import changeLanguage from "../i18n/i18nchangeLanguage.js" +import UIWindowSettings from "./Settings/UIWindowSettings.js" async function UIDesktop(options){ let h = ''; @@ -1156,12 +1157,12 @@ $(document).on('click', '.user-options-menu-btn', async function(e){ // ------------------------------------------- // Load available languages // ------------------------------------------- - const supportedLanguagesItems = ListSupportedLanguages().map(lang => { + const supportedLanguagesItems = listSupportedLanguages().map(lang => { return { html: lang.name, icon: window.locale === lang.code ? '✓' : '', onClick: async function(){ - ChangeLanguage(lang.code); + changeLanguage(lang.code); } } }); @@ -1288,10 +1289,7 @@ $(document).on('click', '.close-launch-popover', function(){ }); $(document).on('click', '.toolbar-puter-logo', function(){ - // launch the about app - launch_app({name: 'about', window_options:{ - single_instance: true, - }}); + UIWindowSettings(); }) $(document).on('click', '.user-options-create-account-btn', async function(e){ diff --git a/src/css/style.css b/src/css/style.css index 5dd97c1e..0950031d 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -2138,7 +2138,7 @@ label { font: 14px "Helvetica Neue", Sans-Serif; border: none !important; backdrop-filter: blur(3px); - filter: drop-shadow(0 0 3px rgba(0,0,0,.455)); + filter: drop-shadow(0 0 3px rgba(0, 0, 0, .455)); } .arrow { @@ -3254,4 +3254,299 @@ label { justify-content: center; align-items: center; display: none; +} + +/*! + * ================================================== + * Settings + * ================================================== + */ + +.settings-container{ + display: flex; + flex-direction: column; + height: 100%; +} +.settings{ + display: flex; + flex-direction: row; + -webkit-font-smoothing: antialiased; + flex-grow: 1; +} + +.settings-sidebar{ + width: 200px; + background-color: #f9f9f9; + border-right: 1px solid #e0e0e0; + padding: 20px; + +} + +.settings-sidebar-item{ + cursor: pointer; + border-radius: 4px; + padding: 10px; + margin-bottom: 15px; + background-repeat: no-repeat; + background-position: 10px center; + background-size: 25px; + padding-left: 45px; + font-size: 15px; +} +.settings-sidebar-item:hover{ + background-color: #e8e8e8; +} + +.settings-sidebar-item.active{ + background-color: #e0e0e0; +} +.settings-content-container{ + flex: 1; + padding: 20px 30px; + height: 500px; + overflow-y: scroll; +} + +.settings-content{ + display: none; +} + +.settings-content[data-settings="about"]{ + height: 100%; +} + +.settings-content h1{ + font-size: 24px; + margin-bottom: 20px; + border-bottom: 1px solid #e0e0e0; + padding-bottom: 10px; + padding-left: 5px; + font-weight: 500; +} + +.settings-content.active{ + display: block; +} + +.settings-content .about-container{ + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.settings-content[data-settings="about"] a { + color: #1663d4; + text-decoration: none; + font-size: 12px; +} + +.settings-content[data-settings="about"] a:hover { + text-decoration: underline; +} + +.settings-content .logo, +.settings-content .logo img { + display: block; + width: 55px; + height: 55px; + margin: 0 auto; + border-radius: 4px; +} + +.settings-content .links { + text-align: center; + font-size: 14px; + margin-top: 10px; +} + +.settings-content .social-links { + text-align: center; + /* margin-top: 10px; */ +} + +.settings-content .social-links a { + opacity: 0.7; + transition: opacity 0.1s ease-in-out; +} + +.settings-content .social-links a, +.settings-content .social-links a:hover { + text-decoration: none; + margin: 0 10px; + +} + +.settings-content .social-links a:hover { + opacity: 1; +} + +.settings-content .social-links svg { + width: 20px; + height: 20px; +} + +.settings-content .about { + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + padding: 20px 40px; + max-width: 500px; +} +.settings-content .version{ + font-size: 9px; + color: #343c4f; + text-align: center; + margin-bottom: 10px; + opacity: 0.3; + transition: opacity 0.1s ease-in-out; + height: 12px; +} +.settings-content .version:hover{ + opacity: 1; +} + +.driver-usage { + background-color: white; + bottom: 0; + width: 100%; + box-sizing: border-box; + color: #3c4963; + height: 85px; +} +.credits{ + padding: 0; + border: 1px solid #bfbfbf; + box-shadow: 1px 1px 10px 0px #8a8a8a; + width: 400px; +} +.credit-content a{ + font-size:15px; +} +.credits .credit-content{ + padding: 20px; +} +.credit-content{ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.credit-content ul{ + max-height: 300px; + overflow-y: scroll; + background: #f4f4f4; + padding: 10px; + box-shadow: 2px 2px 5px 2px inset #CCC; + +} +.credit-content li{ + margin-bottom: 10px; +} +#storage-bar-wrapper { + width: 100%; + height: 15px; + border: 1px solid #8a9096; + border-radius: 3px; + background-color: #fff; + position: relative; +} + +#storage-bar { + height: 15px; + background-color: #dbe3ef; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + width: 0; +} + +#storage-used-percent { + position: absolute; + left: calc(50% - 20px); + text-align: center; + display: inline-block; + width: 40px; + font-size: 13px; +} + +.usage-progbar-wrapper { + width: 100%; + height: 15px; + border: 1px solid #8a9096; + border-radius: 3px; + background-color: #fff; + position: relative; +} + +.usage-progbar { + height: 15px; + background-color: #dbe3ef; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + width: 0; +} + +.usage-progbar-percent { + position: absolute; + left: calc(50% - 20px); + text-align: center; + display: inline-block; + width: 40px; + font-size: 13px; +} + + +.version{ + font-size: 9px; + color: #343c4f; + text-align: center; + margin-bottom: 10px; + opacity: 0.3; + transition: opacity 0.1s ease-in-out; + height: 12px; +} +.version:hover{ + opacity: 1; +} +.language-list{ + display: grid; + grid-template-columns: 33.333333333% 33.333333333% 33.333333333%; +} +.language-item{ + cursor: pointer; + padding: 10px; + border-radius: 4px; + margin-bottom: 10px; + margin-right: 10px; +} + +.language-item:hover{ + background-color: #f6f6f6; +} + +.language-item.active{ + background-color: #e0e0e0; +} + +.settings-card{ + overflow: hidden; + padding: 10px 15px; + border: 1px solid; + border-radius: 4px; + background: #f7f7f7a1; + border: 1px solid #cccccc8f; + margin-bottom: 20px; + display: flex; + flex-direction: row; + align-items: center; + height: 45px; +} + +.settings-card strong{ + font-weight: 500; +} + +.settings-card-danger{ + border-color: #f0080866; + background: #ffecec; + color: rgb(215 2 2); } \ No newline at end of file diff --git a/src/globals.js b/src/globals.js index fb69918b..97d1251a 100644 --- a/src/globals.js +++ b/src/globals.js @@ -220,4 +220,7 @@ window.feature_flags = { window.is_auto_arrange_enabled = true; window.desktop_item_positions = {}; -window.reset_item_positions = true; // The variable decides if the item positions should be reset when the user enabled auto arrange \ No newline at end of file +window.reset_item_positions = true; // The variable decides if the item positions should be reset when the user enabled auto arrange + +// default language +window.locale = 'en'; \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index 96b13a23..cf352459 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -722,7 +722,7 @@ window.mutate_user_preferences = function(user_preferences_delta) { window.update_user_preferences = function(user_preferences) { window.user_preferences = user_preferences; localStorage.setItem('user_preferences', JSON.stringify(user_preferences)); - window.locale = user_preferences.language; + window.locale = user_preferences.language ?? 'en'; } window.sendWindowWillCloseMsg = function(iframe_element) { diff --git a/src/i18n/i18n.js b/src/i18n/i18n.js index 14b46873..5503a293 100644 --- a/src/i18n/i18n.js +++ b/src/i18n/i18n.js @@ -18,7 +18,7 @@ */ import translations from './translations/translations.js'; -window.ListSupportedLanguages = () => Object.keys(translations).map(lang => translations[lang]); +window.listSupportedLanguages = () => Object.keys(translations).map(lang => translations[lang]); window.i18n = function (key, replacements = [], encode_html = true) { if(typeof replacements === 'boolean' && encode_html === undefined){ diff --git a/src/i18n/i18nChangeLanguage.js b/src/i18n/i18nChangeLanguage.js index e3482daf..44cfa335 100644 --- a/src/i18n/i18nChangeLanguage.js +++ b/src/i18n/i18nChangeLanguage.js @@ -17,11 +17,11 @@ * along with this program. If not, see . */ -function ChangeLanguage(lang) { +function changeLanguage(lang) { window.locale = lang; window.mutate_user_preferences({ language : lang, }); } -export default ChangeLanguage; \ No newline at end of file +export default changeLanguage; \ No newline at end of file diff --git a/src/i18n/translations/ar.js b/src/i18n/translations/ar.js index ea51c9d4..279d8119 100644 --- a/src/i18n/translations/ar.js +++ b/src/i18n/translations/ar.js @@ -18,6 +18,7 @@ */ const ar = { name: "العربية", + english_name: "Arabic", code: "ar", dictionary: { access_granted_to: "دخول مسموح به", diff --git a/src/i18n/translations/bn.js b/src/i18n/translations/bn.js index 2bd82522..8cc68092 100644 --- a/src/i18n/translations/bn.js +++ b/src/i18n/translations/bn.js @@ -18,6 +18,7 @@ */ const bn = { name: "বাংলা", + english_name: "Bengali", code: "bn", dictionary: { access_granted_to: "অ্যাক্সেস মঞ্জুর করা হয়েছে", diff --git a/src/i18n/translations/da.js b/src/i18n/translations/da.js index ca72769e..21f9ab30 100644 --- a/src/i18n/translations/da.js +++ b/src/i18n/translations/da.js @@ -18,6 +18,7 @@ */ const da = { name: "Dansk", + english_name: "Danish", code: "da", dictionary: { access_granted_to: "Adgang givet til", diff --git a/src/i18n/translations/de.js b/src/i18n/translations/de.js index 7a750370..5f665b8c 100644 --- a/src/i18n/translations/de.js +++ b/src/i18n/translations/de.js @@ -18,6 +18,7 @@ */ const de = { name: "Deutsch", + english_name: "German", code: "de", dictionary: { access_granted_to: "Erlaubt zugriff auf", diff --git a/src/i18n/translations/emoji.js b/src/i18n/translations/emoji.js index 0ec22b53..dfa41ee4 100644 --- a/src/i18n/translations/emoji.js +++ b/src/i18n/translations/emoji.js @@ -18,6 +18,7 @@ */ const emoji = { name: "🌍", + english_name: "Emoji", code: "emoji", dictionary: { access_granted_to: "🔓✅", diff --git a/src/i18n/translations/en.js b/src/i18n/translations/en.js index 706d765d..88fcf2ef 100644 --- a/src/i18n/translations/en.js +++ b/src/i18n/translations/en.js @@ -18,8 +18,11 @@ */ const en = { name: "English", + english_name: "English", code: "en", dictionary: { + about: "About", + account: "Account", access_granted_to: "Access Granted To", add_existing_account: "Add Existing Account", all_fields_required: 'All fields are required.', @@ -87,6 +90,7 @@ const en = { items_in_trash_cannot_be_renamed: `This item can't be renamed because it's in the trash. To rename this item, first drag it out of the Trash.`, jpeg_image: 'JPEG image', keep_in_taskbar: 'Keep in Taskbar', + language: "Language", loading: 'Loading', log_in: "Log In", log_into_another_account_anyway: 'Log into another account anyway', @@ -173,6 +177,7 @@ const en = { unzip: "Unzip", upload: 'Upload', upload_here: 'Upload here', + usage: 'Usage', username: "Username", username_changed: 'Username updated successfully.', versions: "Versions", diff --git a/src/i18n/translations/es.js b/src/i18n/translations/es.js index 82005c4e..e6ccca5b 100644 --- a/src/i18n/translations/es.js +++ b/src/i18n/translations/es.js @@ -18,6 +18,7 @@ */ const es = { name: "Español", + english_name: "Spanish", code: "es", dictionary: { access_granted_to: "Acceso Permitido A", diff --git a/src/i18n/translations/fa.js b/src/i18n/translations/fa.js index 10068094..e2d3b94d 100644 --- a/src/i18n/translations/fa.js +++ b/src/i18n/translations/fa.js @@ -18,6 +18,7 @@ */ const fa = { name: "فارسی", + english_name: "Farsi", code: "fa", dictionary: { access_granted_to: "دسترسی داده شده به", diff --git a/src/i18n/translations/fi.js b/src/i18n/translations/fi.js index d43d0753..9950f10f 100644 --- a/src/i18n/translations/fi.js +++ b/src/i18n/translations/fi.js @@ -18,6 +18,7 @@ */ const fi = { name: "Suomi", + english_name: "Finnish", code: "fi", dictionary: { access_granted_to: "Käyttöoikeus Myönnetty", diff --git a/src/i18n/translations/fr.js b/src/i18n/translations/fr.js index 1ce604af..bff28b64 100644 --- a/src/i18n/translations/fr.js +++ b/src/i18n/translations/fr.js @@ -18,6 +18,7 @@ */ const fr = { name: "Français", + english_name: "French", code: "fr", dictionary: { access_granted_to: "Accès accordé à", diff --git a/src/i18n/translations/hy.js b/src/i18n/translations/hy.js index 1388753b..01c4d6cc 100644 --- a/src/i18n/translations/hy.js +++ b/src/i18n/translations/hy.js @@ -18,6 +18,7 @@ */ const hy = { name: "Հայերեն", + english_name: "Armenian", code: "hy", dictionary: { access_granted_to: "Մուտքը տրված է՝", diff --git a/src/i18n/translations/it.js b/src/i18n/translations/it.js index c967671b..abace233 100644 --- a/src/i18n/translations/it.js +++ b/src/i18n/translations/it.js @@ -18,6 +18,7 @@ */ const it = { name: "Italiano", + english_name: "Italian", code: "it", dictionary: { access_granted_to: "Accesso garantito a", diff --git a/src/i18n/translations/ko.js b/src/i18n/translations/ko.js index 517b264e..6ec427e2 100644 --- a/src/i18n/translations/ko.js +++ b/src/i18n/translations/ko.js @@ -18,6 +18,7 @@ */ const ko = { name: "한국어", + english_name: "Korean", code: "ko", dictionary: { access_granted_to: "접근 권한 부여", diff --git a/src/i18n/translations/nb.js b/src/i18n/translations/nb.js index b5e309ff..50bfbe83 100644 --- a/src/i18n/translations/nb.js +++ b/src/i18n/translations/nb.js @@ -18,6 +18,7 @@ */ const nb = { name: "Norsk Bokmål", + english_name: "Norwegian Bokmål", code: "nb", dictionary: { access_granted_to: "Tilgang gitt til", diff --git a/src/i18n/translations/nn.js b/src/i18n/translations/nn.js index a4298e5b..471762ea 100644 --- a/src/i18n/translations/nn.js +++ b/src/i18n/translations/nn.js @@ -18,6 +18,7 @@ */ const nn = { name: "Norsk Nynorsk", + english_name: "Norwegian Nynorsk", code: "nn", dictionary: { access_granted_to: "Tilgang gjeven til", diff --git a/src/i18n/translations/ro.js b/src/i18n/translations/ro.js index 14adabba..feaa56b7 100644 --- a/src/i18n/translations/ro.js +++ b/src/i18n/translations/ro.js @@ -18,6 +18,7 @@ */ const ro = { name: "Română", + english_name: "Romanian", code: "ro", dictionary: { access_granted_to: "Acces acordat pentru", diff --git a/src/i18n/translations/sv.js b/src/i18n/translations/sv.js index 30f3c700..6136cdfb 100644 --- a/src/i18n/translations/sv.js +++ b/src/i18n/translations/sv.js @@ -18,6 +18,7 @@ */ const sv = { name: "Svenska", + english_name: "Swedish", code: "sv", dictionary: { access_granted_to: "Tillgång beviljad till", diff --git a/src/i18n/translations/th.js b/src/i18n/translations/th.js index f369143f..99383292 100644 --- a/src/i18n/translations/th.js +++ b/src/i18n/translations/th.js @@ -18,6 +18,7 @@ */ const th = { name: "ไทย", + english_name: "Thai", code: "th", dictionary: { access_granted_to: "อนุญาตให้เข้าถึง", diff --git a/src/i18n/translations/ur.js b/src/i18n/translations/ur.js index cfa51332..65c79eaf 100644 --- a/src/i18n/translations/ur.js +++ b/src/i18n/translations/ur.js @@ -18,6 +18,7 @@ */ const ur = { name: "اردو", + english_name: "Urdu", code: "ur", dictionary: { access_granted_to: "رسائی مسموح ہے", diff --git a/src/i18n/translations/zh.js b/src/i18n/translations/zh.js index 7066a4d0..82e44ab0 100644 --- a/src/i18n/translations/zh.js +++ b/src/i18n/translations/zh.js @@ -18,6 +18,7 @@ */ const zh = { name: "中文", + english_name: "Chinese", code: "zh", dictionary: { access_granted_to: "访问授权给", diff --git a/src/icons/cube-outline.svg b/src/icons/cube-outline.svg new file mode 100644 index 00000000..77faf2a5 --- /dev/null +++ b/src/icons/cube-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/language.svg b/src/icons/language.svg new file mode 100644 index 00000000..67a1c09f --- /dev/null +++ b/src/icons/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/logo-outline.svg b/src/icons/logo-outline.svg new file mode 100644 index 00000000..397bb720 --- /dev/null +++ b/src/icons/logo-outline.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/icons/palette-fill.svg b/src/icons/palette-fill.svg new file mode 100644 index 00000000..d7a6a3bc --- /dev/null +++ b/src/icons/palette-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/icons/palette-outline.svg b/src/icons/palette-outline.svg new file mode 100644 index 00000000..0ab25d65 --- /dev/null +++ b/src/icons/palette-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/speedometer-outline.svg b/src/icons/speedometer-outline.svg new file mode 100644 index 00000000..75e79c85 --- /dev/null +++ b/src/icons/speedometer-outline.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/icons/user.svg b/src/icons/user.svg new file mode 100644 index 00000000..a7b32245 --- /dev/null +++ b/src/icons/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/images/logo.png b/src/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..56603cbad2d4d00f1dc9e83dbdcd01dd6f7372fb GIT binary patch literal 10365 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