mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 01:14:15 +02:00
app suggenstions when no app is installed
This commit is contained in:
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- a confirmation popup is shown when the "install" / "upgrade all" button is clicked
|
||||
- "About" button added to the manage panel
|
||||
- "Updates" icon left-aligned
|
||||
- show application suggestions when no app is installed. Can be enabled / disabled through the environment variable / parameter FPAKMAN_SUGGESTIONS (--sugs)
|
||||
- minor GUI improvements
|
||||
### Fixes:
|
||||
- apps table not showing empty descriptions
|
||||
|
||||
@@ -53,6 +53,7 @@ You can change some application settings via environment variables or arguments
|
||||
- **FPAKMAN_SNAP**: enables / disables snap usage. Use **0** (disable) or **1** (enabled, default)
|
||||
- **FPAKMAN_CHECK_PACKAGING_ONCE**: If the available supported packaging types should be checked ONLY once. It improves the application speed if enabled, but can generate errors if you uninstall any packaging technology while using it, and every time a supported packaging type is installed it will only be available after a restart. Use **0** (disable, default) or **1** (enable).
|
||||
- **FPAKMAN_TRAY**: If the tray icon and update-check daemon should be created. Use **0** (disable) or **1** (enable, default).
|
||||
- **FPAKMAN_SUGGESTIONS**: If application suggestions should be displayed if no app is installed (runtimes do not count as apps). Use **0** (disable) or **1** (enable, default).
|
||||
|
||||
### How to improve the application performance
|
||||
- If you don't care about a specific packaging technology and don't want **fpakman** to deal with it, just disable it via the specific argument or environment variable. For instance, if I don't care
|
||||
@@ -60,6 +61,7 @@ about **snaps**, I can initialize the application setting "snap=0" (**fpakman --
|
||||
with the technology that I don't care every time an action is executed.
|
||||
- If you don't care about restarting **fpakman** every time a new supported packaging technology is installed, set "check-packaging-once=1" (**fpakman --check-packaging-once=1**). This can reduce the application response time up to 80% in some scenarios, since it won't need to recheck if the packaging type is available for every action you request.
|
||||
- If you don't mind to see the applications icons, you can set "download-icons=0" (**fpakman --download-icons=0**). The application may have a slight response improvement, since it will reduce the parallelism within it.
|
||||
- If you don't mind app suggestions, disable it (**fpakman --sugs=0**)
|
||||
|
||||
### Roadmap
|
||||
- Support for other packaging technologies
|
||||
|
||||
@@ -47,6 +47,7 @@ parser.add_argument('--flatpak', action="store", default=os.getenv('FPAKMAN_FLAT
|
||||
parser.add_argument('--snap', action="store", default=os.getenv('FPAKMAN_SNAP', 1), choices=[0, 1], type=int, help='Enables / disables snap usage. Default: %(default)s')
|
||||
parser.add_argument('-co', '--check-packaging-once', action="store", default=os.getenv('FPAKMAN_CHECK_PACKAGING_ONCE', 0), choices=[0, 1], type=int, help='If the available supported packaging types should be checked ONLY once. It improves the application speed if enabled, but can generate errors if you uninstall any packaging technology while using it, and every time a supported packaging type is installed it will only be available after a restart. Default: %(default)s')
|
||||
parser.add_argument('--tray', action="store", default=os.getenv('FPAKMAN_TRAY', 1), choices=[0, 1], type=int, help='If the tray icon and update-check daemon should be created. Default: %(default)s')
|
||||
parser.add_argument('--sugs', action="store", default=os.getenv('FPAKMAN_SUGGESTIONS', 1), choices=[0, 1], type=int, help='If app suggestions should be displayed if no app is installed (runtimes do not count as apps). Default: %(default)s')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.cache_exp < 0:
|
||||
@@ -78,6 +79,9 @@ if args.download_icons == 0:
|
||||
if args.check_packaging_once == 1:
|
||||
log_msg("'check-packaging-once' is enabled", Fore.YELLOW)
|
||||
|
||||
if args.sugs == 0:
|
||||
log_msg("suggestions are disabled", Fore.YELLOW)
|
||||
|
||||
locale_keys = util.get_locale_keys(args.locale)
|
||||
|
||||
http_session = requests.Session()
|
||||
@@ -122,7 +126,8 @@ manage_window = ManageWindow(locale_keys=locale_keys,
|
||||
icon_cache=icon_cache,
|
||||
disk_cache=args.disk_cache,
|
||||
download_icons=bool(args.download_icons),
|
||||
screen_size=screen_size)
|
||||
screen_size=screen_size,
|
||||
suggestions=args.sugs)
|
||||
|
||||
if args.tray:
|
||||
trayIcon = TrayIcon(locale_keys=locale_keys,
|
||||
|
||||
@@ -90,6 +90,10 @@ class ApplicationManager(ABC):
|
||||
def list_warnings(self) -> List[str]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_suggestions(self, limit: int) -> List[Application]:
|
||||
pass
|
||||
|
||||
|
||||
class GenericApplicationManager(ApplicationManager):
|
||||
|
||||
@@ -285,3 +289,11 @@ class GenericApplicationManager(ApplicationManager):
|
||||
warnings.extend(man_warnings)
|
||||
|
||||
return warnings
|
||||
|
||||
def list_suggestions(self, limit: int) -> List[Application]:
|
||||
if self.managers:
|
||||
suggestions = []
|
||||
for man in self.managers:
|
||||
if self._is_enabled(man):
|
||||
suggestions.extend(man.list_suggestions(5))
|
||||
return suggestions
|
||||
|
||||
@@ -46,7 +46,9 @@ class FlatpakManager(ApplicationManager):
|
||||
|
||||
if not api_data or expired_data:
|
||||
if not app_json['runtime']:
|
||||
disk_loader.add(app) # preloading cached disk data
|
||||
if disk_loader:
|
||||
disk_loader.add(app) # preloading cached disk data
|
||||
|
||||
FlatpakAsyncDataLoader(app=app, api_cache=self.api_cache, manager=self, http_session=self.http_session).start()
|
||||
|
||||
else:
|
||||
@@ -181,3 +183,22 @@ class FlatpakManager(ApplicationManager):
|
||||
def list_warnings(self) -> List[str]:
|
||||
if not flatpak.has_remotes_set():
|
||||
return [self.locale_keys['flatpak.notification.no_remotes']]
|
||||
|
||||
def list_suggestions(self, limit: int) -> List[FlatpakApplication]:
|
||||
|
||||
res = []
|
||||
|
||||
if limit != 0:
|
||||
|
||||
for app_id in ('com.spotify.Client', 'com.skype.Client', 'com.dropbox.Client', 'us.zoom.Zoom', 'com.visualstudio.code', 'org.inkscape.Inkscape', 'org.libretro.RetroArch', 'org.kde.kdenlive', 'org.videolan.VLC'):
|
||||
|
||||
app_json = flatpak.search(app_id, app_id=True)
|
||||
|
||||
if app_json:
|
||||
res.append(self._map_to_model(app_json[0], False, None))
|
||||
|
||||
if len(res) == limit:
|
||||
break
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ def get_app_commits_data(app_ref: str, origin: str) -> List[dict]:
|
||||
return commits
|
||||
|
||||
|
||||
def search(word: str) -> List[dict]:
|
||||
def search(word: str, app_id: bool = False) -> List[dict]:
|
||||
cli_version = get_version()
|
||||
|
||||
res = system.run_cmd('{} search {}'.format(BASE_CMD, word))
|
||||
@@ -166,13 +166,17 @@ def search(word: str) -> List[dict]:
|
||||
for info in split_res:
|
||||
if info:
|
||||
info_list = info.split('\t')
|
||||
|
||||
if cli_version >= '1.3.0':
|
||||
id_ = info_list[2].strip()
|
||||
|
||||
if app_id and id_ != word:
|
||||
continue
|
||||
|
||||
version = info_list[3].strip()
|
||||
found.append({
|
||||
app = {
|
||||
'name': info_list[0].strip(),
|
||||
'description': info_list[1].strip(),
|
||||
'id': info_list[2].strip(),
|
||||
'id': id_,
|
||||
'version': version,
|
||||
'latest_version': version,
|
||||
'branch': info_list[4].strip(),
|
||||
@@ -180,14 +184,19 @@ def search(word: str) -> List[dict]:
|
||||
'runtime': False,
|
||||
'arch': None, # unknown at this moment,
|
||||
'ref': None # unknown at this moment
|
||||
})
|
||||
}
|
||||
elif cli_version >= '1.2.0':
|
||||
id_ = info_list[1].strip()
|
||||
|
||||
if app_id and id_ != word:
|
||||
continue
|
||||
|
||||
desc = info_list[0].split('-')
|
||||
version = info_list[2].strip()
|
||||
found.append({
|
||||
app = {
|
||||
'name': desc[0].strip(),
|
||||
'description': desc[1].strip(),
|
||||
'id': info_list[1].strip(),
|
||||
'id': id_,
|
||||
'version': version,
|
||||
'latest_version': version,
|
||||
'branch': info_list[3].strip(),
|
||||
@@ -195,13 +204,18 @@ def search(word: str) -> List[dict]:
|
||||
'runtime': False,
|
||||
'arch': None, # unknown at this moment,
|
||||
'ref': None # unknown at this moment
|
||||
})
|
||||
}
|
||||
else:
|
||||
id_ = info_list[0].strip()
|
||||
|
||||
if app_id and id_ != word:
|
||||
continue
|
||||
|
||||
version = info_list[1].strip()
|
||||
found.append({
|
||||
app = {
|
||||
'name': '',
|
||||
'description': info_list[4].strip(),
|
||||
'id': info_list[0].strip(),
|
||||
'id': id_,
|
||||
'version': version,
|
||||
'latest_version': version,
|
||||
'branch': info_list[2].strip(),
|
||||
@@ -209,7 +223,13 @@ def search(word: str) -> List[dict]:
|
||||
'runtime': False,
|
||||
'arch': None, # unknown at this moment,
|
||||
'ref': None # unknown at this moment
|
||||
})
|
||||
}
|
||||
|
||||
found.append(app)
|
||||
|
||||
if app_id and len(found) > 0:
|
||||
break
|
||||
|
||||
return found
|
||||
|
||||
|
||||
|
||||
@@ -137,3 +137,18 @@ class SnapManager(ApplicationManager):
|
||||
def list_warnings(self) -> List[str]:
|
||||
if snap.get_snapd_version() == 'unavailable':
|
||||
return [self.locale_keys['snap.notification.snapd_unavailable']]
|
||||
|
||||
def list_suggestions(self, limit: int) -> List[SnapApplication]:
|
||||
|
||||
suggestions = []
|
||||
|
||||
if limit != 0:
|
||||
for name in ('whatsdesk', 'slack', 'yakyak', 'instagraph', 'eclipse', 'gimp', 'supertuxkart'):
|
||||
res = snap.search(name, exact_name=True)
|
||||
if res:
|
||||
suggestions.append(self.map_json(res[0], installed=False, disk_loader=None))
|
||||
|
||||
if len(suggestions) == limit:
|
||||
break
|
||||
|
||||
return suggestions
|
||||
|
||||
@@ -86,7 +86,7 @@ def read_installed() -> List[dict]:
|
||||
return apps
|
||||
|
||||
|
||||
def search(word: str) -> List[dict]:
|
||||
def search(word: str, exact_name: bool = False) -> List[dict]:
|
||||
apps = []
|
||||
|
||||
res = system.run_cmd('{} find "{}"'.format(BASE_CMD, word), print_error=False)
|
||||
@@ -98,6 +98,10 @@ def search(word: str) -> List[dict]:
|
||||
for idx, app_str in enumerate(res):
|
||||
if idx > 0 and app_str:
|
||||
app_data = [word for word in app_str.split(' ') if word]
|
||||
|
||||
if exact_name and app_data[0] != word:
|
||||
continue
|
||||
|
||||
apps.append({
|
||||
'name': app_data[0],
|
||||
'version': app_data[1],
|
||||
@@ -109,6 +113,9 @@ def search(word: str) -> List[dict]:
|
||||
'type': None
|
||||
})
|
||||
|
||||
if exact_name and len(apps) > 0:
|
||||
break
|
||||
|
||||
return apps
|
||||
|
||||
|
||||
|
||||
@@ -335,3 +335,16 @@ class RefreshApp(AsyncAction):
|
||||
finally:
|
||||
self.app = None
|
||||
self.signal_finished.emit(success)
|
||||
|
||||
|
||||
class FindSuggestions(AsyncAction):
|
||||
|
||||
signal_finished = pyqtSignal(list)
|
||||
|
||||
def __init__(self, man: ApplicationManager):
|
||||
super(FindSuggestions, self).__init__()
|
||||
self.man = man
|
||||
|
||||
def run(self):
|
||||
self.signal_finished.emit(self.man.list_suggestions(limit=-1))
|
||||
|
||||
@@ -18,7 +18,7 @@ from fpakman.view.qt.history import HistoryDialog
|
||||
from fpakman.view.qt.info import InfoDialog
|
||||
from fpakman.view.qt.root import is_root, ask_root_password
|
||||
from fpakman.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
|
||||
GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels, RefreshApp
|
||||
GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels, RefreshApp, FindSuggestions
|
||||
from fpakman.view.qt.view_model import ApplicationView
|
||||
|
||||
DARK_ORANGE = '#FF4500'
|
||||
@@ -27,7 +27,7 @@ DARK_ORANGE = '#FF4500'
|
||||
class ManageWindow(QWidget):
|
||||
__BASE_HEIGHT__ = 400
|
||||
|
||||
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, disk_cache: bool, download_icons: bool, screen_size, tray_icon=None):
|
||||
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, disk_cache: bool, download_icons: bool, screen_size, suggestions: bool, tray_icon=None):
|
||||
super(ManageWindow, self).__init__()
|
||||
self.locale_keys = locale_keys
|
||||
self.manager = manager
|
||||
@@ -177,6 +177,9 @@ class ManageWindow(QWidget):
|
||||
self.thread_refresh_app.signal_finished.connect(self._finish_refresh)
|
||||
self.thread_refresh_app.signal_output.connect(self._update_action_output)
|
||||
|
||||
self.thread_suggestions = FindSuggestions(man=self.manager)
|
||||
self.thread_suggestions.signal_finished.connect(self._finish_search)
|
||||
|
||||
self.toolbar_bottom = QToolBar()
|
||||
self.toolbar_bottom.setIconSize(QSize(16, 16))
|
||||
|
||||
@@ -208,6 +211,7 @@ class ManageWindow(QWidget):
|
||||
self._maximized = False
|
||||
|
||||
self.dialog_about = None
|
||||
self.first_refresh = suggestions
|
||||
|
||||
def _show_about(self):
|
||||
if self.dialog_about is None:
|
||||
@@ -286,6 +290,7 @@ class ManageWindow(QWidget):
|
||||
self.ref_checkbox_only_apps.setVisible(True)
|
||||
self.ref_bt_upgrade.setVisible(True)
|
||||
self.update_apps(apps)
|
||||
self.first_refresh = False
|
||||
|
||||
def uninstall_app(self, app: ApplicationView):
|
||||
pwd = None
|
||||
@@ -457,8 +462,14 @@ class ManageWindow(QWidget):
|
||||
self.apps.append(app_model)
|
||||
|
||||
if napps == 0:
|
||||
self.checkbox_only_apps.setChecked(False)
|
||||
self.checkbox_only_apps.setCheckable(False)
|
||||
|
||||
if self.first_refresh:
|
||||
self._begin_search('')
|
||||
self.thread_suggestions.start()
|
||||
return
|
||||
else:
|
||||
self.checkbox_only_apps.setChecked(False)
|
||||
self.checkbox_only_apps.setCheckable(False)
|
||||
else:
|
||||
self.checkbox_only_apps.setCheckable(True)
|
||||
self.checkbox_only_apps.setChecked(True)
|
||||
@@ -626,16 +637,19 @@ class ManageWindow(QWidget):
|
||||
dialog_history = HistoryDialog(app, self.icon_cache, self.locale_keys)
|
||||
dialog_history.exec_()
|
||||
|
||||
def _begin_search(self, word):
|
||||
self._handle_console_option(False)
|
||||
self.ref_checkbox_only_apps.setVisible(False)
|
||||
self.ref_checkbox_updates.setVisible(False)
|
||||
self.filter_updates = False
|
||||
self._begin_action('{}{}'.format(self.locale_keys['manage_window.status.searching'], '"{}"'.format(word) if word else ''), clear_filters=True)
|
||||
|
||||
def search(self):
|
||||
|
||||
word = self.input_search.text().strip()
|
||||
|
||||
if word:
|
||||
self._handle_console_option(False)
|
||||
self.ref_checkbox_only_apps.setVisible(False)
|
||||
self.ref_checkbox_updates.setVisible(False)
|
||||
self.filter_updates = False
|
||||
self._begin_action('{} "{}"'.format(self.locale_keys['manage_window.status.searching'], word), clear_filters=True)
|
||||
self._begin_search(word)
|
||||
self.thread_search.word = word
|
||||
self.thread_search.start()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user