import flet as ft import designer as d import api_client as api import theme_manager class NewsDetailView: def __init__(self, page: ft.Page, slug: str): self.page = page self.slug = slug self._content_col = ft.Column( [ft.Container(content=d.loader(), alignment=ft.alignment.center, padding=40)], expand=True, ) def build_page_view(self) -> ft.View: header = ft.Container( content=ft.Row( [ ft.IconButton( ft.icons.ARROW_BACK, icon_color=ft.colors.WHITE, tooltip="Назад", on_click=lambda e: self.page.go("/"), ), ft.Row([ ft.Icon(ft.icons.NEWSPAPER, color=ft.colors.WHITE, size=24), ft.Text("Новости", size=18, weight=ft.FontWeight.BOLD, color=ft.colors.WHITE), ], spacing=8), ft.Container(width=40), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN, ), bgcolor=d.ADMIN_SIDEBAR, padding=ft.padding.symmetric(horizontal=16, vertical=12), ) return ft.View( route=f"/news/{self.slug}", bgcolor=d.BACKGROUND, controls=[ ft.Column( [ header, ft.Container( content=self._content_col, alignment=ft.alignment.top_center, expand=True, padding=ft.padding.symmetric(horizontal=24, vertical=20), ), ], spacing=0, expand=True, scroll=ft.ScrollMode.AUTO, ) ], ) async def load(self): article = await api.get_article(self.slug) if not article: theme_manager.reset_font(self.page) self._content_col.controls = [ ft.Container( content=ft.Column( [ ft.Icon(ft.icons.ARTICLE_OUTLINED, size=64, color=d.DIVIDER), d.headline("Статья не найдена", size=20), d.btn_outlined("На главную", on_click=lambda e: self.page.go("/")), ], horizontal_alignment=ft.CrossAxisAlignment.CENTER, spacing=16, ), alignment=ft.alignment.center, padding=60, ) ] self.page.update() return font = article.get("font_family", "Merriweather") theme_manager.apply_article_font(self.page, font) self._render(article) def _render(self, a: dict): font = a.get("font_family", "Merriweather") cat = a.get("category") tags = a.get("tags", []) pub_date = a.get("published_at", "")[:10] if a.get("published_at") else "" meta_chips = ft.Row(wrap=True, spacing=8, run_spacing=8) if cat: meta_chips.controls.append( ft.Container( content=ft.Text(cat["name"], size=12, color=ft.colors.WHITE), bgcolor=d.PRIMARY, border_radius=4, padding=ft.padding.symmetric(horizontal=10, vertical=4), ) ) for tag in tags: meta_chips.controls.append( ft.Container( content=ft.Text(f"#{tag['name']}", size=12, color=d.PRIMARY), bgcolor=ft.colors.with_opacity(0.1, d.PRIMARY), border_radius=4, padding=ft.padding.symmetric(horizontal=10, vertical=4), ) ) controls = [ d.headline(a["title"], size=30, font=d.FONT_DISPLAY), ft.Row( [ meta_chips, ft.Row([ ft.Text(pub_date, size=12, color=d.TEXT_SECONDARY), ft.Row([ ft.Icon(ft.icons.REMOVE_RED_EYE_OUTLINED, size=14, color=d.TEXT_SECONDARY), ft.Text(str(a.get("view_count", 0)), size=12, color=d.TEXT_SECONDARY), ], spacing=4), ], spacing=16), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN, wrap=True, ), ] if a.get("cover_url"): controls.append( ft.Container( content=ft.Image( src=a["cover_url"], width=float("inf"), height=400, fit=ft.ImageFit.COVER, border_radius=12, ), border_radius=12, clip_behavior=ft.ClipBehavior.HARD_EDGE, ) ) if a.get("excerpt"): controls.append( ft.Container( content=ft.Text( a["excerpt"], size=16, color=d.TEXT_SECONDARY, italic=True, ), border=ft.Border(left=ft.BorderSide(4, d.PRIMARY)), padding=ft.padding.only(left=16, top=8, bottom=8), ) ) controls.append(d.divider()) controls.append( ft.Markdown( value=a.get("content", ""), selectable=True, extension_set=ft.MarkdownExtensionSet.GITHUB_WEB, on_tap_link=lambda e: self.page.launch_url(e.data), auto_follow_links=False, ) ) controls.append(ft.Divider(height=32, color="transparent")) controls.append( ft.TextButton( "← Назад к новостям", icon=ft.icons.ARROW_BACK, style=ft.ButtonStyle(color=d.PRIMARY), on_click=lambda e: self.page.go("/"), ) ) self._content_col.controls = [ ft.Container( content=ft.Column(controls, spacing=16), bgcolor=d.SURFACE, border_radius=12, padding=32, border=ft.border.all(1, d.DIVIDER), ) ] self.page.update()