diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8eb2b2..429aa3a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,6 +2,8 @@ name: Create Release on: push: + branches: + - main paths: - index.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 57c6fbe..068f59a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Plugin Manager (dd-mm-yyyy) +### 1.0.17 (22-04-2024) + +- Added ChangeLog Window to view latest changes. + ### 1.0.16 (22-04-2024) - Fix for error caused when disable button was missing diff --git a/index.json b/index.json index 8e02d1c..bae8046 100644 --- a/index.json +++ b/index.json @@ -1,6 +1,12 @@ { "plugin_manager_url": "https://github.com/bombsquad-community/plugin-manager/{content_type}/{tag}/plugin_manager.py", "versions": { + "1.0.17": { + "api_version": 8, + "commit_sha": "02530f0", + "released_on": "22-04-2024", + "md5sum": "b5f3902046731ae4cc5bc88586203238" + }, "1.0.16": { "api_version": 8, "commit_sha": "0839999", diff --git a/plugin_manager.py b/plugin_manager.py index 77256f0..bea4f69 100644 --- a/plugin_manager.py +++ b/plugin_manager.py @@ -31,7 +31,7 @@ from datetime import datetime from threading import Thread import logging -PLUGIN_MANAGER_VERSION = "1.0.16" +PLUGIN_MANAGER_VERSION = "1.0.17" REPOSITORY_URL = "https://github.com/bombsquad-community/plugin-manager" # Current tag can be changed to "staging" or any other branch in # plugin manager repo for testing purpose. @@ -71,6 +71,7 @@ _env = _babase.env() _uiscale = bui.app.ui_v1.uiscale INDEX_META = "{repository_url}/{content_type}/{tag}/index.json" +CHANGELOG_META = "{repository_url}/{content_type}/{tag}/CHANGELOG.md" HEADERS = { "User-Agent": _env["legacy_user_agent_string"], } @@ -894,6 +895,73 @@ class Plugin: bui.getsound('error').play() +class ChangelogWindow(popup.PopupWindow): + def __init__(self, origin_widget): + self.scale_origin = origin_widget.get_screen_space_center() + bui.getsound('swish').play() + s = 1.65 if _uiscale is babase.UIScale.SMALL else 1.39 if _uiscale is babase.UIScale.MEDIUM else 1.67 + width = 400 * s + height = width * 0.5 + color = (1, 1, 1) + text_scale = 0.7 * s + self._transition_out = 'out_scale' + transition = 'in_scale' + + self._root_widget = bui.containerwidget(size=(width, height), + on_outside_click_call=self._back, + transition=transition, + scale=(1.5 if _uiscale is babase.UIScale.SMALL else 1.5 + if _uiscale is babase.UIScale.MEDIUM else 1.0), + scale_origin_stack_offset=self.scale_origin) + + bui.textwidget(parent=self._root_widget, + position=(width * 0.49, height * 0.87), size=(0, 0), + h_align='center', v_align='center', text='ChangeLog', + scale=text_scale * 1.25, color=bui.app.ui_v1.title_color, + maxwidth=width * 0.9) + + back_button = bui.buttonwidget( + parent=self._root_widget, + position=(width * 0.1, height * 0.8), + size=(60, 60), + scale=0.8, + label=babase.charstr(babase.SpecialChar.BACK), + # autoselect=True, + button_type='backSmall', + on_activate_call=self._back) + + bui.containerwidget(edit=self._root_widget, cancel_button=back_button) + + bui.textwidget(parent=self._root_widget, + position=(width * 0.49, height * 0.72), size=(0, 0), + h_align='center', v_align='center', text=PLUGIN_MANAGER_VERSION, + scale=text_scale * 1.1, color=color, + maxwidth=width * 0.9) + + bui.buttonwidget( + parent=self._root_widget, + position=(width * 0.7, height * 0.72 - 20), + size=(140, 60), + scale=0.8, + label='Full ChangeLog', + button_type='square', + on_activate_call=lambda: bui.open_url(REPOSITORY_URL + '/blob/main/CHANGELOG.md')) + + logs = _CACHE['changelog'].split('\n') + loop_height = height * 0.62 + for log in logs: + bui.textwidget(parent=self._root_widget, + position=(width * 0.05, loop_height), size=(0, 0), + h_align='left', v_align='top', text=log, + scale=text_scale, color=color, + maxwidth=width * 0.9) + loop_height -= 35 + + def _back(self) -> None: + bui.getsound('swish').play() + bui.containerwidget(edit=self._root_widget, transition='out_scale') + + class AuthorsWindow(popup.PopupWindow): def __init__(self, authors_info, origin_widget): self.authors_info = authors_info @@ -1036,7 +1104,8 @@ class PluginWindow(popup.PopupWindow): size=(len(text)*14, 20), label='', texture=bui.gettexture("empty"), - on_activate_call=lambda: AuthorsWindow(self.plugin.info["authors"], self._root_widget)) + on_activate_call=lambda: + AuthorsWindow(self.plugin.info["authors"], self._root_widget)) bui.textwidget(parent=self._root_widget, position=(width * 0.49 - (len(text)*14/2), pos - 10), size=(len(text)*14, 20), @@ -1283,9 +1352,11 @@ class PluginManager: def __init__(self): self.request_headers = HEADERS self._index = _CACHE.get("index", {}) + self._changelog = _CACHE.get("changelog", {}) self.categories = {} self.module_path = sys.modules[__name__].__file__ self._index_setup_in_progress = False + self._changelog_setup_in_progress = False async def get_index(self): if not self._index: @@ -1313,6 +1384,36 @@ class PluginManager: await self.setup_plugin_categories(index) self._index_setup_in_progress = False + async def get_changelog(self) -> str: + if not self._changelog: + request = urllib.request.Request(CHANGELOG_META.format( + repository_url=REPOSITORY_URL, + content_type="raw", + tag=CURRENT_TAG + ), + headers=self.request_headers) + response = await async_send_network_request(request) + self._changelog = response.read().decode() + return self._changelog + + async def setup_changelog(self, version=None) -> None: + if version is None: + version = PLUGIN_MANAGER_VERSION + while self._changelog_setup_in_progress: + # Avoid making multiple network calls to the same resource in parallel. + # Rather wait for the previous network call to complete. + await asyncio.sleep(0.1) + self._changelog_setup_in_progress = not bool(self._changelog) + full_changelog = await self.get_changelog() + pattern = rf"### {version} \(\d\d-\d\d-\d{{4}}\)\n(.*?)(?=### \d+\.\d+\.\d+|\Z)" + matches = re.findall(pattern, full_changelog, re.DOTALL) + if matches: + changelog = matches[0].strip() + else: + changelog = f"Changelog entry for version {version} not found." + self.set_changelog_global_cache(changelog) + self._changelog_setup_in_progress = False + async def setup_plugin_categories(self, plugin_index): # A hack to have the "All" category show at the top. self.categories["All"] = None @@ -1355,10 +1456,14 @@ class PluginManager: async def refresh(self): self.cleanup() await self.setup_index() + await self.setup_changelog() def set_index_global_cache(self, index): _CACHE["index"] = index + def set_changelog_global_cache(self, changelog): + _CACHE["changelog"] = changelog + def unset_index_global_cache(self): try: del _CACHE["index"] @@ -1740,6 +1845,7 @@ class PluginManagerWindow(bui.Window): self.draw_category_selection_button(post_label="All") self.draw_refresh_icon() self.draw_settings_icon() + await self.plugin_manager.setup_changelog() with self.exception_handler(): await self.plugin_manager.setup_index() bui.textwidget(edit=self._plugin_manager_status_text, @@ -2132,6 +2238,25 @@ class PluginManagerSettingsWindow(popup.PopupWindow): maxwidth=width * 0.9) pos -= 20 + self._changelog_button = bui.buttonwidget(parent=self._root_widget, + position=((width * 0.2) - button_size[0] / 2 - 5, + pos), + size=(80, 30), + on_activate_call=lambda: + ChangelogWindow(self._root_widget), + textcolor=b_text_color, + button_type='square', + label='') + bui.textwidget(parent=self._root_widget, + position=((width * 0.2) - button_size[0] / 2, pos), + size=(70, 30), + scale=0.6, + h_align='center', + v_align='center', + text='ChangeLog', + color=b_text_color, + draw_controller=self._changelog_button, + ) self._save_button = bui.buttonwidget(parent=self._root_widget, position=((width * 0.82) - button_size[0] / 2, pos), size=(73, 35), @@ -2283,6 +2408,7 @@ class PluginManagerSettingsWindow(popup.PopupWindow): async def update(self, to_version=None, commit_sha=None): try: await self._plugin_manager.update(to_version, commit_sha) + await self._plugin_manager.setup_changelog() except MD5CheckSumFailed: bui.screenmessage("MD5 checksum failed during plugin manager update", color=(1, 0, 0)) bui.getsound('error').play()