AppsGrid.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { FormattedMessage, useIntl } from "react-intl"
  2. import { AppCard } from "../components/AppCard"
  3. import classNames from "classnames"
  4. import { useState } from "react"
  5. import SelectMenu from "../components/SelectMenu"
  6. import { sortBy as _sortBy } from "lodash"
  7. import type { appsList } from "../data/apps"
  8. import Category from "../components/Category"
  9. export type AppsGridProps = {
  10. apps: appsList
  11. }
  12. /** Renders AppCards as a grid, with sorting and filtering options */
  13. export const AppsGrid = ({ apps }: AppsGridProps) => {
  14. const intl = useIntl()
  15. const [activeCategory, setActiveCategory] = useState("all")
  16. //prettier-ignore
  17. const categories = [
  18. { key: "all", label: intl.formatMessage({ id: "browse_apps.all", defaultMessage: "All" }) },
  19. { key: "android", label: intl.formatMessage({ id: "browse_apps.android", defaultMessage: "Android" }) },
  20. { key: "ios", label: intl.formatMessage({ id: "browse_apps.ios", defaultMessage: "iOS" }) },
  21. { key: "web", label: intl.formatMessage({ id: "browse_apps.web", defaultMessage: "Web" }) },
  22. { key: "sailfish", label: intl.formatMessage({ id: "browse_apps.sailfish", defaultMessage: "SailfishOS" }) },
  23. { key: "desktop", label: intl.formatMessage({ id: "browse_apps.desktop", defaultMessage: "Desktop" }) },
  24. { key: "retro", label: intl.formatMessage({ id: "browse_apps.retro", defaultMessage: "Retro computing" }) },
  25. ]
  26. /** normalizing the apps dictionary as an array */
  27. const allApps = Object.entries(apps)
  28. .map(([category, apps]) =>
  29. apps.map(({ name, icon, url, paid, released_on, hidden_from_all }) => ({
  30. name,
  31. icon,
  32. url,
  33. paid: paid ?? false,
  34. hidden_from_all: hidden_from_all ?? false,
  35. released_on: new Date(released_on) ?? null,
  36. category,
  37. categoryLabel: categories.find((c) => c.key === category)["label"],
  38. }))
  39. )
  40. .flat()
  41. //prettier-ignore
  42. const sortOptions = [
  43. { value: "date_added", label: intl.formatMessage({ id: "sorting.recently_added", defaultMessage: "Recently Added" }) },
  44. { value: "paid", label: intl.formatMessage({ id: "sorting.free", defaultMessage: "Free" }) },
  45. { value: "category", label: intl.formatMessage({ id: "sorting.category", defaultMessage: "Category" }) },
  46. { value: "name", label: intl.formatMessage({ id: "sorting.name", defaultMessage: "Alphabetical" }) },
  47. ]
  48. const [sortOption, setSortOption] = useState(sortOptions[0].value)
  49. const filteredApps = allApps.filter(
  50. ({ category, hidden_from_all }) =>
  51. category === activeCategory ||
  52. (activeCategory === "all" && !hidden_from_all)
  53. )
  54. const sortedAndFilteredApps = _sortBy(filteredApps, sortOption)
  55. return (
  56. <div>
  57. <div>
  58. <h2 className="h4 mb-8">
  59. <FormattedMessage
  60. id="browse_apps.title2"
  61. defaultMessage="Browse third-party apps"
  62. />
  63. </h2>
  64. <div className="-mx-gutter ps-gutter mb-6 overflow-x-auto">
  65. <div className="flex flex-wrap gap-gutter md:flex-nowrap">
  66. {categories.map((category) => (
  67. <Category
  68. key={category.key}
  69. value={category.key}
  70. currentValue={activeCategory}
  71. label={category.label}
  72. onChange={(e) => setActiveCategory(e.target.value)}
  73. />
  74. ))}
  75. </div>
  76. </div>
  77. </div>
  78. <div className="my-8">
  79. <SelectMenu
  80. label={
  81. <FormattedMessage id="sorting.sort_by" defaultMessage="Sort" />
  82. }
  83. value={sortOption}
  84. onChange={(v) => {
  85. setSortOption(v)
  86. }}
  87. options={sortOptions}
  88. />
  89. </div>
  90. <div className="grid grid-cols-[repeat(auto-fill,minmax(250px,1fr))] gap-4">
  91. {sortedAndFilteredApps.map(AppCard)}
  92. </div>
  93. </div>
  94. )
  95. }
  96. export default AppsGrid