diff --git a/code/datums/changelog/changelog.dm b/code/datums/changelog/changelog.dm index 8bdd2fb5795..e50933bd684 100644 --- a/code/datums/changelog/changelog.dm +++ b/code/datums/changelog/changelog.dm @@ -1,13 +1,21 @@ /datum/changelog var/static/list/changelog_items = list() + var/static/list/dates + var/static/list/testmerges /datum/changelog/tgui_state() return GLOB.tgui_always_state /datum/changelog/tgui_interact(mob/user, datum/tgui/ui) + if(isnull(dates)) + dates = get_dates() + if(isnull(testmerges)) + testmerges = get_testmerge_data() + ui = SStgui.try_update_ui(user, src, ui) if (!ui) ui = new(user, src, "Changelog") + ui.set_autoupdate(FALSE) ui.open() /datum/changelog/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) @@ -22,11 +30,62 @@ return ui.send_asset(changelog_item) /datum/changelog/tgui_static_data() - var/list/data = list( "dates" = list() ) - var/regex/ymlRegex = regex(@"\.yml", "g") + var/list/data = list() - for(var/archive_file in sortList(flist("html/changelogs/archive/"))) - var/archive_date = ymlRegex.Replace(archive_file, "") - data["dates"] = list(archive_date) + data["dates"] + data["dates"] = dates + data["testmerges"] = testmerges + data["discord_url"] = CONFIG_GET(string/discordurl) return data + +/datum/changelog/proc/get_dates() + . = list() + var/regex/yml_regex = regex(@"\.yml", "g") + for(var/archive_file in flist("html/changelogs/archive/")) + . += replacetext(archive_file, yml_regex, "") + +/datum/changelog/proc/get_testmerge_data() + . = list() + for(var/datum/tgs_revision_information/test_merge/testmerge in world.TgsTestMerges()) + if(!testmerge.body || findtext(testmerge.title, @"[s]")) + continue + var/list/changes = parse_github_changelog(testmerge.body) + if(!length(changes)) + changes = list("unknown" = list("Changes are not documented. Ask the author ([testmerge.author]) to add a changelog to their PR!")) + var/list/testmerge_data = list( + "title" = trimtext("[testmerge.title]"), + "number" = testmerge.number, + "author" = testmerge.author, + "link" = testmerge.url, + "changes" = changes, + ) + . += list(testmerge_data) + +/proc/parse_github_changelog(body) as /list + // these shouldn't be static, as this will only be called a few times at most in get_testmerge_data, which will only be called once. + var/regex/cl_pattern = new(@"(:cl:|🆑)([\S \t]*)$") + var/regex/entry_pattern = new(@"(\w+): (.+)") + var/regex/end_pattern = new(@"^/(:cl:|🆑)") + var/regex/newline_pattern = new(@"(\r\n|\r|\n)") + + // no changes, this is just using the default template + if(findtext_char(body, "add: Added new mechanics or gameplay changes")) + return + + var/started = FALSE + var/list/lines = splittext_char(trimtext(body), newline_pattern) + + for(var/line in lines) + line = trimtext(line) + if(findtext_char(line, end_pattern)) + break + if(started) + if (findtext_char(line, entry_pattern)) + var/change_type = trimtext(entry_pattern.group[1]) + var/change_desc = trimtext(entry_pattern.group[2]) + if(!change_type || !change_desc) + continue + LAZYADDASSOCLIST(., change_type, change_desc) + else + if(findtext_char(line, cl_pattern)) + started = TRUE diff --git a/tgui/packages/tgui/interfaces/Changelog.tsx b/tgui/packages/tgui/interfaces/Changelog.tsx deleted file mode 100644 index 924b885890b..00000000000 --- a/tgui/packages/tgui/interfaces/Changelog.tsx +++ /dev/null @@ -1,338 +0,0 @@ -import dateformat from 'dateformat'; -import yaml from 'js-yaml'; -import { Component, Fragment } from 'react'; -import { resolveAsset } from 'tgui/assets'; -import { useBackend } from 'tgui/backend'; -import { Window } from 'tgui/layouts'; -import { - Box, - Button, - Dropdown, - Icon, - Section, - Stack, - Table, -} from 'tgui-core/components'; -import { classes } from 'tgui-core/react'; -import { sendAct as act } from '../events/act'; - -const icons = { - add: { icon: 'check-circle', color: 'green' }, - admin: { icon: 'user-shield', color: 'purple' }, - balance: { icon: 'balance-scale-right', color: 'yellow' }, - bugfix: { icon: 'bug', color: 'green' }, - code_imp: { icon: 'code', color: 'green' }, - config: { icon: 'cogs', color: 'purple' }, - expansion: { icon: 'check-circle', color: 'green' }, - experiment: { icon: 'radiation', color: 'yellow' }, - image: { icon: 'image', color: 'green' }, - imageadd: { icon: 'tg-image-plus', color: 'green' }, - imagedel: { icon: 'tg-image-minus', color: 'red' }, - qol: { icon: 'hand-holding-heart', color: 'green' }, - refactor: { icon: 'tools', color: 'green' }, - rscadd: { icon: 'check-circle', color: 'green' }, - rscdel: { icon: 'times-circle', color: 'red' }, - server: { icon: 'server', color: 'purple' }, - sound: { icon: 'volume-high', color: 'green' }, - soundadd: { icon: 'tg-sound-plus', color: 'green' }, - sounddel: { icon: 'tg-sound-minus', color: 'red' }, - spellcheck: { icon: 'spell-check', color: 'green' }, - tgs: { icon: 'toolbox', color: 'purple' }, - tweak: { icon: 'wrench', color: 'green' }, - unknown: { icon: 'info-circle', color: 'label' }, - wip: { icon: 'hammer', color: 'orange' }, -}; - -type Data = { dates: string[] }; - -type ChangelogProps = Record; - -interface ChangelogState { - data: string | { date: string; authors: { name: string; changes: string[] } }; - selectedDate: string; - selectedIndex: number; -} -export class Changelog extends Component { - dateChoices: string[]; - - constructor(props) { - super(props); - this.state = { - data: 'Loading changelog data...', - selectedDate: '', - selectedIndex: 0, - }; - this.dateChoices = []; - } - - setData(data) { - this.setState({ data }); - } - - setSelectedDate(selectedDate) { - this.setState({ selectedDate }); - } - - setSelectedIndex(selectedIndex) { - this.setState({ selectedIndex }); - } - - getData = (date, attemptNumber = 1) => { - const self = this; - const maxAttempts = 6; - - if (attemptNumber > maxAttempts) { - return this.setData(`Failed to load data after ${maxAttempts} attempts`); - } - - act('get_month', { date }); - - fetch(resolveAsset(`${date}.yml`)).then(async (changelogData) => { - const result = await changelogData.text(); - const errorRegex = /^Cannot find/; - - if (errorRegex.test(result)) { - const timeout = 50 + attemptNumber * 50; - - self.setData(`Loading changelog data${'.'.repeat(attemptNumber + 3)}`); - setTimeout(() => { - self.getData(date, attemptNumber + 1); - }, timeout); - } else { - self.setData(yaml.load(result, { schema: yaml.CORE_SCHEMA })); - } - }); - }; - - componentDidMount() { - const { - data: { dates = [] }, - } = useBackend(); - - if (dates) { - dates.forEach((date) => { - this.dateChoices.push(dateformat(date, 'mmmm yyyy', true)); - }); - this.setSelectedDate(this.dateChoices[0]); - this.getData(dates[0]); - } - } - - render() { - const { data, selectedDate, selectedIndex } = this.state; - const { - data: { dates }, - } = useBackend(); - const { dateChoices } = this; - - const dateDropdown = dateChoices.length > 0 && ( - - -