diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js new file mode 100644 index 00000000..0c06bea7 --- /dev/null +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/desklet.js @@ -0,0 +1,232 @@ +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, + + _init: function (metadata, desklet_id) { + 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.setupUpdateInterval(); + this.setupCountdownUpdateInterval(); + }, + + setupUI: function () { + this.window = new St.BoxLayout({ vertical: true }); + this.text = new St.Label({ style_class: 'label' }); + + 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 () { + let url = this.apiUrl || "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) { + this.text.set_text("Error fetching data"); + return; + } + + let data = ByteArray.toString(contents); + this.lastFetchedData = JSON.parse(data); + this.updateDisplay(); + } catch (e) { + this.text.set_text("Error fetching data"); + } + }); + }, + + 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 = [ + "Zora", "Izlazak sunca", "Podne", "Ikindija", "Akšam", "Jacija" + ]; + + let nextVakat = null; + let countdown = ""; + + 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 = 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 = `${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: ${formattedTime}\n + Lokacija: ${this.lastFetchedData.lokacija}\n + ${showDate} + ${showIslamicDate} + Do sljedećeg vakta: ${countdown}\n + 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); + let timeUntilMidnight = tomorrowMidnight - now; + + setTimeout(() => { + this.fetchData(); + this.setupMidnightTimer(); + }, timeUntilMidnight); + }, + + 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 () { + GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { + this.updateDisplay(); + return GLib.SOURCE_CONTINUE; + }); + }, + + pad: function (num) { + return (num < 10 ? '0' : '') + num; + }, + + parseTime: function (timeString) { + 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(); + } +}; diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/icon.png b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/icon.png new file mode 100644 index 00000000..2f866083 Binary files /dev/null and b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/icon.png differ diff --git a/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json new file mode 100644 index 00000000..9181446f --- /dev/null +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/metadata.json @@ -0,0 +1,11 @@ +{ + "uuid": "vaktija-desklet@MuxBH28", + "name": "Vaktija Desklet", + "description": "This desklet fetches and displays prayer times using data from vaktija.ba API.", + "max-instances": "1", + "version": "1.2", + "author": "MuxBH28", + "website": "https://sehic.rf.gd/?project=vaktija", + "credits": "API created by vaktija.ba - https://github.com/vaktija", + "last-edited": 1721249424 +} \ No newline at end of file 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 00000000..8f166274 --- /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 new file mode 100644 index 00000000..32de4655 --- /dev/null +++ b/vaktija-desklet@MuxBH28/files/vaktija-desklet@MuxBH28/stylesheet.css @@ -0,0 +1,14 @@ +.window { + border: 2px solid #848688; + border-radius: 10px; +} + +.label { + font-size: 14px; + color: #ffffff; + padding: 0px; + padding-right: 50px; + border-radius: 5px; + text-align: center; + text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2); +} \ No newline at end of file