You've already forked AstralRinth
forked from didirus/AstralRinth
Add TailwindCSS (#1252)
* Setup TailwindCSS * Fully setup configuration * Refactor some tailwind variables
This commit is contained in:
@@ -163,7 +163,7 @@
|
||||
id: 'withhold-reply',
|
||||
color: 'danger',
|
||||
action: () => {
|
||||
sendReply('withheld')
|
||||
sendReply('withheld');
|
||||
},
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld',
|
||||
@@ -174,7 +174,7 @@
|
||||
id: 'withhold',
|
||||
color: 'danger',
|
||||
action: () => {
|
||||
setStatus('withheld')
|
||||
setStatus('withheld');
|
||||
},
|
||||
hoverFilled: true,
|
||||
disabled: project.status === 'withheld',
|
||||
@@ -196,22 +196,22 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { OverflowMenu, MarkdownEditor } from '@modrinth/ui'
|
||||
import { DropdownIcon } from '@modrinth/assets'
|
||||
import { useImageUpload } from '~/composables/image-upload.ts'
|
||||
import CopyCode from '~/components/ui/CopyCode.vue'
|
||||
import ReplyIcon from '~/assets/images/utils/reply.svg?component'
|
||||
import SendIcon from '~/assets/images/utils/send.svg?component'
|
||||
import CloseIcon from '~/assets/images/utils/check-circle.svg?component'
|
||||
import CrossIcon from '~/assets/images/utils/x.svg?component'
|
||||
import EyeOffIcon from '~/assets/images/utils/eye-off.svg?component'
|
||||
import CheckIcon from '~/assets/images/utils/check.svg?component'
|
||||
import ModerationIcon from '~/assets/images/sidebar/admin.svg?component'
|
||||
import ThreadMessage from '~/components/ui/thread/ThreadMessage.vue'
|
||||
import { isStaff } from '~/helpers/users.js'
|
||||
import { isApproved, isRejected } from '~/helpers/projects.js'
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
import Checkbox from '~/components/ui/Checkbox.vue'
|
||||
import { OverflowMenu, MarkdownEditor } from "@modrinth/ui";
|
||||
import { DropdownIcon } from "@modrinth/assets";
|
||||
import { useImageUpload } from "~/composables/image-upload.ts";
|
||||
import CopyCode from "~/components/ui/CopyCode.vue";
|
||||
import ReplyIcon from "~/assets/images/utils/reply.svg?component";
|
||||
import SendIcon from "~/assets/images/utils/send.svg?component";
|
||||
import CloseIcon from "~/assets/images/utils/check-circle.svg?component";
|
||||
import CrossIcon from "~/assets/images/utils/x.svg?component";
|
||||
import EyeOffIcon from "~/assets/images/utils/eye-off.svg?component";
|
||||
import CheckIcon from "~/assets/images/utils/check.svg?component";
|
||||
import ModerationIcon from "~/assets/images/sidebar/admin.svg?component";
|
||||
import ThreadMessage from "~/components/ui/thread/ThreadMessage.vue";
|
||||
import { isStaff } from "~/helpers/users.js";
|
||||
import { isApproved, isRejected } from "~/helpers/projects.js";
|
||||
import Modal from "~/components/ui/Modal.vue";
|
||||
import Checkbox from "~/components/ui/Checkbox.vue";
|
||||
|
||||
const props = defineProps({
|
||||
thread: {
|
||||
@@ -236,166 +236,166 @@ const props = defineProps({
|
||||
currentMember: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
return null;
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update-thread'])
|
||||
const emit = defineEmits(["update-thread"]);
|
||||
|
||||
const app = useNuxtApp()
|
||||
const flags = useFeatureFlags()
|
||||
const app = useNuxtApp();
|
||||
const flags = useFeatureFlags();
|
||||
|
||||
const members = computed(() => {
|
||||
const members = {}
|
||||
const members = {};
|
||||
for (const member of props.thread.members) {
|
||||
members[member.id] = member
|
||||
members[member.id] = member;
|
||||
}
|
||||
return members
|
||||
})
|
||||
return members;
|
||||
});
|
||||
|
||||
const replyBody = ref('')
|
||||
const replyBody = ref("");
|
||||
|
||||
const sortedMessages = computed(() => {
|
||||
if (props.thread !== null) {
|
||||
return props.thread.messages
|
||||
.slice()
|
||||
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created))
|
||||
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created));
|
||||
}
|
||||
return []
|
||||
})
|
||||
return [];
|
||||
});
|
||||
|
||||
const modalSubmit = ref(null)
|
||||
const modalSubmit = ref(null);
|
||||
|
||||
async function updateThreadLocal() {
|
||||
let threadId = null
|
||||
let threadId = null;
|
||||
if (props.project) {
|
||||
threadId = props.project.thread_id
|
||||
threadId = props.project.thread_id;
|
||||
} else if (props.report) {
|
||||
threadId = props.report.thread_id
|
||||
threadId = props.report.thread_id;
|
||||
}
|
||||
let thread = null
|
||||
let thread = null;
|
||||
if (threadId) {
|
||||
thread = await useBaseFetch(`thread/${threadId}`)
|
||||
thread = await useBaseFetch(`thread/${threadId}`);
|
||||
}
|
||||
emit('update-thread', thread)
|
||||
emit("update-thread", thread);
|
||||
}
|
||||
|
||||
const imageIDs = ref([])
|
||||
const imageIDs = ref([]);
|
||||
|
||||
async function onUploadImage(file) {
|
||||
const response = await useImageUpload(file, { context: 'thread_message' })
|
||||
const response = await useImageUpload(file, { context: "thread_message" });
|
||||
|
||||
imageIDs.value.push(response.id)
|
||||
imageIDs.value.push(response.id);
|
||||
// Keep the last 10 entries of image IDs
|
||||
imageIDs.value = imageIDs.value.slice(-10)
|
||||
imageIDs.value = imageIDs.value.slice(-10);
|
||||
|
||||
return response.url
|
||||
return response.url;
|
||||
}
|
||||
|
||||
async function sendReply(status = null, privateMessage = false) {
|
||||
try {
|
||||
const body = {
|
||||
body: {
|
||||
type: 'text',
|
||||
type: "text",
|
||||
body: replyBody.value,
|
||||
private: privateMessage,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if (imageIDs.value.length > 0) {
|
||||
body.body = {
|
||||
...body.body,
|
||||
uploaded_images: imageIDs.value,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
await useBaseFetch(`thread/${props.thread.id}`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body,
|
||||
})
|
||||
});
|
||||
|
||||
replyBody.value = ''
|
||||
replyBody.value = "";
|
||||
|
||||
await updateThreadLocal()
|
||||
await updateThreadLocal();
|
||||
if (status !== null) {
|
||||
props.setStatus(status)
|
||||
props.setStatus(status);
|
||||
}
|
||||
} catch (err) {
|
||||
app.$notify({
|
||||
group: 'main',
|
||||
title: 'Error sending message',
|
||||
group: "main",
|
||||
title: "Error sending message",
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function closeReport(reply) {
|
||||
if (reply) {
|
||||
await sendReply()
|
||||
await sendReply();
|
||||
}
|
||||
|
||||
try {
|
||||
await useBaseFetch(`report/${props.report.id}`, {
|
||||
method: 'PATCH',
|
||||
method: "PATCH",
|
||||
body: {
|
||||
closed: true,
|
||||
},
|
||||
})
|
||||
await updateThreadLocal()
|
||||
});
|
||||
await updateThreadLocal();
|
||||
} catch (err) {
|
||||
app.$notify({
|
||||
group: 'main',
|
||||
title: 'Error closing report',
|
||||
group: "main",
|
||||
title: "Error closing report",
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function reopenReport() {
|
||||
try {
|
||||
await useBaseFetch(`report/${props.report.id}`, {
|
||||
method: 'PATCH',
|
||||
method: "PATCH",
|
||||
body: {
|
||||
closed: false,
|
||||
},
|
||||
})
|
||||
await updateThreadLocal()
|
||||
});
|
||||
await updateThreadLocal();
|
||||
} catch (err) {
|
||||
app.$notify({
|
||||
group: 'main',
|
||||
title: 'Error reopening report',
|
||||
group: "main",
|
||||
title: "Error reopening report",
|
||||
text: err.data ? err.data.description : err,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const replyWithSubmission = ref(false)
|
||||
const submissionConfirmation = ref(false)
|
||||
const replyWithSubmission = ref(false);
|
||||
const submissionConfirmation = ref(false);
|
||||
|
||||
function openResubmitModal(reply) {
|
||||
submissionConfirmation.value = false
|
||||
replyWithSubmission.value = reply
|
||||
modalSubmit.value.show()
|
||||
submissionConfirmation.value = false;
|
||||
replyWithSubmission.value = reply;
|
||||
modalSubmit.value.show();
|
||||
}
|
||||
|
||||
async function resubmit() {
|
||||
if (replyWithSubmission.value) {
|
||||
await sendReply('processing')
|
||||
await sendReply("processing");
|
||||
} else {
|
||||
await props.setStatus('processing')
|
||||
await props.setStatus("processing");
|
||||
}
|
||||
modalSubmit.value.hide()
|
||||
modalSubmit.value.hide();
|
||||
}
|
||||
|
||||
const requestedStatus = computed(() => props.project.requested_status ?? 'approved')
|
||||
const requestedStatus = computed(() => props.project.requested_status ?? "approved");
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -106,12 +106,12 @@ import {
|
||||
LockIcon,
|
||||
ModrinthIcon,
|
||||
ScaleIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { OverflowMenu, ConditionalNuxtLink } from '@modrinth/ui'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
import Avatar from '~/components/ui/Avatar.vue'
|
||||
import Badge from '~/components/ui/Badge.vue'
|
||||
import { isStaff } from '~/helpers/users.js'
|
||||
} from "@modrinth/assets";
|
||||
import { OverflowMenu, ConditionalNuxtLink } from "@modrinth/ui";
|
||||
import { renderString } from "@modrinth/utils";
|
||||
import Avatar from "~/components/ui/Avatar.vue";
|
||||
import Badge from "~/components/ui/Badge.vue";
|
||||
import { isStaff } from "~/helpers/users.js";
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
@@ -142,34 +142,34 @@ const props = defineProps({
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update-thread'])
|
||||
const emit = defineEmits(["update-thread"]);
|
||||
|
||||
const formattedMessage = computed(() => {
|
||||
const body = renderString(props.message.body.body)
|
||||
const body = renderString(props.message.body.body);
|
||||
if (props.forceCompact) {
|
||||
const hasImage = body.includes('<img')
|
||||
const noHtml = body.replace(/<\/?[^>]+(>|$)/g, '')
|
||||
const hasImage = body.includes("<img");
|
||||
const noHtml = body.replace(/<\/?[^>]+(>|$)/g, "");
|
||||
if (noHtml.trim()) {
|
||||
return noHtml
|
||||
return noHtml;
|
||||
} else if (hasImage) {
|
||||
return 'sent an image.'
|
||||
return "sent an image.";
|
||||
} else {
|
||||
return 'sent a message.'
|
||||
return "sent a message.";
|
||||
}
|
||||
}
|
||||
return body
|
||||
})
|
||||
return body;
|
||||
});
|
||||
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const timeSincePosted = ref(formatRelativeTime(props.message.created))
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
const timeSincePosted = ref(formatRelativeTime(props.message.created));
|
||||
|
||||
async function deleteMessage() {
|
||||
await useBaseFetch(`message/${props.message.id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
emit('update-thread')
|
||||
method: "DELETE",
|
||||
});
|
||||
emit("update-thread");
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -194,9 +194,9 @@ async function deleteMessage() {
|
||||
--gap-size: var(--spacing-card-sm);
|
||||
display: grid;
|
||||
grid-template:
|
||||
'icon author actions'
|
||||
'icon body actions'
|
||||
'date date date';
|
||||
"icon author actions"
|
||||
"icon body actions"
|
||||
"date date date";
|
||||
grid-template-columns: min-content auto 1fr;
|
||||
column-gap: var(--gap-size);
|
||||
row-gap: var(--spacing-card-xs);
|
||||
@@ -312,9 +312,9 @@ role-moderator {
|
||||
|
||||
&.has-body {
|
||||
grid-template:
|
||||
'icon author actions'
|
||||
'icon body actions'
|
||||
'date date date';
|
||||
"icon author actions"
|
||||
"icon body actions"
|
||||
"date date date";
|
||||
grid-template-columns: min-content auto 1fr;
|
||||
}
|
||||
}
|
||||
@@ -327,8 +327,8 @@ role-moderator {
|
||||
|
||||
&.has-body {
|
||||
grid-template:
|
||||
'icon author date actions'
|
||||
'icon body body actions';
|
||||
"icon author date actions"
|
||||
"icon body body actions";
|
||||
grid-template-columns: min-content auto 1fr;
|
||||
grid-template-rows: min-content 1fr auto;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg?component'
|
||||
import ThreadMessage from '~/components/ui/thread/ThreadMessage.vue'
|
||||
import ChevronRightIcon from "~/assets/images/utils/chevron-right.svg?component";
|
||||
import ThreadMessage from "~/components/ui/thread/ThreadMessage.vue";
|
||||
|
||||
const props = defineProps({
|
||||
thread: {
|
||||
@@ -49,36 +49,36 @@ const props = defineProps({
|
||||
type: Array,
|
||||
required: false,
|
||||
default() {
|
||||
return []
|
||||
return [];
|
||||
},
|
||||
},
|
||||
auth: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const app = useNuxtApp()
|
||||
const app = useNuxtApp();
|
||||
|
||||
const members = computed(() => {
|
||||
const members = {}
|
||||
const members = {};
|
||||
for (const member of props.thread.members) {
|
||||
members[member.id] = member
|
||||
members[member.id] = member;
|
||||
}
|
||||
members[props.auth.user.id] = props.auth.user
|
||||
return members
|
||||
})
|
||||
members[props.auth.user.id] = props.auth.user;
|
||||
return members;
|
||||
});
|
||||
|
||||
const displayMessages = computed(() => {
|
||||
const sortedMessages = props.thread.messages
|
||||
.slice()
|
||||
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created))
|
||||
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created));
|
||||
if (props.messages.length > 0) {
|
||||
return sortedMessages.filter((msg) => props.messages.includes(msg.id))
|
||||
return sortedMessages.filter((msg) => props.messages.includes(msg.id));
|
||||
} else {
|
||||
return sortedMessages.length > 0 ? [sortedMessages[sortedMessages.length - 1]] : []
|
||||
return sortedMessages.length > 0 ? [sortedMessages[sortedMessages.length - 1]] : [];
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user