diff --git a/vaktija-desklet@MuxBH28/README.md b/vaktija-desklet@MuxBH28/README.md index 476c82537..f2a049a74 100644 --- a/vaktija-desklet@MuxBH28/README.md +++ b/vaktija-desklet@MuxBH28/README.md @@ -32,13 +32,15 @@ Vaktija Desklet is a simple desklet that displays prayer times using data from t 1. Add the Vaktija Desklet to your desktop via the "Add Desklets" menu. 2. The desklet will automatically fetch and display prayer times for the default location. +3. Configure the desklet’s settings, including location, appearance, and other preferences, through the settings menu. This allows you to customize the desklet to suit your needs and preferences. ## Configuration -Currently, the desklet uses default settings for the Sarajevo location. Future versions will include configuration options. +Currently, the desklet supports 177 locations across Bosnia and Herzegovina and southern Serbia. Future versions will include additional configuration options and more locations. ## Links +- Website - [Vaktija Desklet](https://sehic.rf.gd/?project=vaktija) - Vaktija API - [Vaktija API](https://vaktija.ba/) ## Author diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js index 3f6b7ba9e..0c06bea77 100644 --- a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js @@ -2,108 +2,196 @@ const Desklet = imports.ui.desklet; const St = imports.gi.St; const Gio = imports.gi.Gio; const ByteArray = imports.byteArray; +const Settings = imports.ui.settings; +const GLib = imports.gi.GLib; +const Clutter = imports.gi.Clutter; + const uuid = "vaktija-desklet@MuxBH28"; +function main(metadata, desklet_id) { + return new VaktijaDesklet(metadata, desklet_id); +} + function VaktijaDesklet(metadata, desklet_id) { this._init(metadata, desklet_id); } VaktijaDesklet.prototype = { __proto__: Desklet.Desklet.prototype, - lastFetchedData: null, + _init: function (metadata, desklet_id) { - global.log("Initializing Vaktija desklet"); Desklet.Desklet.prototype._init.call(this, metadata, desklet_id); + this.settings = new Settings.DeskletSettings(this, uuid, desklet_id); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'api-url', 'apiUrl', + this.fetchData, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'api-location', 'apiLocation', + this.fetchData, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'update-interval', 'updateInterval', + this.setupUpdateInterval, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'show-date', 'showDate', + this.updateDisplay, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'show-islamic-date', 'showIslamicDate', + this.updateDisplay, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'show-seconds', 'showSeconds', + this.updateDisplay, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'height', 'height', + this.updateDisplay, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'width', 'width', + this.updateDisplay, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'transparency', 'transparency', + this.updateAppearance, this); + + this.settings.bindProperty(Settings.BindingDirection.IN, + 'backgroundColor', 'backgroundColor', + this.updateAppearance, this); + this.setupUI(); this.fetchData(); this.setupMidnightTimer(); - this.setupTimeUpdateInterval(); + this.setupUpdateInterval(); this.setupCountdownUpdateInterval(); }, setupUI: function () { - global.log("Setting up UI"); - this.window = new St.Bin(); + this.window = new St.BoxLayout({ vertical: true }); this.text = new St.Label({ style_class: 'label' }); - this.text.set_text("Fetching data..."); this.window.add_actor(this.text); this.setContent(this.window); + + this.updateAppearance(); + }, + + updateAppearance: function () { + this.window.set_width(this.width); + this.window.set_height(this.height); + + this.window.set_opacity(Math.round(this.transparency * 255)); + + let backgroundColor = Clutter.Color.from_string(this.backgroundColor)[1]; + this.window.set_style(`background-color: rgba(${backgroundColor.red}, ${backgroundColor.green}, ${backgroundColor.blue}, ${this.transparency});`); }, fetchData: function () { - global.log("Fetching data from API"); + let url = this.apiUrl || "https://api.vaktija.ba/"; - let url = "https://api.vaktija.ba/"; + let location = this.apiLocation || 77; + + if (url === "https://api.vaktija.ba/") { + url = url + 'vaktija/v1/' + location; + } let file = Gio.File.new_for_uri(url); file.load_contents_async(null, (obj, result) => { try { let [success, contents] = obj.load_contents_finish(result); if (!success) { - global.log("Error fetching data"); this.text.set_text("Error fetching data"); return; } let data = ByteArray.toString(contents); - let json = JSON.parse(data); - global.log("Data fetched: " + JSON.stringify(json)); - this.lastFetchedData = data; - this.updateDisplay(json); + this.lastFetchedData = JSON.parse(data); + this.updateDisplay(); } catch (e) { - global.log("Error fetching data: " + e.message); this.text.set_text("Error fetching data"); } }); }, - updateDisplay: function (json) { - let currentTime = new Date().toLocaleTimeString(); + updateDisplay: function () { + if (!this.lastFetchedData) return; + + let now = new Date(); + let timeFormat = this.showSeconds ? 'HH:mm:ss' : 'HH:mm'; + let formattedTime = this.formatTime(now, timeFormat); + + let showIslamicDate = this.showIslamicDate ? `${this.lastFetchedData.datum[0]}\n` : ''; + let showDate = this.showDate ? `Datum: ${this.lastFetchedData.datum[1]}\n` : ''; + let vakatTimes = [ - "Sabah", "Izlazak", "Podne", "Ikindija", "Akšam", "Jacija" + "Zora", "Izlazak sunca", "Podne", "Ikindija", "Akšam", "Jacija" ]; - let now = new Date(); let nextVakat = null; let countdown = ""; - for (let i = 0; i < json.vakat.length; i++) { - let vakatTime = json.vakat[i].split(":"); + for (let i = 0; i < this.lastFetchedData.vakat.length; i++) { + let vakatTime = this.lastFetchedData.vakat[i].split(":"); let vakatDate = new Date(now); vakatDate.setHours(parseInt(vakatTime[0], 10)); vakatDate.setMinutes(parseInt(vakatTime[1], 10)); if (vakatDate > now) { nextVakat = vakatTimes[i]; - let diff = Math.abs(vakatDate - now) / 1000; - let hours = Math.floor(diff / 3600) % 24; - let minutes = Math.floor(diff / 60) % 60; - let seconds = Math.floor(diff % 60); + let diff = vakatDate - now; + let hours = Math.floor(diff / (1000 * 60 * 60)); + let minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); + let seconds = Math.floor((diff % (1000 * 60)) / 1000); - countdown = `${hours}h ${minutes}m ${seconds}s`; + countdown = `${this.pad(hours)}h ${this.pad(minutes)}m ${this.pad(seconds)}s`; break; } } + if (!nextVakat) { + let firstVakatTomorrow = this.parseTime(this.lastFetchedData.vakat[0]) + 24 * 60 * 60 * 1000; + let timeUntilNextVakat = firstVakatTomorrow - now.getTime(); + let hours = Math.floor(timeUntilNextVakat / (1000 * 60 * 60)) % 24; + let minutes = Math.floor((timeUntilNextVakat % (1000 * 60 * 60)) / (1000 * 60)); + let seconds = Math.floor((timeUntilNextVakat % (1000 * 60)) / 1000); + + countdown = `${this.pad(hours)}h ${this.pad(minutes)}m ${this.pad(seconds)}s`; + } + let displayText = ` - Trenutno vrijeme: ${currentTime}\n - Lokacija: ${json.lokacija}\n - Datum: ${json.datum[1]}\n - \n + Trenutno vrijeme: ${formattedTime}\n + Lokacija: ${this.lastFetchedData.lokacija}\n + ${showDate} + ${showIslamicDate} Do sljedećeg vakta: ${countdown}\n - Sabah: ${json.vakat[0]}\n - Izlazak: ${json.vakat[1]}\n - Podne: ${json.vakat[2]}\n - Ikindija: ${json.vakat[3]}\n - Akšam: ${json.vakat[4]}\n - Jacija: ${json.vakat[5]} + Zora: ${this.lastFetchedData.vakat[0]}\n + Izlazak sunca: ${this.lastFetchedData.vakat[1]}\n + Podne: ${this.lastFetchedData.vakat[2]}\n + Ikindija: ${this.lastFetchedData.vakat[3]}\n + Akšam: ${this.lastFetchedData.vakat[4]}\n + Jacija: ${this.lastFetchedData.vakat[5]} `; this.text.set_text(displayText); }, + formatTime: function (date, format) { + let hours = date.getHours().toString().padStart(2, '0'); + let minutes = date.getMinutes().toString().padStart(2, '0'); + let seconds = date.getSeconds().toString().padStart(2, '0'); + + if (format === 'HH:mm:ss') { + return `${hours}:${minutes}:${seconds}`; + } else { + return `${hours}:${minutes}`; + } + }, + setupMidnightTimer: function () { let now = new Date(); let tomorrowMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0, 0); @@ -115,71 +203,20 @@ VaktijaDesklet.prototype = { }, timeUntilMidnight); }, - setupTimeUpdateInterval: function () { - setInterval(() => { - this.updateTime(); - }, 1000); + setupUpdateInterval: function () { + if (this.updateInterval && this.updateInterval > 0) { + GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, this.updateInterval, () => { + this.fetchData(); + return GLib.SOURCE_CONTINUE; + }); + } }, setupCountdownUpdateInterval: function () { - setInterval(() => { - this.updateCountdown(); - }, 1000); - }, - - updateTime: function () { - let currentTime = new Date().toLocaleTimeString(); - let displayText = this.text.get_text(); - displayText = displayText.replace(/Trenutno vrijeme: .+/, `Trenutno vrijeme: ${currentTime}`); - this.text.set_text(displayText); - }, - - updateCountdown: function () { - let json = JSON.parse(this.lastFetchedData); - - let now = new Date(); - let currentTime = now.getTime(); - - let vakatTimes = [ - { name: "Sabah", time: this.parseTime(json.vakat[0]) }, - { name: "Izlazak", time: this.parseTime(json.vakat[1]) }, - { name: "Podne", time: this.parseTime(json.vakat[2]) }, - { name: "Ikindija", time: this.parseTime(json.vakat[3]) }, - { name: "Akšam", time: this.parseTime(json.vakat[4]) }, - { name: "Jacija", time: this.parseTime(json.vakat[5]) } - ]; - - let nextVakat = null; - let countdown = ""; - - // Pronalazi sljedeći vakat - for (let i = 0; i < vakatTimes.length; i++) { - if (vakatTimes[i].time > currentTime) { - nextVakat = vakatTimes[i]; - break; - } - } - - if (nextVakat) { - let timeUntilNextVakat = nextVakat.time - currentTime; - let hours = Math.floor(timeUntilNextVakat / (1000 * 60 * 60)); - let minutes = Math.floor((timeUntilNextVakat % (1000 * 60 * 60)) / (1000 * 60)); - let seconds = Math.floor((timeUntilNextVakat % (1000 * 60)) / 1000); - - countdown = `${this.pad(hours)}:${this.pad(minutes)}:${this.pad(seconds)}`; - } else { - let firstVakatTomorrow = this.parseTime(json.vakat[0]) + 24 * 60 * 60 * 1000; - let timeUntilNextVakat = firstVakatTomorrow - currentTime; - let hours = Math.floor(timeUntilNextVakat / (1000 * 60 * 60)) % 24; - let minutes = Math.floor((timeUntilNextVakat % (1000 * 60 * 60)) / (1000 * 60)); - let seconds = Math.floor((timeUntilNextVakat % (1000 * 60)) / 1000); - - countdown = `${this.pad(hours)}:${this.pad(minutes)}:${this.pad(seconds)}`; - } - - let displayText = this.text.get_text(); - displayText = displayText.replace(/Do sljedećeg vakta: .+/, `Do sljedećeg vakta: ${countdown}`); - this.text.set_text(displayText); + GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { + this.updateDisplay(); + return GLib.SOURCE_CONTINUE; + }); }, pad: function (num) { @@ -187,16 +224,9 @@ VaktijaDesklet.prototype = { }, parseTime: function (timeString) { - let parts = timeString.split(":"); - let hours = parseInt(parts[0], 10); - let minutes = parseInt(parts[1], 10); - return new Date().setHours(hours, minutes, 0, 0); - }, - - + let [hours, minutes] = timeString.split(':').map(Number); + let now = new Date(); + let vakatDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes, 0, 0); + return vakatDate.getTime(); + } }; - -function main(metadata, desklet_id) { - global.log("Creating new desklet instance"); - return new VaktijaDesklet(metadata, desklet_id); -} diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json index ec8665ee2..a5c614aee 100644 --- a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json @@ -1,11 +1,10 @@ { "uuid": "vaktija-desklet@MuxBH28", "name": "Vaktija Desklet", - "description": "Displays Vaktija available from vaktija.ba API!", + "description": "This desklet fetches and displays prayer times using data from vaktija.ba API.", "max-instances": "1", - "version": "1.1", + "version": "1.2", "author": "MuxBH28", - "website": "https://github.com/MuxBH28/vaktija-desklet", - "description": "This desklet fetches and displays prayer times using data from vaktija.ba API.", - "credits": "Desklet created by MuxBH28 - https://github.com/MuxBH28/ \nAPI created by vaktija.ba - https://github.com/vaktija" + "website": "https://sehic.rf.gd/?project=vaktija", + "credits": "API created by vaktija.ba - https://github.com/vaktija" } diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/settings-schema.json b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/settings-schema.json new file mode 100644 index 000000000..8f1662741 --- /dev/null +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/settings-schema.json @@ -0,0 +1,244 @@ +{ + "head0": { + "type": "header", + "description": "Desklet Settings for Vaktija Desklet" + }, + "head1": { + "type": "header", + "description": "API Settings" + }, + "api-url": { + "type": "combobox", + "default": "https://api.vaktija.ba/", + "description": "API", + "tooltip": "Select the API that provides prayer times data.", + "options": { + "Vaktija API (Bosnia)": "https://api.vaktija.ba/" + } + }, + "api-location": { + "type": "combobox", + "default": "77", + "description": "Location (for Vaktija.ba API)", + "tooltip": "Select the location that provides vaktija.ba API.", + "options": { + "Banovići": "0", + "Banja Luka": "1", + "Bihać": "2", + "Bijeljina": "3", + "Bileća": "4", + "Bosanski Brod": "5", + "Bosanska Dubica": "6", + "Bosanska Gradiška": "7", + "Bosansko Grahovo": "8", + "Bosanska Krupa": "9", + "Bosanski Novi": "10", + "Bosanski Petrovac": "11", + "Bosanski Šamac": "12", + "Bratunac": "13", + "Brčko": "14", + "Breza": "15", + "Bugojno": "16", + "Busovača": "17", + "Bužim": "18", + "Cazin": "19", + "Čajniče": "20", + "Čapljina": "21", + "Čelić": "22", + "Čelinac": "23", + "Čitluk": "24", + "Derventa": "25", + "Doboj": "26", + "Donji Vakuf": "27", + "Drvar": "28", + "Foča": "29", + "Fojnica": "30", + "Gacko": "31", + "Glamoč": "32", + "Goražde": "33", + "Gornji Vakuf": "34", + "Gračanica": "35", + "Gradačac": "36", + "Grude": "37", + "Hadžići": "38", + "Han-Pijesak": "39", + "Hlivno": "40", + "Ilijaš": "41", + "Jablanica": "42", + "Jajce": "43", + "Kakanj": "44", + "Kalesija": "45", + "Kalinovik": "46", + "Kiseljak": "47", + "Kladanj": "48", + "Ključ": "49", + "Konjic": "50", + "Kotor-Varoš": "51", + "Kreševo": "52", + "Kupres": "53", + "Laktaši": "54", + "Lopare": "55", + "Lukavac": "56", + "Ljubinje": "57", + "Ljubuški": "58", + "Maglaj": "59", + "Modriča": "60", + "Mostar": "61", + "Mrkonjić-Grad": "62", + "Neum": "63", + "Nevesinje": "64", + "Novi Travnik": "65", + "Odžak": "66", + "Olovo": "67", + "Orašje": "68", + "Pale": "69", + "Posušje": "70", + "Prijedor": "71", + "Prnjavor": "72", + "Prozor": "73", + "Rogatica": "74", + "Rudo": "75", + "Sanski Most": "76", + "Sarajevo": "77", + "Skender-Vakuf": "78", + "Sokolac": "79", + "Srbac": "80", + "Srebrenica": "81", + "Srebrenik": "82", + "Stolac": "83", + "Šekovići": "84", + "Šipovo": "85", + "Široki Brijeg": "86", + "Teslić": "87", + "Tešanj": "88", + "Tomislav-Grad": "89", + "Travnik": "90", + "Trebinje": "91", + "Trnovo": "92", + "Tuzla": "93", + "Ugljevik": "94", + "Vareš": "95", + "Velika Kladuša": "96", + "Visoko": "97", + "Višegrad": "98", + "Vitez": "99", + "Vlasenica": "100", + "Zavidovići": "101", + "Zenica": "102", + "Zvornik": "103", + "Žepa": "104", + "Žepče": "105", + "Živinice": "106", + "Bijelo Polje": "107", + "Gusinje": "108", + "Nova Varoš": "109", + "Novi Pazar": "110", + "Plav": "111", + "Pljevlja": "112", + "Priboj": "113", + "Prijepolje": "114", + "Rožaje": "115", + "Sjenica": "116", + "Tutin": "117" + } + }, + "head2": { + "type": "header", + "description": "Date and time" + }, + "update-interval": { + "type": "spinbutton", + "default": 1, + "min": 1, + "max": 60, + "step": 1, + "description": "Update Interval (seconds)", + "tooltip": "Number of seconds between data updates." + }, + "show-date": { + "type": "checkbox", + "description": "Show Gregorian calendar date", + "tooltip": "Whether to show today's Gregorian calendar date.", + "default": true + }, + "show-islamic-date": { + "type": "checkbox", + "description": "Show Hijri Calendar Date", + "tooltip": "Whether to show today's Hijri Calendar date.", + "default": false + }, + "show-seconds": { + "type": "checkbox", + "description": "Show Seconds", + "tooltip": "Whether to show seconds in the time display.", + "default": true + }, + "head3": { + "type": "header", + "description": "Visual settings" + }, + "height": { + "default": 380, + "type": "spinbutton", + "min": 150, + "max": 500, + "description": "Height :", + "units": "pixels", + "step": 10 + }, + "width": { + "default": 300, + "type": "spinbutton", + "min": 300, + "max": 500, + "description": "Width :", + "units": "pixels", + "step": 10 + }, + "transparency": { + "type": "scale", + "default": 1, + "min": 0.1, + "max": 1.0, + "step": 0.05, + "description": "Background transparency", + "tooltip": "The higher the value, the more solid the desklet background" + }, + "backgroundColor": { + "type": "colorchooser", + "description": "Background color", + "tooltip": "Desklet background color", + "default": "rgb(0,0,0)" + }, + "head4": { + "type": "header", + "description": "Troubleshoot" + }, + "reload-button": { + "type": "button", + "description": "Reload Desklet", + "callback": "reloadDesklet" + }, + "head5": { + "type": "header", + "description": "Links" + }, + "help-entry1": { + "type": "entry", + "description": "Issues", + "tooltip": "Copy link and paste it into Your browser", + "default": "https://github.com/linuxmint/cinnamon-spices-desklets/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+vaktija-desklet%40MuxBH28" + }, + "help-entry2": { + "type": "entry", + "description": "Cinnamon Spices", + "tooltip": "Copy link and paste it into Your browser", + "default": "https://cinnamon-spices.linuxmint.com/desklets/view/67" + }, + "help-entry3": { + "type": "entry", + "description": "Author", + "tooltip": "Copy link and paste it into Your browser", + "default": "https://github.com/MuxBH28" + } +} \ No newline at end of file diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/stylesheet.css b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/stylesheet.css index 32e4b4a68..32de4655d 100644 --- a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/stylesheet.css +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/stylesheet.css @@ -1,5 +1,4 @@ .window { - background-color: #2c2c2c; border: 2px solid #848688; border-radius: 10px; } @@ -7,7 +6,6 @@ .label { font-size: 14px; color: #ffffff; - background-color: #001922; padding: 0px; padding-right: 50px; border-radius: 5px;