Skip to content

Commit e2c4095

Browse files
committed
Merge branch 'dev'
2 parents 2e15ded + 9b1555b commit e2c4095

29 files changed

+1081
-272
lines changed

.releaserc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
{
4646
"path": "dist/untis.js",
4747
"label": "${branch.prerelease ? '⚠️ BETA ⚠️ ' : ''}Scriptable Untis Widget ${nextRelease.gitTag}",
48-
"name": "UntisWidget${branch.prerelease ? '-Beta' : ''}.scriptable"
48+
"name": "UntisWidget${branch.prerelease ? '-Beta' : ''}.js"
4949
}
5050
]
5151
}

README.md

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@ iOS Widgets for [Untis](https://webuntis.com/) using [Scriptable](https://script
77

88
## ❔ What is this for?
99

10-
Untis is a service used by many students to keep track of their school-life, especially for checking the timetable.
11-
There is a mobile app, but it does not provide any widgets.
10+
Untis is a service used by many students (mainly in Austria and Germany) to keep track of their school-life, especially for checking the timetable.
11+
There is a mobile app, but it does not provide any widgets, at least on iOS.
1212
That's why I decided to create those widgets myself, so I can have the most important information directly on my homescreen.
13+
It also notifies me about the most important changes.
1314

1415
## ✨ Features
1516

1617
The script allows you to display data for one WebUntis account in a widget, and will notify you about important changes.\
1718
You can set what information (views) should be displayed for each widget.\
1819
To safe battery and bandwidth, the data is updated at the start of every lesson and every few hours outside of school-hours.\
1920
The fetched data is cached for some time, so the script takes less time, bandwidth and battery to run.\
20-
Many things can be configured via the visual config editor or the configuration file `untis-config.json` in the scriptable folder, check the [configuration section](#️-configuration) for details.
21+
Many things can be configured via the visual config editor or the configuration file `untis-config.json` in the scriptable folder, check the [configuration section](#️-configuration) for details.
2122

2223
### 🔔 Notifications
2324

@@ -41,86 +42,102 @@ After a school day has ended, a summary of the next day is displayed. (list of s
4142
#### Exams
4243

4344
Shows the upcoming exams, meaning their subject and date.
45+
By default, exams are colored according to the subject configuration. The widget displays as much information as fits.
4446
You can configure how many days ahead the exams should be displayed and set a maximum count.
4547

4648
#### Grades
4749

4850
Displays the recent grades you received. (subject + mark)
51+
By default, grades are colored according to the subject configuration. The widget displays as much information as fits.
4952
You can configure how long grades should be displayed and set a maximum count.
5053

5154
#### Absences
5255

53-
Lists open (unexcused) absences with their date and duration.
56+
Lists open (unexcused) absences with their date and duration. It also shows who created the absence, if there is enough space.
5457

5558
## 📥 Installation
5659

57-
1. Before installing the script, you need to [install Scriptable from the AppStore](https://apps.apple.com/us/app/scriptable/id1405459188?uo=4).
60+
1. Before installing the script, you need to [install Scriptable from the App Store](https://apps.apple.com/us/app/scriptable/id1405459188?uo=4).
5861

5962
2. To install the script, head over to the [releases page](https://github.com/JFK-05/scriptable-untis/releases) select the latest release.\
60-
If you always want to check out the latest features, which might not be stable, you can download the beta release.\
61-
Alternatively, you can download the latest stable release [here](https://github.com/JFK-05/scriptable-untis/releases/latest/download/UntisWidget.scriptable).
63+
Alternatively, you can directly download the latest stable release [here](https://github.com/JFK-05/scriptable-untis/releases/latest/download/UntisWidget.scriptable).
6264

63-
3. After downloading the file, you can click to open it with scriptable.
64-
This will add the script to the list.
65+
3. After downloading the file, click share, and select scriptable.
66+
Now click `Add to My Scripts` at the bottom.
6567

66-
4. Open Scriptable, click the script to run it.
67-
You will be asked what you want to do.
68-
Select `🔑 Change Credentials` and enter your WebUntis credentials as prompted.\
69-
You will be asked for the url, which you can find when selecting you school [here](https://arche.webuntis.com/WebUntis/?school=litec#/basic/login). You might have to click the "switch school" button to get to the correct page. The url should look something like this: `https://<server>.webuntis.com/WebUntis/?school=<schoolname>#/basic/login`. (with `<server>` and `<schoolname>` replaced)
68+
4. Click the play button at the lower right (or click the script in the overview) to run the script. This will start the setup process.\
69+
You will be asked for the url, which you can find when selecting you school [here](https://webuntis.com/). You might have to click the "switch school" button to get to the correct page.\
70+
The url should look something like this: `https://<server>.webuntis.com/WebUntis/?school=<schoolname>#/basic/login`. (with `<server>` and `<schoolname>` replaced)
7071

71-
5. Now you can add an IOS widget for Scriptable and select the script in the options.
72+
5. Now you can add an iOS widget for Scriptable and select the script in the options.
7273

73-
6. The widget should update automatically and display the information. 🥳
74+
6. The widget should update automatically and display the information. 🥳 (might take a few seconds)
7475

75-
7. If you want to customize the widget (e.g. color the lessons, see the [configuration section](#️-configuration))
76+
7. If you want to customize the widget (e.g. color the lessons), see the [configuration section](#️-configuration).
7677

77-
## ⚙️ Configuration
78+
## ⏫ Updating
79+
80+
The script will check for updates every few hours.\
81+
Regular updates will be downloaded and installed automatically.\
82+
If you want to update manually, you can do so by running the script and selecting `🔄 Update Script` in the menu.\
83+
When there is a breaking change, you will be notified and asked to update manually.\
84+
Check the [releases page](https://github.com/JFK-05/scriptable-untis/releases) for more information about the changes.
85+
86+
## ⚒️ Configuration
7887

7988
When long-pressing the widget, you see the iOS widget configuration.
8089
In the `Parameter` field, you can enter the configuration for the widget.
8190
The configuration is a list of the views you want to be displayed, separated by a comma.\
8291
For larger widgets, you can create multiple columns by separating the views with a pipe (`|`).\
8392
Example: `lessons,exams` or `lessons|exams,grades,absences`
8493

85-
### ⚒️ Config Editor
94+
### ⚙️ Settings Editor
8695

87-
The config editor is a UI utility, so you don't have to deal with the configuration file directy.\
88-
You can open it by running the script and selecting `⚒️ Config Editor` in the menu.\
96+
The config editor is a UI utility, so you don't have to deal with the configuration file directly.\
97+
You can open it by running the script and selecting `⚙️ Open Settings` in the menu.\
8998
It will open up a list with settings categories. The top row shows you where you are in the menu, below there are the categories and settings.\
9099
Clicking a category will open it, clicking a setting will open a dialog to edit it.\
100+
If you change a value, the default will be displayed below it in gray.\
91101
You can use the `↩️` button to reset a setting to its default value.
92102

93103
#### 📚 Subjects Config
94104

95105
The subjects config allows you to configure e.g. the colors of the lessons.\
96106
It is accessible in the config editor under `📚 Subjects Config`.\
97-
You will now see a list of all subjects you have configurations for. (might be empty)
107+
You will now see a list of all subjects you have configurations for, listed by their short name. (might be empty)\
108+
You can delete a subject config by clicking the trash emoji (🗑️).
109+
110+
By clicking a subject, you can open its specific configuration.
98111

99112
Here are the configuration options:
100113

101-
- `color`: The color of the lesson. This can be a hex code (like `#ff0000`) or a color name (like `red`).
114+
- `color`: The color of the lesson. You can either enter a hex code (like `#ff0000`) at the top, or select one from the palette.
102115
To find the perfect color, you can use [this website](https://htmlcolorcodes.com/color-picker/).
103116
Darker, less saturated colors are recommended, as this makes it easier to read the white text on the background.
104-
- `teacher`: The name of the teacher to apply this config for. This is only available, if you specified a teacher when adding the subject.
105-
- `custom short name`: The short name of the subject to display.
106-
- `custom long name`: The name of the subject to display if there is enough space (and the option is enabled).
117+
- `short name`: The short name of the subject to display.
118+
- `long name`: The name of the subject to display if there is enough space (and the option is enabled).
107119
- `ignore infos`: A list of lesson infos to ignore. This can be useful if there is lesson info which is always there.
108120
This should be a comma-separated list of the infos to ignore, wrapped in double quotes. (`"infoName"` or `"info1", "info2"`)
109121

110-
#### Adding a new subject
122+
##### Adding a new subject
111123

112124
At the top you can add a new subject configuration. You will have to enter the `short subject name` (the one displayed in webuntis.)\
113125
If you want to only apply the configuration when a specific teacher is teaching the subject, you can enter the `teacher` name as well.
114126
This can be helpful when there are subjects which are combined in WebUntis.
115127

128+
##### 🧑‍🏫 Teacher specific configs
129+
130+
Some subjects may be combined in Untis. For example, geography and history may be combined.\
131+
In the teachers section of a subject, you can create configs for specific teachers.\
132+
When this teachers now teaches this subject, the configuration of the teacher will override the one from the subject.
133+
116134
### 📄 Config File
117135

118136
The configuration file `untis-config.json` is located in the scriptable folder.\
119137
A default configuration file is created when the script is run for the first time. Check it out to see what's possible!\
120-
Changes to the configuration file are applied when the script is run again.\
121-
I am aware that editing this json file is not the most user-friendly way, I am considering adding a visual configuration tool.
138+
Changes to the configuration file are applied when the script is run again.
122139

123-
## 🧑‍💻 A Note for Developers
140+
## 🧑‍💻 Notes for Developers
124141

125142
### 📦 Bundling
126143

@@ -134,4 +151,10 @@ That's why I switched to [rollup](https://rollupjs.org/), which worked perfectly
134151
### ⚙️ Config Editor
135152

136153
The config editor is a handy utility which makes the widget easier to use for people, who are not into editing config files.\
137-
But of the limitations of scriptable, and as the implementation had to be quite generalizable, the code is a little complex.
154+
But of the limitations of scriptable, and as the implementation had to be quite generalizable, the code is a little complex.\
155+
I am quite happy with the result, even though it has a few flaws, which I might address in the future.
156+
157+
### 📖 Documentation
158+
159+
Currently there seems to be an issue with github (similar to [this one](https://github.com/thlorenz/anchor-markdown-header/issues/36)), where emojis in titles break header links.\
160+
Therefore the header anchors in this readme may not work.

index.ts

Lines changed: 68 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,34 @@
11
import { clearCache, prepareUser } from '@/api/cache'
2-
import { PREVIEW_WIDGET_SIZE, SCRIPT_START_DATETIME } from '@/constants'
2+
import {
3+
GITHUB_REPO,
4+
GITHUB_SCRIPT_NAME,
5+
GITHUB_USER,
6+
PREVIEW_WIDGET_SIZE,
7+
SCRIPT_START_DATETIME,
8+
UPDATE_INTERVAL,
9+
} from '@/constants'
310
import { getLayout } from '@/layout'
411
import { openSettings } from '@/settings/editor/settingsEditor'
5-
import { writeKeychain } from '@/setup'
6-
import { createErrorWidget, ExtendedError, SCRIPTABLE_ERROR_MAP } from '@/utils/errors'
12+
import { fillLoginDataInKeychain } from '@/setup'
13+
import { createErrorWidget, ExtendedError, handleError, isExtendedError, SCRIPTABLE_ERROR_MAP } from '@/utils/errors'
714
import { getModuleFileManager as getFileManagerOptions, readConfig } from '@/utils/scriptable/fileSystem'
8-
import { selectOption } from '@/utils/scriptable/input'
15+
import { selectOption, showInfoPopup } from '@/utils/scriptable/input'
16+
import { KeychainManager } from '@/utils/scriptable/keychainManager'
17+
import { checkForUpdates, checkForUpdatesWith, shouldCheckForUpdates } from '@/utils/updater'
918
import { createWidget } from '@/widget'
1019

11-
// TODO: Auto-Update
12-
// store the last update date in the keychain
13-
// check every day (store the last check date in the keychain)
14-
// compare the last update date with the date from the github api
20+
// initialize the keychain manager
21+
new KeychainManager('untis')
22+
23+
// check for updates (in a try-catch block to prevent the script from crashing if the update fails)
24+
try {
25+
if (shouldCheckForUpdates(UPDATE_INTERVAL)) {
26+
await checkForUpdates()
27+
}
28+
} catch (error) {
29+
console.error('⏫❌ Could not check for updates.')
30+
log(error)
31+
}
1532

1633
async function setupAndCreateWidget() {
1734
const { useICloud, fileManager } = getFileManagerOptions()
@@ -21,51 +38,61 @@ async function setupAndCreateWidget() {
2138
return widget
2239
}
2340

41+
async function presentWidget() {
42+
const widget = await setupAndCreateWidget()
43+
switch (PREVIEW_WIDGET_SIZE) {
44+
case 'small':
45+
widget.presentSmall()
46+
break
47+
case 'medium':
48+
widget.presentMedium()
49+
break
50+
case 'large':
51+
widget.presentLarge()
52+
break
53+
}
54+
}
55+
56+
function showDocumentation() {
57+
console.log('📖 Opening documentation in Safari.')
58+
Safari.openInApp('https://github.com/JFK-05/scriptable-untis#readme')
59+
}
60+
2461
enum ScriptActions {
2562
VIEW = '💻 Show Widget',
26-
CHANGE_CREDENTIALS = '🔑 Change Credentials',
27-
EDIT_CONFIG = '🛠️ Edit Config',
28-
CLEAR_CACHE = '🗑️ Clear Cache',
63+
OPEN_SETTINGS = '⚙️ Open Settings',
64+
// CHANGE_CREDENTIALS = '🔑 Change Credentials',
65+
// UPDATE = '⏫ Update Script',
66+
// CLEAR_CACHE = '🗑️ Clear Cache',
2967
SHOW_DOCUMENTATION = '📖 Open Documentation',
3068
}
3169

70+
const actionMap: Record<ScriptActions, Function> = {
71+
[ScriptActions.VIEW]: presentWidget,
72+
// [ScriptActions.CHANGE_CREDENTIALS]: fillLoginDataInKeychain,
73+
[ScriptActions.OPEN_SETTINGS]: openSettings,
74+
// [ScriptActions.CLEAR_CACHE]: clearCache,
75+
[ScriptActions.SHOW_DOCUMENTATION]: showDocumentation,
76+
// [ScriptActions.UPDATE]: checkForUpdates,
77+
}
78+
3279
async function runInteractive() {
3380
const actions = Object.values(ScriptActions).filter((item) => {
3481
return isNaN(Number(item))
3582
})
3683

37-
const input = await selectOption(actions, {
84+
const input = (await selectOption(actions, {
3885
title: 'What do you want to do?',
39-
})
86+
})) as ScriptActions | null
4087

41-
switch (input) {
42-
case ScriptActions.VIEW:
43-
const widget = await setupAndCreateWidget()
44-
switch (PREVIEW_WIDGET_SIZE) {
45-
case 'small':
46-
widget.presentSmall()
47-
break
48-
case 'medium':
49-
widget.presentMedium()
50-
break
51-
case 'large':
52-
widget.presentLarge()
53-
break
54-
}
55-
break
56-
case ScriptActions.CHANGE_CREDENTIALS:
57-
await writeKeychain()
58-
break
59-
case ScriptActions.EDIT_CONFIG:
60-
await openSettings()
61-
break
62-
case ScriptActions.CLEAR_CACHE:
63-
clearCache()
64-
break
65-
case ScriptActions.SHOW_DOCUMENTATION:
66-
Safari.openInApp('https://github.com/JFK-05/scriptable-untis#readme')
67-
break
88+
const action = actionMap[input]
89+
90+
if (!action) {
91+
console.log('No action selected, exiting.')
92+
return
6893
}
94+
95+
await action()
6996
}
7097

7198
try {
@@ -76,28 +103,7 @@ try {
76103
await runInteractive()
77104
}
78105
} catch (error) {
79-
// throw the error if it runs in the app
80-
if (config.runsInApp) {
81-
throw error
82-
}
83-
84-
let widget: ListWidget
85-
const castedError = error as Error
86-
87-
const scriptableError = SCRIPTABLE_ERROR_MAP[castedError.message.toLowerCase()]
88-
log(scriptableError)
89-
if (scriptableError) {
90-
widget = createErrorWidget(scriptableError.title, scriptableError.description, scriptableError.icon)
91-
} else {
92-
const extendedError = error as ExtendedError
93-
widget = createErrorWidget(extendedError.name, extendedError.message, extendedError.icon)
94-
}
95-
96-
if (!config.runsInWidget) {
97-
widget.presentLarge()
98-
}
99-
100-
Script.setWidget(widget)
106+
handleError(error)
101107
}
102108

103109
console.log(`Script finished in ${new Date().getTime() - SCRIPT_START_DATETIME.getTime()}ms.`)

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
"@semantic-release/release-notes-generator": "^10.0.3",
1414
"semantic-release": "^21.0.1",
1515
"@rollup/plugin-typescript": "^11.1.0",
16-
"rollup": "^3.20.2",
16+
"@types/scriptable-ios": "^1.7.0",
17+
"rollup": "^3.20.3",
1718
"tslib": "^2.5.0",
18-
"typescript": "^5.0.3",
19-
"@types/scriptable-ios": "^1.7.0"
19+
"typescript": "^5.0.4"
2020
}
2121
}

0 commit comments

Comments
 (0)