diff --git a/README.md b/README.md index b3ce2dc..027f124 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Clicking the button opens 10bis.co.il in your default browser. ### Todos - [x] Push to Git - [x] Submit to JetBrains Repo +- [x] Order Food Reminder - [ ] Add Installation Instructions -- [ ] Order Food Reminder ### Installation Instructions @@ -25,3 +25,7 @@ Coming Soon! ### Dracula Theme ![Dracula Theme](readme/screenshot_dracula.png) + + +### Order Reminder +![Dracula Theme](readme/screenshot_reminder.png) diff --git a/build.gradle b/build.gradle index e6f8572..7586497 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'com.fimaworks' -version '1.0.1' +version '1.0.2' sourceCompatibility = 1.8 @@ -30,6 +30,7 @@ compileTestKotlin { } patchPluginXml { changeNotes """ - V1.0 + V1.0.2 + Added Daily Reminder """ } \ No newline at end of file diff --git a/readme/screenshot_reminder.png b/readme/screenshot_reminder.png new file mode 100644 index 0000000..98d4c6d Binary files /dev/null and b/readme/screenshot_reminder.png differ diff --git a/releases/jetbrains-10bis-plugin-1.0.1.zip b/releases/jetbrains-10bis-plugin-1.0.1.zip deleted file mode 100644 index 81ed9f6..0000000 Binary files a/releases/jetbrains-10bis-plugin-1.0.1.zip and /dev/null differ diff --git a/releases/jetbrains-10bis-plugin-1.0.zip b/releases/jetbrains-10bis-plugin-1.0.zip deleted file mode 100644 index 0dee363..0000000 Binary files a/releases/jetbrains-10bis-plugin-1.0.zip and /dev/null differ diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/Constants.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/Constants.kt new file mode 100644 index 0000000..9b8c941 --- /dev/null +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/Constants.kt @@ -0,0 +1,5 @@ +package com.fimaworks.jetbrains.tenbis + +object Constants { + const val browserUrl = "https://www.10bis.co.il/" +} \ No newline at end of file diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/OrderFoodAction.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/OrderFoodAction.kt index c3bba9e..914d4e1 100644 --- a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/OrderFoodAction.kt +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/OrderFoodAction.kt @@ -7,7 +7,11 @@ import com.intellij.openapi.actionSystem.AnActionEvent class OrderFoodAction : AnAction() { override fun actionPerformed(p0: AnActionEvent) { // doesn't get any simpler than this - BrowserUtil.browse("https://www.10bis.co.il/") + BrowserUtil.browse(Constants.browserUrl) } + // should the button be hidden after the order was made? + override fun update(e: AnActionEvent) { + super.update(e) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/extensions/LocalDateTime.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/extensions/LocalDateTime.kt new file mode 100644 index 0000000..b940783 --- /dev/null +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/extensions/LocalDateTime.kt @@ -0,0 +1,51 @@ +package com.fimaworks.jetbrains.tenbis.extensions + +import java.time.LocalDateTime +import java.time.ZoneOffset + + +/** + * Check if @LocalDateTime is after a certain hour of the day + */ +fun LocalDateTime.isAfter(hour: Int, minutes: Int): Boolean { + return this.isAfter( + this + .withHour(hour) + .withMinute(minutes) + .withSecond(0) + .withNano(0) + ) +} + +/** + * is this @LocalDateTime is today + */ +fun LocalDateTime.isToday(): Boolean { + + val todayStart = LocalDateTime + .now() + .withHour(0) + .withMinute(0) + .withSecond(0) + .withNano(0) + + val todayEnd = todayStart.plusDays(1) + + val millis = this.millis + + if (millis >= todayStart.millis + && millis < todayEnd.millis + ) { + return true + } + + return false +} + +/** + * quick conversion to millis + */ +val LocalDateTime.millis: Long + get() { + return this.toInstant(ZoneOffset.UTC).toEpochMilli() + } \ No newline at end of file diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderComponent.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderComponent.kt new file mode 100644 index 0000000..3d4eafe --- /dev/null +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderComponent.kt @@ -0,0 +1,80 @@ +package com.fimaworks.jetbrains.tenbis.reminder + +import com.fimaworks.jetbrains.tenbis.extensions.isAfter +import com.fimaworks.jetbrains.tenbis.extensions.isToday +import com.intellij.openapi.components.BaseComponent +import java.time.LocalDateTime +import java.util.* +import java.util.concurrent.TimeUnit + + +class ReminderComponent : BaseComponent { + + private var lastReminder = ReminderConfigurationProvider.instance.state.lastReminder + + private val timer = Timer("10bis_reminder_timer") + + private val timerTask = + ReminderTimerTask(object : + LastReminderListener { + override fun updateLastReminder(now: LocalDateTime) { + this@ReminderComponent.lastReminder = now + ReminderConfigurationProvider.instance.state.lastReminder = now + + } + + override val lastReminder: LocalDateTime + get() = this@ReminderComponent.lastReminder + }) + + override fun initComponent() { + super.initComponent() + + // delay first trigger by 15 seconds + val initialDelayInMillis = TimeUnit.SECONDS.toMillis(15) + + // schedule task every 30 seconds + val periodInMillis = TimeUnit.SECONDS.toMillis(30) + + timer.schedule( + timerTask, + Date(System.currentTimeMillis() + initialDelayInMillis), + periodInMillis + ) + } + + override fun disposeComponent() { + timerTask.cancel() + timer.cancel() + super.disposeComponent() + } + + class ReminderTimerTask(private val lastReminderListener: LastReminderListener) : TimerTask() { + override fun run() { + + val isRemindedForToday = lastReminderListener.lastReminder.isToday() + + val configState = ReminderConfigurationProvider.instance.state + + val reminderHour = configState.reminderHour + val reminderMinutes = configState.reminderMinutes + + val isAfterReminderTime = LocalDateTime.now().isAfter(reminderHour, reminderMinutes) + + if (isRemindedForToday.not() && isAfterReminderTime) { + + // todo - add notification sound? + + // ui notification + ReminderNotification.notifyUser(lastReminderListener) + + } + } + } + + interface LastReminderListener { + fun updateLastReminder(now: LocalDateTime) + + val lastReminder: LocalDateTime + } +} diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderConfigurable.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderConfigurable.kt new file mode 100644 index 0000000..342d56a --- /dev/null +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderConfigurable.kt @@ -0,0 +1,87 @@ +package com.fimaworks.jetbrains.tenbis.reminder + +import com.intellij.openapi.Disposable +import com.intellij.openapi.options.Configurable +import com.intellij.openapi.options.Configurable.NoScroll +import java.text.NumberFormat +import javax.swing.* +import javax.swing.text.NumberFormatter + + +class ReminderConfigurable : Configurable, NoScroll, Disposable { + + private val hourFormatter = NumberFormatter(NumberFormat.getIntegerInstance()).also { + it.minimum = 0 + it.maximum = 23 + it.allowsInvalid = true + } + + private val minutesFormatter = NumberFormatter(NumberFormat.getIntegerInstance()).also { + it.minimum = 0 + it.maximum = 59 + it.allowsInvalid = true + } + private val configState + get() = ReminderConfigurationProvider.instance.state + + // ui + private var hourField: JFormattedTextField? = + JFormattedTextField(hourFormatter).also { it.text = configState.reminderHour.toString() } + + private var minutesField: JTextField? = + JFormattedTextField(minutesFormatter).also { it.text = configState.reminderMinutes.toString() } + + override fun getDisplayName(): String = "10bis Plugin Configuration" + + override fun createComponent(): JComponent? { + val dialogPanel = JPanel() + + val timeLabel = JLabel("Reminder Time") + + dialogPanel.add(timeLabel) + dialogPanel.add(createTimeSetting()) + + return dialogPanel + } + + // todo - need to fix ui + private fun createTimeSetting(): JPanel { + + val timePanel = JPanel() + + timePanel.add(hourField) + timePanel.add(JLabel(" : ")) // 10:00 seperator + timePanel.add(minutesField) + + return timePanel + } + + override fun dispose() { + hourField = null + minutesField = null + } + + override fun isModified(): Boolean { + + return configState.reminderHour != hourField!!.text.toIntOrNull() + || configState.reminderMinutes != minutesField!!.text.toIntOrNull() + } + + override fun apply() { + hourField!!.text.toIntOrNull()?.let { + if (it in 0..23) + configState.reminderHour = it + + } + + minutesField!!.text.toIntOrNull()?.let { + if (it in 0..59) + configState.reminderMinutes = it + } + } + + override fun reset() { + hourField!!.text = configState.reminderHour.toString() + minutesField!!.text = configState.reminderMinutes.toString() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderConfigurationProvider.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderConfigurationProvider.kt new file mode 100644 index 0000000..c39f63c --- /dev/null +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderConfigurationProvider.kt @@ -0,0 +1,36 @@ +package com.fimaworks.jetbrains.tenbis.reminder + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.util.xmlb.XmlSerializerUtil +import java.time.LocalDateTime + +@State( + name = "ReminderConfigurationProvider", + storages = [Storage("10bis-plugin.xml")] +) +open class ReminderConfigurationProvider : PersistentStateComponent { + + private var myState: ConfigurationState = ConfigurationState() + + override fun getState(): ConfigurationState { + return myState + } + + override fun loadState(state: ConfigurationState) { + myState = state + } + + class ConfigurationState { + var lastReminder: LocalDateTime = LocalDateTime.now().minusDays(1) + var reminderHour = 11 + var reminderMinutes = 0 + } + + companion object { + val instance: ReminderConfigurationProvider + get() = ServiceManager.getService(ReminderConfigurationProvider::class.java) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderNotification.kt b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderNotification.kt new file mode 100755 index 0000000..cf07b1e --- /dev/null +++ b/src/main/kotlin/com/fimaworks/jetbrains/tenbis/reminder/ReminderNotification.kt @@ -0,0 +1,53 @@ +package com.fimaworks.jetbrains.tenbis.reminder + +import com.fimaworks.jetbrains.tenbis.Constants +import com.intellij.ide.BrowserUtil +import com.intellij.notification.* +import java.time.LocalDateTime + +object ReminderNotification { + + fun notifyUser(lastReminderListener: ReminderComponent.LastReminderListener) { + // prep message + val message = escapeString( + // order button + "Order Now" + + // six spaces + "      " + + // dismiss buton + "Dismiss" + )!! + + // create notificaiton + val notification = NotificationGroup("10bis Plugin", NotificationDisplayType.STICKY_BALLOON, false) + .createNotification( + "Did you remember to order food?", + message, + NotificationType.WARNING + ) { notification, event -> + if (event != null) { + if (event.url != null) { + BrowserUtil.browse(event.url) + } + } + + // dismiss notification + notification?.expire() + + } + + notification.whenExpired { + // update last reminder to now + lastReminderListener.updateLastReminder(LocalDateTime.now()) + } + + // ping user + notification.notify(null) + } + + private fun escapeString(string: String?): String? { + return if (string == null || !string.contains("\n")) { + string + } else string.replace("\n".toRegex(), "\n
") + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 353c9e1..d9b903e 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -22,4 +22,24 @@ + + + + + com.fimaworks.jetbrains.tenbis.reminder.ReminderComponent + + + + + + + + + + + + \ No newline at end of file