Skip to content

chore(release): add release workflow, update docs, RTK tags, UI perf improvements#158

Merged
farhan2742 merged 1 commit into
arbisoft:devfrom
arbisoft-qaisarirfan:ARBISOFTSESS-189
Apr 22, 2026
Merged

chore(release): add release workflow, update docs, RTK tags, UI perf improvements#158
farhan2742 merged 1 commit into
arbisoft:devfrom
arbisoft-qaisarirfan:ARBISOFTSESS-189

Conversation

@arbisoft-qaisarirfan

@arbisoft-qaisarirfan arbisoft-qaisarirfan commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🚀 Description

Set up automated release process with release-it and GitHub Actions. Optimize Dockerfile for better caching/layers. Sync docs with recent changes. Add perf improvements (dynamic page imports, Next Image blur, RTK tags). Bump to v1.3.0.

📌 Summary

Prepares production-ready release tooling, container optimization, comprehensive docs refresh, and targeted perf/UI enhancements for better loading and DX.

🔧 Changes Implemented

  • ✅ Add .github/workflows/release.yml for auto-release on dev→main merge
  • ✅ Add .release-it.json for conventional changelog & GitHub releases (no npm publish)
  • ✅ Optimize Dockerfile: multi-stage deps caching, standalone output
  • ✅ Update docs/*: ARCHITECTURE.md, deployment-and-release.md, etc. (reflect perf, virtuoso, RTK tags, release flow)
  • ✅ Dynamic imports: HomePage, VideoDetail, VideosListingPage, SearchResultsPage
  • VideoCard: Next Image w/ blur/placeholder, remove hover video (perf), React.memo, test/snapshot updates
  • EmptyState: use Next Image
  • ✅ Theme: font display: swap
  • ✅ RTK: add tagTypes/providesTags for Event/Tag/Playlist/Recommendation
  • ✅ Hooks: useVideoQueryManager page reset fix w/ ref
  • VideosListingPage: auto refetchOnMountOrArgChange
  • ✅ Config: next.config.js images *, .prettierrc remove deprecated, tsconfig.json remove baseUrl
  • ✅ New BLUR_DATA_URI, package.json/lock v1.3.0

🛠️ How It Works?

  1. Release: On dev→main PR merge, workflow runs npm run release --ci: bumps version, commits/tags, updates CHANGELOG.md, creates GH release.
  2. Docker: deps stage caches node_modules, builder reuses, runner lightweight standalone.
  3. Perf: Dynamic imports code-split pages; Next Image optimizes/remote lazy w/ blur; no hover video reduces initial load.
  4. RTK: Tags enable future invalidation; query manager resets page=1 on filter changes.
  5. Docs: Synced w/ virtuoso removal?, RTK updates, release process.

Edge cases: Release skips if not dev→main; Docker build args for NEXT_PUBLIC_*; images allow all hostnames (review security).

✅ Checklist Before Merging

  • Test docker build . & docker-compose up
  • Verify npm run release --ci --dry-run changelog
  • Check dynamic imports bundle sizes (next build)
  • Confirm docs accuracy (e.g., virtuoso still used?)
  • RTK tags don't break queries
  • Prettier/ESLint pass
  • No console errors on page loads/VideoCard hover

📸 Screenshots (if applicable)

N/A (configs/docs/perf)

🔗 Related Issues

https://projects.arbisoft.com/arbisoft/browse/ARBISOFTSESS-189/

📢 Notes for Reviewers

  • Test release workflow manually via PR dev→main.
  • images remotePatterns: "*" broad—restrict if possible post-localhost/prod.
  • VideoCard hover video removed (perf/simplicity)—re-add if UX needed.
  • Version bump included—release-it will handle future.
  • Docs updated extensively—spot-check sections like deployment/release.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on performance-oriented refactors across the Next.js app: replacing react-virtuoso with a native IntersectionObserver infinite scroll, tightening RTK Query caching primitives (tag types + providesTags), optimizing Docker builds, and improving image/font loading behavior.

Changes:

  • Replace react-virtuoso virtualization with a custom infinite scroll hook and adjust pagination/query-reset behavior.
  • Introduce RTK Query tagTypes and add providesTags to events-related endpoints.
  • Optimize image loading (Next/Image + blur placeholder), introduce code-splitting via dynamic imports, and update Docker build stages + docs.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/redux/events/apiSlice.ts Adds providesTags to event-related queries to support tag-based caching.
src/redux/baseApi.tsx Defines RTK Query tagTypes for events/tags/playlists/recommendations.
src/hooks/useVideoQueryManager.ts Updates pagination reset logic and query param handling for listings.
src/hooks/useInfiniteScroll.ts New IntersectionObserver-based load-more hook.
src/features/VideosListingPage/videosListingPage.tsx Removes Virtuoso grid; implements native infinite scroll + useGetEventsQuery usage.
src/features/VideosListingPage/styled.tsx Simplifies grid styling to apply directly on the container.
src/features/VideosListingPage/skeletonLoader.tsx Adjusts skeleton layout to match new grid container styling.
src/constants/images.ts Adds a shared blur placeholder data URI constant.
src/components/theme/theme-provider.tsx Sets font display: "swap" for Inter/Roboto Condensed.
src/components/VideoCard/videoCard.tsx Migrates thumbnail rendering to Next/Image, removes hover-preview video, memoizes component.
src/components/VideoCard/videoCard.test.tsx Removes hover-preview tests and updates imports accordingly.
src/components/VideoCard/snapshots/videoCard.test.tsx.snap Updates snapshot output for Next/Image rendering changes.
src/components/EmptyState/emptyState.tsx Switches empty-state image rendering to Next/Image.
src/app/videos/results/page.tsx Uses dynamic import for SearchResultsPage for code splitting.
src/app/videos/page.tsx Uses dynamic import for VideosListingPage for code splitting.
src/app/videos/[videoId]/page.tsx Uses dynamic import for VideoDetail for code splitting.
src/app/page.tsx Uses dynamic import for HomePage for code splitting.
next.config.js Broadens Next/Image remotePatterns to allow any hostname.
docs/state-management-and-api-layer.md Updates RTK Query tagTypes and notes on tag-based caching.
docs/project-overview.md Updates helper libs list after removing react-virtuoso.
docs/deployment-and-release.md Updates Docker multi-stage + caching documentation.
docs/ARCHITECTURE.md Removes now-outdated note about empty tagTypes.
Dockerfile Refactors to a 3-stage build with cache mounts and a corrected runtime CMD.
Comments suppressed due to low confidence (1)

src/hooks/useVideoQueryManager.ts:48

  • prevParamsRef + page: paramsChanged ? 1 : page prevents stale page numbers from being sent after a filter change, but it also means page state can be incremented (via infinite scroll) while paramsChanged is still true, and those increments won’t affect the query arg until after the effect updates prevParamsRef. This can lead to jumping directly to later pages once paramsChanged flips false. Consider exposing an isResetting flag (paramsChanged) to disable load-more until the reset effect runs, or update the “previous params” synchronously when detecting a change.
  const prevParamsRef = useRef<typeof parsedParams | null>(null);

  useEffect(() => {
    setPage(1);
    prevParamsRef.current = parsedParams;
  }, [parsedParams.tag, parsedParams.playlist, parsedParams.search, parsedParams.order, parsedParams.year]);

  const apiParams = useMemo(() => {
    const prevParams = prevParamsRef.current;
    const paramsChanged =
      !prevParams ||
      prevParams.tag !== parsedParams.tag ||
      prevParams.playlist !== parsedParams.playlist ||
      prevParams.search !== parsedParams.search ||
      prevParams.order !== parsedParams.order ||
      prevParams.year !== parsedParams.year;

    return parseNonPassedParams({
      ...defaultParams,
      page: paramsChanged ? 1 : page,
      page_size: 12,
      ordering: parsedParams.order ? [parsedParams.order] : ["-event_time"],
      search: parsedParams.search ?? undefined,
      tag: parsedParams.search ? "" : (parsedParams.tag ?? ""),
      playlist: parsedParams.search ? "" : (parsedParams.playlist ?? ""),
      event_time_after: parsedParams.year ? format(startOfYear(new Date(parsedParams.year)), "yyyy-MM-dd") : undefined,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +150 to +165
<>
<VideoListingContainer>
{videoData.map((videoCard) => {
return (
<VideoCard
data={transformVideoToCardData(videoCard)}
href={`/videos/${videoCard.slug}`}
key={videoCard.id}
width="100%"
/>
);
})}
</VideoListingContainer>
{isFetching && <SkeletonLoader count={6} />}
{hasNextPage && <Box ref={loadMoreRef} height={1} aria-hidden />}
</>
Comment on lines +64 to 74
<Image
data-testid="video-card-image"
alt={data.title}
height={196}
width={315}
src={data.thumbnail || DEFAULT_THUMBNAIL}
loading="lazy"
placeholder="blur"
blurDataURL={BLUR_DATA_URI}
sizes="315px"
style={{ borderRadius: "8px" }}
Comment thread src/constants/images.ts Outdated
Comment on lines +1 to +3
/* eslint-disable max-len */
export const BLUR_DATA_URI =
"data:image/jpeg;base64,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";
Comment thread docs/state-management-and-api-layer.md Outdated
## Notes and Gaps

- RTK Query tags are not currently configured, so tag-based invalidation is not documented.
- RTK Query tags are configured, and endpoints provide matching tags for automatic cache invalidation on relevant mutations.
Comment thread Dockerfile Outdated
Comment on lines +9 to +12
# Cache npm downloads
RUN --mount=type=cache,target=/root/.npm \
npm ci --prefer-offline --no-audit --progress=false

Comment thread src/hooks/useInfiniteScroll.ts Outdated
Comment on lines +9 to +15
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && canLoadMore && !loading) {
onLoadMore();
}
},
{ rootMargin: "400px" }
@arbisoft-qaisarirfan arbisoft-qaisarirfan changed the title refactor(virtualization): custom IntersectionObserver + docker optimizations chore(release): add release workflow, update docs, RTK tags, UI perf improvements Apr 17, 2026
@arbisoft-qaisarirfan arbisoft-qaisarirfan force-pushed the ARBISOFTSESS-189 branch 2 times, most recently from d2f21dd to 954f86e Compare April 17, 2026 19:41

@farhan2742 farhan2742 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@farhan2742 farhan2742 merged commit aba80e7 into arbisoft:dev Apr 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants