feat: route library browsing through QML shell

This commit is contained in:
dela
2026-07-15 14:03:31 +08:00
parent a411fd396a
commit e2357aaa32
13 changed files with 439 additions and 117 deletions
+4 -2
View File
@@ -44,7 +44,7 @@
| 2 | 用户首页 | 收藏 `views/user/FavoritesView` | favorites-agent | done | MediaCard/List、收藏模型 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;分类、详情、文件夹、人物跳转及返回 | | 2 | 用户首页 | 收藏 `views/user/FavoritesView` | favorites-agent | done | MediaCard/List、收藏模型 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;分类、详情、文件夹、人物跳转及返回 |
| 2 | 用户首页 | 分类 `views/user/CategoryView` | category-agent | done | MediaCard/List、筛选/分页模型 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;分类切换、分页、空态、返回状态保持 | | 2 | 用户首页 | 分类 `views/user/CategoryView` | category-agent | done | MediaCard/List、筛选/分页模型 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;分类切换、分页、空态、返回状态保持 |
| 2 | 媒体浏览 | 媒体库/人物/过滤结果(共用 `views/media/LibraryView`) | library-agent | done | MediaCard/Grid、排序筛选菜单、分页模型 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;海报/平铺视图、排序筛选、滚动续载、人物页 | | 2 | 媒体浏览 | 媒体库/人物/过滤结果(共用 `views/media/LibraryView`) | library-agent | done | MediaCard/Grid、排序筛选菜单、分页模型 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;海报/平铺视图、排序筛选、滚动续载、人物页 |
| 2 | 应用壳 | `MainWindow` 标题栏、全局搜索、HomeView 侧栏与路由栈 | core | in-progress | `NavigationController`、单引擎 `Router.qml`、Home/Favorites/Search 已接入;待 Category/Library 等浏览 route、侧栏与标题导航 | `cmake --build cmake-build-debug --target qEmbyNavigationTest qEmbyApp_qmllint qEmbyApp -j2 && ./cmake-build-debug/bin/qEmbyNavigationTest`;登录→首页→搜索→返回→收藏→详情→返回→设置→退出全链路,检查窗口按钮与托盘 | | 2 | 应用壳 | `MainWindow` 标题栏、全局搜索、HomeView 侧栏与路由栈 | core | in-progress | `NavigationController`、单引擎 `Router.qml`、Home/Favorites/Search/Category/Library/Person/Filtered 已接入;待 Detail/Season、侧栏与标题导航 | `cmake --build cmake-build-debug --target qEmbyNavigationTest qEmbyApp_qmllint qEmbyApp -j2 && ./cmake-build-debug/bin/qEmbyNavigationTest`;登录→首页→分类→媒体库→人物/过滤→搜索→返回→详情→返回→退出全链路,检查窗口按钮与托盘 |
| 3 | 媒体详情 | 详情 `views/media/DetailView` | detail-agent | done | MediaCard、Flow/Tag、动作菜单、编辑/识别弹窗桥接 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;电影/剧集/人物各一项,播放、收藏、版本选择、编辑入口 | | 3 | 媒体详情 | 详情 `views/media/DetailView` | detail-agent | done | MediaCard、Flow/Tag、动作菜单、编辑/识别弹窗桥接 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;电影/剧集/人物各一项,播放、收藏、版本选择、编辑入口 |
| 3 | 媒体详情 | 季 `views/media/SeasonView` | season-agent | done | Episode 列表模型、MediaCard、播放动作 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;切季、剧集续播/播放、已播状态、返回 | | 3 | 媒体详情 | 季 `views/media/SeasonView` | season-agent | done | Episode 列表模型、MediaCard、播放动作 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;切季、剧集续播/播放、已播状态、返回 |
| 3 | 管理 | 媒体库 `PageLibraries` | admin-libraries-agent | done | 管理卡片/Grid、拖拽排序、Library CRUD 对话框 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;新增/编辑/删除/扫描/取消扫描/排序 | | 3 | 管理 | 媒体库 `PageLibraries` | admin-libraries-agent | done | 管理卡片/Grid、拖拽排序、Library CRUD 对话框 | `cmake --build cmake-build-debug --target qEmbyApp_qmllint qEmbyApp -j2`;新增/编辑/删除/扫描/取消扫描/排序 |
@@ -67,7 +67,9 @@
当前开发批次(2026-07-15):导航 Phase 0/1 已落地,Phase 2 首批完成。新增强类型 `NavigationRoute`、`NavigationController` 和轻量契约测试;Widgets 页面统一先转换为规范 route,再进入旧页面栈。`QuickRouterHost` 以单个 QML engine 承载 `Router.qml`,Home/Favorites 共享该 host 与图片 provider,并由 `HomeQuickRouterView` 协调 ViewModel 生命周期和 BaseView 媒体动作。其他 route 暂时保持旧 Widgets host,切入时 Router 保留底层页面且暂停生命周期,避免过渡闪屏。可用 `QEMBY_USE_LEGACY_HOME_PAGES=1` 临时回退旧 Home/Favorites 双 host。 当前开发批次(2026-07-15):导航 Phase 0/1 已落地,Phase 2 首批完成。新增强类型 `NavigationRoute`、`NavigationController` 和轻量契约测试;Widgets 页面统一先转换为规范 route,再进入旧页面栈。`QuickRouterHost` 以单个 QML engine 承载 `Router.qml`,Home/Favorites 共享该 host 与图片 provider,并由 `HomeQuickRouterView` 协调 ViewModel 生命周期和 BaseView 媒体动作。其他 route 暂时保持旧 Widgets host,切入时 Router 保留底层页面且暂停生命周期,避免过渡闪屏。可用 `QEMBY_USE_LEGACY_HOME_PAGES=1` 临时回退旧 Home/Favorites 双 host。
Phase 2 第二批(2026-07-15):Search 已加入根 Router。`HomeView::triggerSearch()` 现在生成规范 `search` route;同一 Quick host 内的 Home/Favorites/Search 切换只更新 Controller/Loader,进入 legacy 详情页时仍保存兼容历史。`SearchViewModel` 新增 active/generation 生命周期,隐藏或离开搜索时淘汰在途结果,返回时仅按需恢复;旧 `SearchView` 默认 active 行为保持兼容。下一批进入 Category/Library/Person/Filtered,重点解决带参数 route 的 ViewModel 实例所有权与滚动状态。 Phase 2 第二批(2026-07-15):Search 已加入根 Router。`HomeView::triggerSearch()` 现在生成规范 `search` route;同一 Quick host 内的 Home/Favorites/Search 切换只更新 Controller/Loader,进入 legacy 详情页时仍保存兼容历史。`SearchViewModel` 新增 active/generation 生命周期,隐藏或离开搜索时淘汰在途结果,返回时仅按需恢复;旧 `SearchView` 默认 active 行为保持兼容。
Phase 2 第三批(2026-07-15):Category、Library、Person、Filtered 已加入根 Router。`HomeView` 的首页媒体库、侧栏媒体库、文件夹、人物和过滤入口统一生成规范 route;Favorite Category 在 key 中具有独立身份。Category/Library ViewModel 在 inactive 时只保存最新参数,并用 generation 淘汰详情探测、首屏与 loadMore 的过期回写;旧包装器继续保持默认 active 兼容。当前 Category 和 Library 系列各使用一个会话级 ViewModel,返回已完成 route 时可复用数据,切换参数时重建当前状态;尚未实现每个历史 route 独立的 LRU ViewModel 与滚动位置。下一批优先接 Detail/Season,再统一 `uiState.scrollY`。
阶段门禁:每阶段至少通过 QML 静态检查、Debug 构建、明暗主题、窗口缩放、键盘焦点、页面反复进入退出、网络失败/空数据检查;依赖真实服务器的行为必须用 Emby/Jellyfin 各冒烟一次。 阶段门禁:每阶段至少通过 QML 静态检查、Debug 构建、明暗主题、窗口缩放、键盘焦点、页面反复进入退出、网络失败/空数据检查;依赖真实服务器的行为必须用 Emby/Jellyfin 各冒烟一次。
+9
View File
@@ -409,3 +409,12 @@ qemby://search?q=<percent-encoded-query>
- `HomeView::triggerSearch()` 改为 push 规范 search route;同一 Router widget 内切换也会记录历史,因此 Home → Search → Back 可以恢复 Home,Search → legacy Detail → Back 可以恢复原搜索。 - `HomeView::triggerSearch()` 改为 push 规范 search route;同一 Router widget 内切换也会记录历史,因此 Home → Search → Back 可以恢复 Home,Search → legacy Detail → Back 可以恢复原搜索。
- `SearchViewModel` 增加 active/generation 取消语义;inactive 时只记录待搜索 query,重新激活后按需恢复,避免隐藏页面回写陈旧结果。 - `SearchViewModel` 增加 active/generation 取消语义;inactive 时只记录待搜索 query,重新激活后按需恢复,避免隐藏页面回写陈旧结果。
- 导航契约测试新增 search key 和返回用例;默认 Router 与 legacy fallback 的无服务器 offscreen 冒烟均无 runtime 输出。 - 导航契约测试新增 search key 和返回用例;默认 Router 与 legacy fallback 的无服务器 offscreen 冒烟均无 runtime 输出。
2026-07-15 完成 Phase 2 第三批 Category/Library/Person/Filtered:
- 根 Router 静态注册 CategoryPage 与 LibraryPage;Library、Person、Filtered 复用同一页面类型,但 route key 变化时重新创建 QML 页面。
- HomeView 的分类、媒体库、文件夹、人物和过滤入口统一 push 规范 route;legacy fallback 仍调用原 `create*View()`。
- Favorite Category 将 `favorite` 纳入规范 key,避免与普通 Category 使用相同身份。
- Category/Library ViewModel 的 inactive 路由更新只保存最新参数;generation 同时覆盖详情探测、首屏、渐进加载和 loadMore,防止离开后陈旧回写。
- 当前每类参数化页面仍共享一个会话 ViewModel;已加载 route 返回时复用数据,切换 route 时重建状态。每个历史 route 独立的 LRU 实例和滚动 uiState 尚未实现。
- 导航测试增加 Favorite Category 身份用例;Debug 构建、qmllint、默认 Router 与 legacy fallback 无服务器启动冒烟通过。
+19 -2
View File
@@ -9,7 +9,8 @@ Item {
property var navigationController: navigation property var navigationController: navigation
// Keep this allow-list deliberately small until each page's context // Keep this allow-list deliberately small until each page's context
// ViewModel has been moved to the root Quick host. // ViewModel has been moved to the root Quick host.
property var enabledRoutes: ["home", "favorites", "search"] property var enabledRoutes: ["home", "favorites", "search", "category",
"library", "person", "filtered"]
property var currentRoute: navigationController property var currentRoute: navigationController
? navigationController.currentRoute ? navigationController.currentRoute
: ({ "name": "navigationError", : ({ "name": "navigationError",
@@ -69,7 +70,9 @@ Item {
} }
function routeImplemented(name) { function routeImplemented(name) {
return name === "home" || name === "favorites" || name === "search"; return name === "home" || name === "favorites" || name === "search"
|| name === "category" || name === "library" || name === "person"
|| name === "filtered";
} }
function routeKnown(name) { function routeKnown(name) {
@@ -103,6 +106,11 @@ Item {
case "home": return homeComponent; case "home": return homeComponent;
case "favorites": return favoritesComponent; case "favorites": return favoritesComponent;
case "search": return searchComponent; case "search": return searchComponent;
case "category": return categoryComponent;
case "library":
case "person":
case "filtered":
return libraryComponent;
default: default:
return null; return null;
} }
@@ -185,6 +193,13 @@ Item {
return true; return true;
} }
function scrollToTop() {
if (!pageLoader.item)
return false;
callHook(pageLoader.item, "scrollToTop", undefined);
return true;
}
onCurrentRouteChanged: applyRoute(currentRoute) onCurrentRouteChanged: applyRoute(currentRoute)
onActiveChanged: { onActiveChanged: {
if (!pageLoader.item) if (!pageLoader.item)
@@ -221,6 +236,8 @@ Item {
Component { id: homeComponent; DashboardPage { anchors.fill: parent } } Component { id: homeComponent; DashboardPage { anchors.fill: parent } }
Component { id: favoritesComponent; FavoritesPage { anchors.fill: parent } } Component { id: favoritesComponent; FavoritesPage { anchors.fill: parent } }
Component { id: searchComponent; SearchPage { anchors.fill: parent } } Component { id: searchComponent; SearchPage { anchors.fill: parent } }
Component { id: categoryComponent; CategoryPage { anchors.fill: parent } }
Component { id: libraryComponent; LibraryPage { anchors.fill: parent } }
Component { Component {
id: recoveryComponent id: recoveryComponent
@@ -191,6 +191,9 @@ bool NavigationController::normalize(const QVariantMap &request,
QStringList identity; QStringList identity;
for (const QString &field : required) for (const QString &field : required)
identity << encoded(params.value(field).toString()); identity << encoded(params.value(field).toString());
if (name == QLatin1String("category") &&
params.value(QStringLiteral("favorite")).toBool())
identity << QStringLiteral("favorite");
route->key = identity.isEmpty() ? name : name + QLatin1Char(':') + identity.join(QLatin1Char(':')); route->key = identity.isEmpty() ? name : name + QLatin1Char(':') + identity.join(QLatin1Char(':'));
return true; return true;
} }
@@ -34,6 +34,14 @@ int main(int argc, char **argv)
"search query participates in the route key"); "search query participates in the route key");
ok &= check(navigation.back() && navigation.currentRoute().value("name") == "home", ok &= check(navigation.back() && navigation.currentRoute().value("name") == "home",
"back returns from search to its session root"); "back returns from search to its session root");
ok &= check(navigation.push({{"name", "category"},
{"params", QVariantMap{{"categoryId", "Movie"},
{"favorite", true}}}}),
"accepts a favorite category route");
ok &= check(navigation.currentRoute().value("key") ==
"category:Movie:favorite",
"favorite category has a distinct route identity");
ok &= check(navigation.back(), "returns from favorite category");
ok &= check(navigation.push({{"name", "library"}, ok &= check(navigation.push({{"name", "library"},
{"params", QVariantMap{{"libraryId", "lib/1"}, {"params", QVariantMap{{"libraryId", "lib/1"},
{"title", "Movies"}}}}), {"title", "Movies"}}}}),
+147 -93
View File
@@ -214,119 +214,111 @@ void LibraryViewModel::setActive(bool active) {
m_active = active; m_active = active;
if (!active) { if (!active) {
++m_generation; ++m_generation;
if (m_loading || m_loadingMore) if (m_routeRequestInFlight || m_loading || m_loadingMore)
m_reloadOnActivate = true; m_reloadOnActivate = true;
m_routeRequestInFlight = false;
m_loading = false; m_loading = false;
m_loadingMore = false; m_loadingMore = false;
emit stateChanged(); emit stateChanged();
} else if (m_reloadOnActivate) { } else if (m_reloadOnActivate) {
m_reloadOnActivate = false; m_reloadOnActivate = false;
launchReload(); launchCurrentRouteLoad();
} }
} }
QCoro::Task<void> LibraryViewModel::loadLibrary(QString libraryId, QCoro::Task<void> LibraryViewModel::loadLibrary(QString libraryId,
QString libraryName) { QString libraryName) {
libraryId = libraryId.trimmed();
if (isCurrentRoute(LibraryMode, libraryId, {})) {
if (m_title != libraryName) {
m_title = std::move(libraryName);
emit stateChanged();
}
co_return;
}
const quint64 generation = ++m_generation; const quint64 generation = ++m_generation;
m_mode = LibraryMode; configureRoute(LibraryMode, std::move(libraryId), std::move(libraryName));
m_targetId = std::move(libraryId);
m_title = std::move(libraryName); if (!m_active) {
m_filterType.clear(); m_reloadOnActivate = true;
m_filterValue.clear(); co_return;
}
co_await loadCurrentRoute(generation);
}
QCoro::Task<void> LibraryViewModel::loadPerson(QString personId,
QString personName) {
personId = personId.trimmed();
if (isCurrentRoute(PersonMode, personId, {})) {
if (m_title != personName) {
m_title = std::move(personName);
emit stateChanged();
}
co_return;
}
const quint64 generation = ++m_generation;
configureRoute(PersonMode, std::move(personId), std::move(personName));
if (!m_active) {
m_reloadOnActivate = true;
co_return;
}
co_await loadCurrentRoute(generation);
}
QCoro::Task<void> LibraryViewModel::loadFiltered(QString filterType,
QString filterValue) {
filterType = filterType.trimmed();
filterValue = filterValue.trimmed();
if (isCurrentRoute(FilteredMode, filterType, filterValue))
co_return;
const quint64 generation = ++m_generation;
configureRoute(FilteredMode, std::move(filterType), std::move(filterValue));
if (!m_active) {
m_reloadOnActivate = true;
co_return;
}
co_await loadCurrentRoute(generation);
}
bool LibraryViewModel::isCurrentRoute(Mode mode, const QString &primary,
const QString &secondary) const {
if (!m_routeInitialized || m_mode != mode)
return false;
if (mode == FilteredMode)
return m_filterType == primary && m_filterValue == secondary;
return m_targetId == primary;
}
void LibraryViewModel::configureRoute(Mode mode, QString primary,
QString secondary) {
m_routeInitialized = true;
m_mode = mode;
m_currentItem = {}; m_currentItem = {};
m_tabIndex = 0; m_tabIndex = 0;
m_firstTabText = tr("All"); m_firstTabText = tr("All");
m_tabsVisible = false; m_tabsVisible = false;
m_favoriteVisible = false; m_favoriteVisible = false;
m_reloadOnActivate = false;
if (mode == FilteredMode) {
m_targetId.clear();
m_filterType = std::move(primary);
m_filterValue = std::move(secondary);
m_title = m_filterValue;
} else {
m_targetId = std::move(primary);
m_title = std::move(secondary);
m_filterType.clear();
m_filterValue.clear();
}
restorePreferences(); restorePreferences();
emit stateChanged(); emit stateChanged();
emit controlsChanged(); emit controlsChanged();
if (!m_active) {
m_reloadOnActivate = true;
co_return;
}
QPointer<LibraryViewModel> guard(this);
if (m_active && m_core && m_core->mediaService() && !m_targetId.isEmpty()) {
try {
const MediaItem item =
co_await m_core->mediaService()->getItemDetail(m_targetId);
if (!guard || !m_active || generation != m_generation)
co_return;
if (item.id == m_targetId)
configureCurrentItem(item);
} catch (const std::exception &error) {
if (!guard || generation != m_generation)
co_return;
qWarning() << "[LibraryViewModel] detail probe failed:" << error.what();
}
}
if (!guard || !m_active || generation != m_generation)
co_return;
co_await reload();
}
QCoro::Task<void> LibraryViewModel::loadPerson(QString personId,
QString personName) {
const quint64 generation = ++m_generation;
m_mode = PersonMode;
m_targetId = std::move(personId);
m_title = std::move(personName);
m_filterType.clear();
m_filterValue.clear();
m_currentItem = {};
m_tabIndex = 0;
m_tabsVisible = false;
m_favoriteVisible = false;
restorePreferences();
emit stateChanged();
emit controlsChanged();
if (!m_active) {
m_reloadOnActivate = true;
co_return;
}
QPointer<LibraryViewModel> guard(this);
if (m_active && m_core && m_core->mediaService() && !m_targetId.isEmpty()) {
try {
const MediaItem item =
co_await m_core->mediaService()->getItemDetail(m_targetId);
if (!guard || !m_active || generation != m_generation)
co_return;
if (item.id == m_targetId) {
m_currentItem = item;
m_favoriteVisible = true;
emit controlsChanged();
}
} catch (const std::exception &error) {
if (!guard || generation != m_generation)
co_return;
qWarning() << "[LibraryViewModel] person detail failed:" << error.what();
}
}
if (!guard || !m_active || generation != m_generation)
co_return;
co_await reload();
}
QCoro::Task<void> LibraryViewModel::loadFiltered(QString filterType,
QString filterValue) {
++m_generation;
m_mode = FilteredMode;
m_filterType = std::move(filterType);
m_filterValue = std::move(filterValue);
m_targetId.clear();
m_title = m_filterValue;
m_currentItem = {};
m_tabIndex = 0;
m_tabsVisible = false;
m_favoriteVisible = false;
restorePreferences();
emit stateChanged();
emit controlsChanged();
co_await reload();
} }
void LibraryViewModel::configureCurrentItem(const MediaItem &item) { void LibraryViewModel::configureCurrentItem(const MediaItem &item) {
@@ -499,11 +491,72 @@ void LibraryViewModel::updateStats() {
emit stateChanged(); emit stateChanged();
} }
void LibraryViewModel::launchCurrentRouteLoad() {
if (!m_active || !m_routeInitialized) {
m_reloadOnActivate = m_routeInitialized;
return;
}
const quint64 generation = ++m_generation;
QCoro::connect(loadCurrentRoute(generation), this, []() {});
}
QCoro::Task<void> LibraryViewModel::loadCurrentRoute(quint64 generation) {
if (!m_active || !m_routeInitialized || generation != m_generation) {
if (!m_active)
m_reloadOnActivate = true;
co_return;
}
m_routeRequestInFlight = true;
m_loading = true;
m_loadingMore = false;
m_hasMore = false;
m_totalCount = 0;
m_errorText.clear();
m_statsText = tr("Loading...");
m_items.clear();
emit stateChanged();
QPointer<LibraryViewModel> guard(this);
if (m_mode != FilteredMode && m_core && m_core->mediaService() &&
!m_targetId.isEmpty()) {
try {
const MediaItem item =
co_await m_core->mediaService()->getItemDetail(m_targetId);
if (!guard || !m_active || generation != m_generation)
co_return;
if (item.id == m_targetId) {
if (m_mode == PersonMode) {
m_currentItem = item;
m_favoriteVisible = true;
emit controlsChanged();
} else {
configureCurrentItem(item);
}
}
} catch (const std::exception &error) {
if (!guard || !m_active || generation != m_generation)
co_return;
qWarning() << (m_mode == PersonMode
? "[LibraryViewModel] person detail failed:"
: "[LibraryViewModel] detail probe failed:")
<< error.what();
}
}
if (!guard || !m_active || generation != m_generation)
co_return;
m_routeRequestInFlight = false;
co_await reload();
}
void LibraryViewModel::launchReload() { void LibraryViewModel::launchReload() {
if (!m_active) { if (!m_active) {
m_reloadOnActivate = true; m_reloadOnActivate = true;
return; return;
} }
m_routeRequestInFlight = false;
QCoro::connect(reload(), this, []() {}); QCoro::connect(reload(), this, []() {});
} }
@@ -512,6 +565,7 @@ QCoro::Task<void> LibraryViewModel::reload() {
m_reloadOnActivate = true; m_reloadOnActivate = true;
co_return; co_return;
} }
m_routeRequestInFlight = false;
const QueryState query = buildQuery(); const QueryState query = buildQuery();
const quint64 generation = ++m_generation; const quint64 generation = ++m_generation;
m_query = query; m_query = query;
@@ -138,6 +138,9 @@ private:
}; };
QString preferenceTargetId() const; QString preferenceTargetId() const;
bool isCurrentRoute(Mode mode, const QString &primary,
const QString &secondary) const;
void configureRoute(Mode mode, QString primary, QString secondary);
QString sortBy() const; QString sortBy() const;
QueryState buildQuery() const; QueryState buildQuery() const;
bool queryValid(const QueryState &query) const; bool queryValid(const QueryState &query) const;
@@ -146,6 +149,8 @@ private:
void saveSortPreference(); void saveSortPreference();
void saveViewPreference(); void saveViewPreference();
void updateStats(); void updateStats();
void launchCurrentRouteLoad();
QCoro::Task<void> loadCurrentRoute(quint64 generation);
void launchReload(); void launchReload();
QCoro::Task<void> reload(); QCoro::Task<void> reload();
QCoro::Task<void> fetchNextPage(quint64 generation, QueryState query, QCoro::Task<void> fetchNextPage(quint64 generation, QueryState query,
@@ -172,6 +177,8 @@ private:
bool m_tabsVisible = false; bool m_tabsVisible = false;
bool m_favoriteVisible = false; bool m_favoriteVisible = false;
bool m_active = true; bool m_active = true;
bool m_routeInitialized = false;
bool m_routeRequestInFlight = false;
bool m_reloadOnActivate = false; bool m_reloadOnActivate = false;
int m_tabIndex = 0; int m_tabIndex = 0;
int m_sortIndex = 0; int m_sortIndex = 0;
+26 -1
View File
@@ -237,13 +237,24 @@ void CategoryViewModel::setActive(bool active) {
m_loadingMore = false; m_loadingMore = false;
emit stateChanged(); emit stateChanged();
} else if (m_reloadOnActivate) { } else if (m_reloadOnActivate) {
const bool clearRecommendCache = m_clearRecommendCacheOnActivate;
m_reloadOnActivate = false; m_reloadOnActivate = false;
launchReload(); m_clearRecommendCacheOnActivate = false;
launchReload(clearRecommendCache);
} }
} }
QCoro::Task<void> CategoryViewModel::loadCategory(QString categoryType, QCoro::Task<void> CategoryViewModel::loadCategory(QString categoryType,
QString title) { QString title) {
const bool routeChanged =
m_category != categoryType || m_title != title;
if (!routeChanged) {
if (!m_active)
m_reloadOnActivate = true;
co_return;
}
++m_generation;
m_category = std::move(categoryType); m_category = std::move(categoryType);
m_title = std::move(title); m_title = std::move(title);
configureCategory(); configureCategory();
@@ -251,6 +262,15 @@ QCoro::Task<void> CategoryViewModel::loadCategory(QString categoryType,
emit stateChanged(); emit stateChanged();
emit sortChanged(); emit sortChanged();
emit viewChanged(); emit viewChanged();
if (!m_active) {
m_loading = false;
m_loadingMore = false;
m_reloadOnActivate = true;
m_clearRecommendCacheOnActivate = false;
co_return;
}
co_await reload(); co_await reload();
} }
@@ -346,6 +366,8 @@ QString CategoryViewModel::sortBy() const {
void CategoryViewModel::launchReload(bool clearRecommendCache) { void CategoryViewModel::launchReload(bool clearRecommendCache) {
if (!m_active) { if (!m_active) {
m_reloadOnActivate = true; m_reloadOnActivate = true;
m_clearRecommendCacheOnActivate =
m_clearRecommendCacheOnActivate || clearRecommendCache;
return; return;
} }
QCoro::connect(reload(clearRecommendCache), this, []() {}); QCoro::connect(reload(clearRecommendCache), this, []() {});
@@ -354,6 +376,8 @@ void CategoryViewModel::launchReload(bool clearRecommendCache) {
QCoro::Task<void> CategoryViewModel::reload(bool clearRecommendCache) { QCoro::Task<void> CategoryViewModel::reload(bool clearRecommendCache) {
if (!m_active) { if (!m_active) {
m_reloadOnActivate = true; m_reloadOnActivate = true;
m_clearRecommendCacheOnActivate =
m_clearRecommendCacheOnActivate || clearRecommendCache;
co_return; co_return;
} }
if (!m_core || !m_core->mediaService() || m_category.isEmpty()) if (!m_core || !m_core->mediaService() || m_category.isEmpty())
@@ -361,6 +385,7 @@ QCoro::Task<void> CategoryViewModel::reload(bool clearRecommendCache) {
const quint64 generation = ++m_generation; const quint64 generation = ++m_generation;
m_reloadOnActivate = false; m_reloadOnActivate = false;
m_clearRecommendCacheOnActivate = false;
m_loading = true; m_loading = true;
m_loadingMore = false; m_loadingMore = false;
m_errorText.clear(); m_errorText.clear();
@@ -138,6 +138,7 @@ private:
bool m_descending = true; bool m_descending = true;
bool m_active = true; bool m_active = true;
bool m_reloadOnActivate = false; bool m_reloadOnActivate = false;
bool m_clearRecommendCacheOnActivate = false;
int m_sortIndex = 0; int m_sortIndex = 0;
quint64 m_generation = 0; quint64 m_generation = 0;
}; };
+132 -2
View File
@@ -2,6 +2,7 @@
#include "dashboardviewmodel.h" #include "dashboardviewmodel.h"
#include "favoritesviewmodel.h" #include "favoritesviewmodel.h"
#include "categoryviewmodel.h"
#include "../../components/libraryactionmenu.h" #include "../../components/libraryactionmenu.h"
#include "../../components/mediaimageeditdialog.h" #include "../../components/mediaimageeditdialog.h"
@@ -9,6 +10,7 @@
#include "../../quick/navigationcontroller.h" #include "../../quick/navigationcontroller.h"
#include "../../quick/quickrouterhost.h" #include "../../quick/quickrouterhost.h"
#include "../search/searchviewmodel.h" #include "../search/searchviewmodel.h"
#include "../media/libraryviewmodel.h"
#include <qembycore.h> #include <qembycore.h>
#include <services/admin/adminservice.h> #include <services/admin/adminservice.h>
@@ -18,6 +20,8 @@
#include <QCursor> #include <QCursor>
#include <QDebug> #include <QDebug>
#include <QPointer> #include <QPointer>
#include <QQuickItem>
#include <QQuickWidget>
#include <QTimer> #include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
@@ -39,6 +43,8 @@ HomeQuickRouterView::HomeQuickRouterView(QEmbyCore *core,
m_dashboardViewModel = new UserDashboardViewModel(core, this); m_dashboardViewModel = new UserDashboardViewModel(core, this);
m_favoritesViewModel = new FavoritesViewModel(core, this); m_favoritesViewModel = new FavoritesViewModel(core, this);
m_searchViewModel = new SearchViewModel(core, this); m_searchViewModel = new SearchViewModel(core, this);
m_categoryViewModel = new CategoryViewModel(core, this);
m_libraryViewModel = new LibraryViewModel(core, this);
m_host->setContextProperty(QStringLiteral("dashboardPage"), m_host->setContextProperty(QStringLiteral("dashboardPage"),
m_dashboardViewModel); m_dashboardViewModel);
@@ -46,6 +52,10 @@ HomeQuickRouterView::HomeQuickRouterView(QEmbyCore *core,
m_favoritesViewModel); m_favoritesViewModel);
m_host->setContextProperty(QStringLiteral("searchPage"), m_host->setContextProperty(QStringLiteral("searchPage"),
m_searchViewModel); m_searchViewModel);
m_host->setContextProperty(QStringLiteral("categoryPage"),
m_categoryViewModel);
m_host->setContextProperty(QStringLiteral("libraryPage"),
m_libraryViewModel);
m_host->loadRouter(); m_host->loadRouter();
layout->addWidget(m_host); layout->addWidget(m_host);
@@ -112,6 +122,32 @@ HomeQuickRouterView::HomeQuickRouterView(QEmbyCore *core,
{ {
handleMoreMenuRequested(item, m_host->mapToGlobal(localPos)); handleMoreMenuRequested(item, m_host->mapToGlobal(localPos));
}); });
connect(m_categoryViewModel, &CategoryViewModel::itemActivated,
this, &HomeQuickRouterView::forwardItemActivation);
connect(m_categoryViewModel, &CategoryViewModel::playRequested,
this, &BaseView::handlePlayRequested);
connect(m_categoryViewModel, &CategoryViewModel::favoriteRequested,
this, &BaseView::handleFavoriteRequested);
connect(m_categoryViewModel, &CategoryViewModel::moreRequested,
this, [this](const MediaItem &item)
{
handleMoreMenuRequested(item, QCursor::pos());
});
connect(m_libraryViewModel, &LibraryViewModel::itemActivated,
this, &HomeQuickRouterView::forwardItemActivation);
connect(m_libraryViewModel, &LibraryViewModel::playRequested,
this, &BaseView::handlePlayRequested);
connect(m_libraryViewModel, &LibraryViewModel::favoriteRequested,
this, &BaseView::handleFavoriteRequested);
connect(m_libraryViewModel, &LibraryViewModel::currentFavoriteRequested,
this, &BaseView::handleFavoriteRequested);
connect(m_libraryViewModel, &LibraryViewModel::moreRequested,
this, [this](const MediaItem &item)
{
handleMoreMenuRequested(item, QCursor::pos());
});
} }
void HomeQuickRouterView::refreshHome() void HomeQuickRouterView::refreshHome()
@@ -135,6 +171,9 @@ void HomeQuickRouterView::scrollToTop()
m_dashboardViewModel->scrollToTop(); m_dashboardViewModel->scrollToTop();
else if (routeName == QLatin1String("favorites")) else if (routeName == QLatin1String("favorites"))
m_favoritesViewModel->scrollToTop(); m_favoritesViewModel->scrollToTop();
else if (m_host && m_host->quickWidget()->rootObject())
QMetaObject::invokeMethod(m_host->quickWidget()->rootObject(),
"scrollToTop");
} }
void HomeQuickRouterView::onMediaItemUpdated(const MediaItem &item) void HomeQuickRouterView::onMediaItemUpdated(const MediaItem &item)
@@ -142,12 +181,16 @@ void HomeQuickRouterView::onMediaItemUpdated(const MediaItem &item)
m_dashboardViewModel->updateItem(item); m_dashboardViewModel->updateItem(item);
m_favoritesViewModel->updateItem(item); m_favoritesViewModel->updateItem(item);
m_searchViewModel->updateItem(item); m_searchViewModel->updateItem(item);
m_categoryViewModel->updateItem(item);
m_libraryViewModel->updateItem(item);
} }
void HomeQuickRouterView::onMediaItemRemoved(const QString &itemId) void HomeQuickRouterView::onMediaItemRemoved(const QString &itemId)
{ {
m_dashboardViewModel->removeItem(itemId); m_dashboardViewModel->removeItem(itemId);
m_favoritesViewModel->removeItem(itemId); m_favoritesViewModel->removeItem(itemId);
m_categoryViewModel->removeItem(itemId);
m_libraryViewModel->removeItem(itemId);
} }
CardContextMenuRequest HomeQuickRouterView::showCardContextMenu( CardContextMenuRequest HomeQuickRouterView::showCardContextMenu(
@@ -227,8 +270,16 @@ void HomeQuickRouterView::updateRouteActivity()
m_favoritesViewModel->setActive(hostActive && m_favoritesViewModel->setActive(hostActive &&
routeName == QLatin1String("favorites")); routeName == QLatin1String("favorites"));
const bool searchActive = hostActive && updateSearchRoute(route, hostActive);
routeName == QLatin1String("search"); updateCategoryRoute(route, hostActive);
updateLibraryRoute(route, hostActive);
}
void HomeQuickRouterView::updateSearchRoute(const QVariantMap &route,
bool hostActive)
{
const QString routeName = route.value(QStringLiteral("name")).toString();
const bool searchActive = hostActive && routeName == QLatin1String("search");
if (!searchActive) if (!searchActive)
{ {
m_searchViewModel->setActive(false); m_searchViewModel->setActive(false);
@@ -255,11 +306,90 @@ void HomeQuickRouterView::updateRouteActivity()
m_searchViewModel->setActive(true); m_searchViewModel->setActive(true);
} }
void HomeQuickRouterView::updateCategoryRoute(const QVariantMap &route,
bool hostActive)
{
const QString routeName = route.value(QStringLiteral("name")).toString();
const bool categoryActive = hostActive &&
routeName == QLatin1String("category");
if (!categoryActive)
{
m_categoryViewModel->setActive(false);
return;
}
const QVariantMap params = route.value(QStringLiteral("params")).toMap();
QString categoryId = params.value(QStringLiteral("categoryId"))
.toString().trimmed();
if (params.value(QStringLiteral("favorite")).toBool())
categoryId.prepend(QStringLiteral("Favorite_"));
const QString title = params.value(QStringLiteral("title")).toString();
const QString routeKey = route.value(QStringLiteral("key")).toString();
const QString routeIdentity = routeKey + QLatin1Char('\n') + title;
if (!categoryId.isEmpty() && routeIdentity != m_categoryRouteKey)
{
m_categoryViewModel->setActive(false);
m_categoryRouteKey = routeIdentity;
QCoro::connect(m_categoryViewModel->loadCategory(categoryId, title),
this, []() {});
}
m_categoryViewModel->setActive(true);
}
void HomeQuickRouterView::updateLibraryRoute(const QVariantMap &route,
bool hostActive)
{
const QString routeName = route.value(QStringLiteral("name")).toString();
const bool libraryActive = hostActive &&
(routeName == QLatin1String("library") ||
routeName == QLatin1String("person") ||
routeName == QLatin1String("filtered"));
if (!libraryActive)
{
m_libraryViewModel->setActive(false);
return;
}
const QVariantMap params = route.value(QStringLiteral("params")).toMap();
const QString routeKey = route.value(QStringLiteral("key")).toString();
const QString title = params.value(QStringLiteral("title")).toString();
const QString routeIdentity = routeKey + QLatin1Char('\n') + title;
if (routeIdentity != m_libraryRouteKey)
{
m_libraryViewModel->setActive(false);
m_libraryRouteKey = routeIdentity;
if (routeName == QLatin1String("library"))
{
QCoro::connect(m_libraryViewModel->loadLibrary(
params.value(QStringLiteral("libraryId")).toString(),
title),
this, []() {});
}
else if (routeName == QLatin1String("person"))
{
QCoro::connect(m_libraryViewModel->loadPerson(
params.value(QStringLiteral("personId")).toString(),
title),
this, []() {});
}
else
{
QCoro::connect(m_libraryViewModel->loadFiltered(
params.value(QStringLiteral("filterType")).toString(),
params.value(QStringLiteral("filterValue")).toString()),
this, []() {});
}
}
m_libraryViewModel->setActive(true);
}
void HomeQuickRouterView::deactivateRoutes() void HomeQuickRouterView::deactivateRoutes()
{ {
m_dashboardViewModel->setActive(false); m_dashboardViewModel->setActive(false);
m_favoritesViewModel->setActive(false); m_favoritesViewModel->setActive(false);
m_searchViewModel->setActive(false); m_searchViewModel->setActive(false);
m_categoryViewModel->setActive(false);
m_libraryViewModel->setActive(false);
} }
void HomeQuickRouterView::forwardItemActivation(const MediaItem &item) void HomeQuickRouterView::forwardItemActivation(const MediaItem &item)
@@ -3,6 +3,8 @@
#include "../baseview.h" #include "../baseview.h"
class FavoritesViewModel; class FavoritesViewModel;
class CategoryViewModel;
class LibraryViewModel;
class NavigationController; class NavigationController;
class QuickRouterHost; class QuickRouterHost;
class SearchViewModel; class SearchViewModel;
@@ -40,6 +42,9 @@ protected:
private: private:
void updateRouteActivity(); void updateRouteActivity();
void updateSearchRoute(const QVariantMap &route, bool hostActive);
void updateCategoryRoute(const QVariantMap &route, bool hostActive);
void updateLibraryRoute(const QVariantMap &route, bool hostActive);
void deactivateRoutes(); void deactivateRoutes();
void forwardItemActivation(const MediaItem &item); void forwardItemActivation(const MediaItem &item);
void launchDashboardTask(QCoro::Task<void> &&task); void launchDashboardTask(QCoro::Task<void> &&task);
@@ -54,6 +59,10 @@ private:
UserDashboardViewModel *m_dashboardViewModel = nullptr; UserDashboardViewModel *m_dashboardViewModel = nullptr;
FavoritesViewModel *m_favoritesViewModel = nullptr; FavoritesViewModel *m_favoritesViewModel = nullptr;
SearchViewModel *m_searchViewModel = nullptr; SearchViewModel *m_searchViewModel = nullptr;
CategoryViewModel *m_categoryViewModel = nullptr;
LibraryViewModel *m_libraryViewModel = nullptr;
QString m_searchRouteKey; QString m_searchRouteKey;
QString m_searchQuery; QString m_searchQuery;
QString m_categoryRouteKey;
QString m_libraryRouteKey;
}; };
+70 -17
View File
@@ -259,9 +259,9 @@ void HomeView::setupUi()
auto navigateToDetailSlot = [this](const QString &itemId, const QString &itemName, const MediaItem &seedItem) auto navigateToDetailSlot = [this](const QString &itemId, const QString &itemName, const MediaItem &seedItem)
{ pushView(createDetailView(itemId, itemName, seedItem)); }; { pushView(createDetailView(itemId, itemName, seedItem)); };
auto navigateToFolderSlot = [this](const QString &id, const QString &name) auto navigateToFolderSlot = [this](const QString &id, const QString &name)
{ pushView(createLibraryView(id, name)); }; { openLibraryRoute(id, name); };
auto navigateToPersonSlot = [this](const QString &id, const QString &name) auto navigateToPersonSlot = [this](const QString &id, const QString &name)
{ pushView(createPersonView(id, name)); }; { openPersonRoute(id, name); };
auto navigateToPlayerSlot = [this](const QString &id, const QString &title, const QString &url, long long ticks, auto navigateToPlayerSlot = [this](const QString &id, const QString &title, const QString &url, long long ticks,
const QVariant &extraData) { launchPlayer(id, title, url, ticks, extraData); }; const QVariant &extraData) { launchPlayer(id, title, url, ticks, extraData); };
auto navigateToSeasonSlot = [this](const QString &seriesId, const QString &seasonId, const QString &seasonName) auto navigateToSeasonSlot = [this](const QString &seriesId, const QString &seasonId, const QString &seasonName)
@@ -278,12 +278,12 @@ void HomeView::setupUi()
break; break;
} }
} }
pushView(createLibraryView(id, name)); openLibraryRoute(id, name);
}; };
const auto openCategory = [this](const QString &categoryId, const QString &title) const auto openCategory = [this](const QString &categoryId, const QString &title)
{ {
m_libraryList->clearSelection(); m_libraryList->clearSelection();
pushView(createCategoryView(categoryId, title)); openCategoryRoute(categoryId, title);
}; };
m_useQuickHomeRouter = qEnvironmentVariableIntValue("QEMBY_USE_LEGACY_HOME_PAGES") != 1; m_useQuickHomeRouter = qEnvironmentVariableIntValue("QEMBY_USE_LEGACY_HOME_PAGES") != 1;
@@ -410,11 +410,11 @@ QWidget *HomeView::createDetailView(const QString &itemId, const QString &itemNa
connect(view, &DetailView::navigateToDetail, this, connect(view, &DetailView::navigateToDetail, this,
[this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); }); [this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); });
connect(view, &DetailView::navigateToFolder, this, connect(view, &DetailView::navigateToFolder, this,
[this](const QString &id, const QString &name) { pushView(createLibraryView(id, name)); }); [this](const QString &id, const QString &name) { openLibraryRoute(id, name); });
connect(view, &BaseView::navigateToPerson, this, connect(view, &BaseView::navigateToPerson, this,
[this](const QString &id, const QString &name) [this](const QString &id, const QString &name)
{ {
pushView(createPersonView(id, name)); openPersonRoute(id, name);
}); });
connect(view, &BaseView::navigateToPlayer, this, connect(view, &BaseView::navigateToPlayer, this,
@@ -429,7 +429,7 @@ QWidget *HomeView::createDetailView(const QString &itemId, const QString &itemNa
connect(view, &DetailView::triggerSearch, this, &HomeView::triggerSearch); connect(view, &DetailView::triggerSearch, this, &HomeView::triggerSearch);
connect(view, &BaseView::navigateToFilteredView, this, connect(view, &BaseView::navigateToFilteredView, this,
[this](const QString &type, const QString &value) [this](const QString &type, const QString &value)
{ pushView(createFilteredView(type, value)); }); { openFilteredRoute(type, value); });
return view; return view;
} }
@@ -449,10 +449,10 @@ QWidget *HomeView::createCategoryView(const QString &categoryId, const QString &
[this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); }); [this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); });
connect(view, &CategoryView::navigateToFolder, this, connect(view, &CategoryView::navigateToFolder, this,
[this](const QString &id, const QString &name) { pushView(createLibraryView(id, name)); }); [this](const QString &id, const QString &name) { openLibraryRoute(id, name); });
connect(view, &CategoryView::navigateToPerson, this, connect(view, &CategoryView::navigateToPerson, this,
[this](const QString &id, const QString &name) { pushView(createPersonView(id, name)); }); [this](const QString &id, const QString &name) { openPersonRoute(id, name); });
connect(view, &BaseView::navigateToPlayer, this, connect(view, &BaseView::navigateToPlayer, this,
@@ -481,10 +481,10 @@ QWidget *HomeView::createLibraryView(const QString &libraryId, const QString &ti
[this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); }); [this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); });
connect(view, &LibraryView::navigateToFolder, this, connect(view, &LibraryView::navigateToFolder, this,
[this](const QString &id, const QString &name) { pushView(createLibraryView(id, name)); }); [this](const QString &id, const QString &name) { openLibraryRoute(id, name); });
connect(view, &BaseView::navigateToPerson, this, connect(view, &BaseView::navigateToPerson, this,
[this](const QString &id, const QString &name) { pushView(createPersonView(id, name)); }); [this](const QString &id, const QString &name) { openPersonRoute(id, name); });
connect(view, &BaseView::navigateToPlayer, this, connect(view, &BaseView::navigateToPlayer, this,
@@ -520,7 +520,7 @@ QWidget *HomeView::createPersonView(const QString &personId, const QString &pers
connect(view, &BaseView::navigateToPerson, this, connect(view, &BaseView::navigateToPerson, this,
[this](const QString &id, const QString &name) { pushView(createPersonView(id, name)); }); [this](const QString &id, const QString &name) { openPersonRoute(id, name); });
connect(view, &BaseView::navigateToPlayer, this, connect(view, &BaseView::navigateToPlayer, this,
@@ -556,10 +556,10 @@ QWidget *HomeView::createSearchView(const QString &query)
[this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); }); [this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); });
connect(view, &SearchView::navigateToFolder, this, connect(view, &SearchView::navigateToFolder, this,
[this](const QString &id, const QString &name) { pushView(createLibraryView(id, name)); }); [this](const QString &id, const QString &name) { openLibraryRoute(id, name); });
connect(view, &SearchView::navigateToPerson, this, connect(view, &SearchView::navigateToPerson, this,
[this](const QString &id, const QString &name) { pushView(createPersonView(id, name)); }); [this](const QString &id, const QString &name) { openPersonRoute(id, name); });
connect(view, &BaseView::navigateToPlayer, this, connect(view, &BaseView::navigateToPlayer, this,
@@ -589,9 +589,9 @@ QWidget *HomeView::createFilteredView(const QString &filterType, const QString &
connect(view, &BaseView::navigateToDetail, this, connect(view, &BaseView::navigateToDetail, this,
[this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); }); [this](const QString &id, const QString &name, const MediaItem &seed) { pushView(createDetailView(id, name, seed)); });
connect(view, &BaseView::navigateToFolder, this, connect(view, &BaseView::navigateToFolder, this,
[this](const QString &id, const QString &name) { pushView(createLibraryView(id, name)); }); [this](const QString &id, const QString &name) { openLibraryRoute(id, name); });
connect(view, &BaseView::navigateToPerson, this, connect(view, &BaseView::navigateToPerson, this,
[this](const QString &id, const QString &name) { pushView(createPersonView(id, name)); }); [this](const QString &id, const QString &name) { openPersonRoute(id, name); });
connect(view, &BaseView::navigateToPlayer, this, connect(view, &BaseView::navigateToPlayer, this,
[this](const QString &id, const QString &title, const QString &url, long long ticks, [this](const QString &id, const QString &title, const QString &url, long long ticks,
const QVariant &extraData) { launchPlayer(id, title, url, ticks, extraData); }); const QVariant &extraData) { launchPlayer(id, title, url, ticks, extraData); });
@@ -998,7 +998,7 @@ void HomeView::setupSidebar()
{ {
return; return;
} }
pushView(createLibraryView(viewId, cleanName)); openLibraryRoute(viewId, cleanName);
}); });
m_sidebarAnim = new QPropertyAnimation(m_sidebar, "pos", this); m_sidebarAnim = new QPropertyAnimation(m_sidebar, "pos", this);
@@ -1301,6 +1301,59 @@ bool HomeView::pushQuickRoute(const QVariantMap &request)
return true; return true;
} }
void HomeView::openCategoryRoute(const QString &categoryId, const QString &title)
{
const bool favorite = categoryId.startsWith(QLatin1String("Favorite_"));
const QString normalizedId = favorite ? categoryId.mid(9) : categoryId;
const QVariantMap request{
{QStringLiteral("name"), QStringLiteral("category")},
{QStringLiteral("title"), title},
{QStringLiteral("params"),
QVariantMap{{QStringLiteral("categoryId"), normalizedId},
{QStringLiteral("title"), title},
{QStringLiteral("favorite"), favorite}}}};
if (!pushQuickRoute(request))
pushView(createCategoryView(categoryId, title));
}
void HomeView::openLibraryRoute(const QString &libraryId, const QString &title)
{
const QVariantMap request{
{QStringLiteral("name"), QStringLiteral("library")},
{QStringLiteral("title"), title},
{QStringLiteral("params"),
QVariantMap{{QStringLiteral("libraryId"), libraryId},
{QStringLiteral("title"), title}}}};
if (!pushQuickRoute(request))
pushView(createLibraryView(libraryId, title));
}
void HomeView::openPersonRoute(const QString &personId, const QString &title)
{
const QVariantMap request{
{QStringLiteral("name"), QStringLiteral("person")},
{QStringLiteral("title"), title},
{QStringLiteral("params"),
QVariantMap{{QStringLiteral("personId"), personId},
{QStringLiteral("title"), title}}}};
if (!pushQuickRoute(request))
pushView(createPersonView(personId, title));
}
void HomeView::openFilteredRoute(const QString &filterType,
const QString &filterValue)
{
const QVariantMap request{
{QStringLiteral("name"), QStringLiteral("filtered")},
{QStringLiteral("title"), filterValue},
{QStringLiteral("params"),
QVariantMap{{QStringLiteral("filterType"), filterType},
{QStringLiteral("filterValue"), filterValue},
{QStringLiteral("title"), filterValue}}}};
if (!pushQuickRoute(request))
pushView(createFilteredView(filterType, filterValue));
}
void HomeView::navigateBack() void HomeView::navigateBack()
{ {
QWidget *current = m_contentSwitcher->currentWidget(); QWidget *current = m_contentSwitcher->currentWidget();
+4
View File
@@ -117,6 +117,10 @@ private:
void pushView(QWidget* view); void pushView(QWidget* view);
bool pushQuickRoute(const QVariantMap &request); bool pushQuickRoute(const QVariantMap &request);
void openCategoryRoute(const QString &categoryId, const QString &title);
void openLibraryRoute(const QString &libraryId, const QString &title);
void openPersonRoute(const QString &personId, const QString &title);
void openFilteredRoute(const QString &filterType, const QString &filterValue);
void resetToView(QWidget* view, const QString &rootRoute = QString()); void resetToView(QWidget* view, const QString &rootRoute = QString());
QWidget* homeRootWidget() const; QWidget* homeRootWidget() const;