diff --git a/.env.example b/.env.example
index 94d96ff4..e2e6f37b 100644
--- a/.env.example
+++ b/.env.example
@@ -7,9 +7,13 @@ GAMMA_ROOT_URL=http://localhost:8081
ACTIVE_GROUP_TYPES="committee,society"
NEXTAUTH_SECRET=secret
NEXTAUTH_URL=http://localhost:3000/api/auth
+BASE_URL="http://localhost:3000"
MEDIA_PATH="./media"
ADMIN_GROUPS="styrit"
+PAGE_EDITOR_GROUPS="snit,motespresidit"
CORPORATE_RELATIONS_GROUP="armit"
-BASE_URL="http://localhost:3000"
# Ensures that the dev enviroment is the same as the production enviroment. Hydration issues can be missed in dev without otherwise.
TZ=UTC
+
+SUBSCRIBE_SLACK_URL_SV="https://"
+SUBSCRIBE_SLACK_URL_EN="https://"
diff --git a/src/app/[locale]/(main-content)/page.tsx b/src/app/[locale]/(main-content)/page.tsx
index 2fdd1a31..048a85fd 100644
--- a/src/app/[locale]/(main-content)/page.tsx
+++ b/src/app/[locale]/(main-content)/page.tsx
@@ -5,6 +5,7 @@ import ContactCard from '@/components/ContactCard/ContactCard';
import Lunch from '@/components/Lunch/Lunch';
import Sponsors from '@/components/Sponsors/Sponsors';
import Calendar from '@/components/Calendar/Calendar';
+import SubscribeOptions from '@/components/SubscribeOptions/SubscribeOptions';
export const revalidate = 3600;
@@ -34,6 +35,7 @@ export default async function Home(props: {
const LeftBar = ({ locale }: { locale: string }) => {
return (
+
diff --git a/src/components/NewsList/NewsClient.tsx b/src/components/NewsList/NewsClient.tsx
index d47f3e84..a2df1f62 100644
--- a/src/components/NewsList/NewsClient.tsx
+++ b/src/components/NewsList/NewsClient.tsx
@@ -10,7 +10,6 @@ import NewsCard from './NewsCard/NewsCard';
import styles from './NewsList.module.scss';
import clientStyles from './NewsListClient.module.scss';
import NewsPost from './NewsPost/NewsPost';
-import RssFeedButton from './RssFeedButton';
import ViewToggle from './ViewToggle';
interface NewsClientProps {
@@ -34,7 +33,6 @@ const NewsClient = ({ news, canPost, locale }: NewsClientProps) => {
{l.news.title}
-
(null);
+
+ const handleClick = async (e: React.MouseEvent) => {
+ e.preventDefault();
+ try {
+ await navigator.clipboard.writeText(window.location.origin + rssUrl);
+ setTooltipVisible(true);
+ if (tooltipTimeout.current) clearTimeout(tooltipTimeout.current);
+ tooltipTimeout.current = setTimeout(() => setTooltipVisible(false), 1500);
+ } catch {
+ window.open(rssUrl, '_blank', 'noopener,noreferrer');
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/SubscribeOptions/SubscribeOptions.module.scss b/src/components/SubscribeOptions/SubscribeOptions.module.scss
new file mode 100644
index 00000000..d66dd86b
--- /dev/null
+++ b/src/components/SubscribeOptions/SubscribeOptions.module.scss
@@ -0,0 +1,67 @@
+.title {
+ font-size: 1.35rem;
+ font-weight: 700;
+ text-align: left;
+ margin-bottom: 1rem;
+}
+
+.optionsContainer {
+ display: flex;
+ flex-direction: row;
+ gap: 0.5rem;
+}
+
+.iconButton {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem;
+ height: 2.5rem;
+ background-color: transparent;
+ border: 1px solid var(--subscribe-border-color, var(--text-color, #333));
+ border-radius: 0.5rem;
+ color: inherit;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 1.3rem;
+ position: relative;
+ padding: 0;
+
+ &:hover:not(:disabled) {
+ background-color: var(--subscribe-hover-bg, rgba(0, 0, 0, 0.05));
+ border-color: var(--subscribe-hover-border-color, var(--text-color, #333));
+ }
+
+ &:focus-visible {
+ outline: 2px solid currentColor;
+ outline-offset: 2px;
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+}
+
+.tooltip {
+ position: absolute;
+ top: 50%;
+ left: calc(100% + 8px);
+ transform: translateY(-50%);
+ background: var(--background-color, #24292f);
+ color: var(--news-view-toggle-selected-color, #fff);
+ padding: 6px 12px;
+ border-radius: 6px;
+ font-size: 0.85rem;
+ white-space: nowrap;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
+ opacity: 0;
+ pointer-events: none;
+ user-select: none;
+ transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ z-index: 10;
+}
+
+.tooltipVisible {
+ opacity: 1;
+}
diff --git a/src/components/SubscribeOptions/SubscribeOptions.tsx b/src/components/SubscribeOptions/SubscribeOptions.tsx
new file mode 100644
index 00000000..b50ba25b
--- /dev/null
+++ b/src/components/SubscribeOptions/SubscribeOptions.tsx
@@ -0,0 +1,55 @@
+import i18nService from '@/services/i18nService';
+import Link from 'next/link';
+import { MdEmail } from 'react-icons/md';
+import { SiSlack } from 'react-icons/si';
+import ContentPane from '../ContentPane/ContentPane';
+import RssButton from './RssButton';
+import styles from './SubscribeOptions.module.scss';
+import getSlackSubscribeLink from '@/hooks/getSlackSubscribeLink';
+
+interface SubscribeOptionsProps {
+ locale: string;
+}
+
+export default async function SubscribeOptions({
+ locale
+}: SubscribeOptionsProps) {
+ const l = i18nService.getLocale(locale);
+ const slackSubscribeUrl = await getSlackSubscribeLink();
+
+ return (
+
+ {l.news.subscribe}
+
+ {slackSubscribeUrl && (
+
+
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/dictionaries/en.json b/src/dictionaries/en.json
index 33f49ded..83961e8a 100644
--- a/src/dictionaries/en.json
+++ b/src/dictionaries/en.json
@@ -10,7 +10,8 @@
"close": "Close",
"save": "Save",
"manage": "Manage",
- "add": "Add"
+ "add": "Add",
+ "comingSoon": "Comming soon"
},
"site": {
"siteTitle": "IT at Chalmers",
@@ -41,6 +42,8 @@
"by": "by",
"unknown": "unknown author",
"subscribe": "Subscribe to news feed",
+ "subscribeViaRss": "Follow us via RSS",
+ "subscribeViaSlack": "Join our Slack",
"confirmDelete": "Are you sure you want to delete this post? This action cannot be undone.",
"deleting": "Deleting...",
"deleted": "Deleted!",
diff --git a/src/dictionaries/sv.json b/src/dictionaries/sv.json
index 41690486..80c3fd82 100644
--- a/src/dictionaries/sv.json
+++ b/src/dictionaries/sv.json
@@ -10,7 +10,8 @@
"close": "Stäng",
"save": "Spara",
"manage": "Hantera",
- "add": "Lägg till"
+ "add": "Lägg till",
+ "comingSoon": "Kommer snart"
},
"site": {
"siteTitle": "IT på Chalmers",
@@ -41,6 +42,8 @@
"by": "av",
"unknown": "okänd användare",
"subscribe": "Prenumerera på nyheter",
+ "subscribeViaRss": "Följ oss via RSS",
+ "subscribeViaSlack": "Gå med i vår Slack",
"confirmDelete": "Vill du radera nyheten? Detta går inte att ångra!",
"deleting": "Raderar...",
"deleted": "Raderad!",
diff --git a/src/hooks/getSlackSubscribeLink.ts b/src/hooks/getSlackSubscribeLink.ts
new file mode 100644
index 00000000..8924507a
--- /dev/null
+++ b/src/hooks/getSlackSubscribeLink.ts
@@ -0,0 +1,10 @@
+'use server';
+
+import i18nService from '@/services/i18nService';
+
+const { SUBSCRIBE_SLACK_URL_EN: EN, SUBSCRIBE_SLACK_URL_SV: SV } = process.env;
+
+export default async function getSlackSubscribeLink(): Promise {
+ const isEn = i18nService.getLocale().en;
+ return (isEn ? EN : SV) || null;
+}