1
1
// ==UserScript==
2
2
// @name UnityForumFixer
3
3
// @namespace https://unitycoder.com/
4
- // @version 0.1 (21 .08.2024)
5
- // @description Fixes For Unity Forums
4
+ // @version 0.3 (23 .08.2024)
5
+ // @description Fixes For Unity Forums - https://github.com/unitycoder/UnityForumFixer
6
6
// @author unitycoder.com
7
7
// @match https://discussions.unity.com/*
8
8
// @grant none
17
17
18
18
AppendCustomCSS ( ) ;
19
19
AddAssetStoreLink ( ) ;
20
- // ShowOriginalPosterInfo (); // TODO needs some css adjustments for name location
21
- //setTimeout(test, 1000);
20
+ NavBar ( ) ;
21
+ TopicsViewShowOriginalPosterInfo ( ) ; // TODO needs some css adjustments for name location
22
22
FixPostActivityTime ( ) ;
23
+ PostViewShowOriginalPosterInfo ( ) ;
24
+
23
25
setTimeout ( OnUpdate , 1000 ) ; // run loop to update activity times (since some script changes them back to original..)
24
- NavBar ( ) ;
25
26
} ) ;
26
27
} ) ( ) ;
27
28
29
+ // runs every second to update things (if you scroll the page, need to update new data)
30
+ // TODO could be better to catch page change/update some other way (xhrevent, mutation..), since it doesnt update now if click some item (like open post)
28
31
function OnUpdate ( )
29
32
{
30
33
FixPostActivityTime ( ) ;
34
+ TopicsViewShowOriginalPosterInfo ( ) ;
31
35
setTimeout ( OnUpdate , 1000 ) ;
32
36
}
33
37
38
+
39
+
34
40
function AppendCustomCSS ( )
35
41
{
36
42
37
43
var style = document . createElement ( 'style' ) ;
38
44
style . textContent =
39
45
`
46
+ // latest posts view
47
+ .show-more.has-topics { width: 35%;!important;} /* updated topics alert */
48
+ .alert.alert-info.clickable {width: 35%; padding:3px !important;} /* updated topics alert */
49
+
50
+
40
51
.wrap.custom-search-banner-wrap h1 {display: none;} /* hide welcome banner */
41
52
.wrap.custom-search-banner-wrap {padding:0px;} /* remove search bar padding */
42
53
:root {--d-background-image: none !important;} /* hide big bg image */
@@ -54,23 +65,51 @@ function AppendCustomCSS()
54
65
.title.raw-link.raw-topic-link:hover {color: rgb(82,132,189) !important; text-decoration: underline !important;} /*post title hover */
55
66
.topic-list .topic-list-data:first-of-type {padding-left: 8px !important;} /* post topic rows, half the padding */
56
67
.discourse-tags {font-size: 0.8em !important;} /* tags below post title, smaller */
57
- .post-activity {font-size: 0.9em !important;}
68
+ .relative-date {font-size: 0.9em !important; color: rgb(150, 150, 150) !important;}
58
69
.ember-view.bread-crumbs-left-outlet.breadcrumb-label {display: none !important;} /* "… or filter the topics via" */
59
70
.navigation-container {--nav-space: 0 !important; padding-bottom: 6px;} /* navbar adjustments */
60
71
.category-breadcrumb.ember-view {width:auto !important;} /* areas,categories,tags not 100% width */
61
72
.navigation-controls { display: flex; justify-content: flex-end; width: 100%; } /* move new topic button to right, still would be nice to have in same row as other nav */
62
73
.select-kit-row .desc { font-size: 0.92em !important; margin-top:1px; color: #777777 !important; } /* new topic dropdown descriptions */
63
74
64
75
.custom-search-banner-wrap > div {max-width:100% !important;} /* search bar maxwidth, need to find better location later */
65
- .sidebar-wrapper {width: 222px !important; font-size: 0.99em !important;} /* sidebar */
76
+ .sidebar-wrapper {font-size: 0.99em !important;} /* sidebar */
66
77
.sidebar-section-header-wrapper.sidebar-row {padding:4px !important;} /* sidebar headers bit to the left */
67
78
.ember-view.sidebar-section-link.sidebar-row {height:25px !important;} /* sidebar row heights */
68
79
.sidebar-section-link-prefix .svg-icon {height: 12px !important; width: 12px !important;} /* sidebar icons smaller */
69
80
81
+ /* post view, username */
82
+ .user-name { margin-bottom: 5px; font-weight: bold; text-align: center; font-size: 0.9em; color: var(--primary); text-decoration: none; display: block; word-wrap: break-word; white-space: normal; width: 100%; }
83
+ .user-name:hover { color: rgb(82,132,189); text-decoration: underline; }
84
+ .names.trigger-user-card {visibility: hidden !important;}
85
+ .row { display: flex; }
86
+ .topic-avatar { flex-basis: 10%; margin:0 !important; }
87
+ .topic-body { flex-basis: 90%; } /* Ensure the main content adjusts accordingly */
88
+ .topic-avatar {background-color: #d1d1d132;}
89
+ .post-avatar { display: flex; flex-direction: column; align-items: center; }
90
+ /*.avatar { margin: 4px; } bug in topic view*/
91
+ .topic-body {padding: 0 !important;}
92
+ .topic-map.--op {display: none !important;} /* hide view count under op post, could move it somewhere else later */
93
+
94
+ .more-topics__container {display:none !important;} /* hide suggested topics at bottom */
95
+ /* unity footer & content - could hide it.. but then unity is sad*/
96
+ .unity-footer {font-size:0.7em !important; line-height: none !important; padding:0 !important; text-align:center !important;}
97
+ .footer.unity-footer .unity-footer-content {padding-left:10px !important; line-height: 12px !important;}
98
+ .unity-footer-content { display: flex; flex-direction: column; align-items: center; text-align: center; }
99
+ .unity-footer-menu.unity-footer-menu-legal.processed { list-style: none; padding: 0; margin: 0; display: flex; justify-content: center; }
100
+ .unity-footer-menu.unity-footer-menu-legal.processed li { margin: 0 10px; }
101
+
102
+
103
+ /* custom added fields */
104
+ .original-poster-span {font: 13px/1.231 arial,helvetica,clean,sans-serif; color: rgb(150, 150, 150); } /* original poster below post title */
105
+ .latest-poster-span { display: block; word-break: break-all; max-width: 100%; } /* activity, latest poster */
106
+
70
107
` ;
71
108
document . head . appendChild ( style ) ;
72
109
}
73
110
111
+ // HEADER
112
+
74
113
function AddAssetStoreLink ( )
75
114
{
76
115
// Create the new list item
@@ -91,90 +130,162 @@ function AddAssetStoreLink()
91
130
}
92
131
}
93
132
94
- function ShowOriginalPosterInfo ( )
133
+ function NavBar ( )
134
+ {
135
+ // remove "try the" text
136
+ document . querySelectorAll ( 'div[title="Try the Product Areas"] .name' ) . forEach ( el => {
137
+ if ( el . textContent . trim ( ) === "Try the Product Areas" ) {
138
+ el . textContent = "Product Areas" ;
139
+ }
140
+ } ) ;
141
+ }
142
+
143
+
144
+ // FORUM VIEW
145
+
146
+ function TopicsViewShowOriginalPosterInfo ( )
95
147
{
96
- // Select all <td> elements with the class 'posters topic-list-data'
97
- var posterCells = document . querySelectorAll ( 'td.posters. topic-list-data ' ) ;
148
+ // Select all topic rows
149
+ const topicRows = document . querySelectorAll ( 'tr. topic-list-item ' ) ;
98
150
99
- // Loop through each <td> element
100
- posterCells . forEach ( function ( cell )
101
- {
102
- // Select the first <a> element with a data-user-card attribute inside the current <td>
103
- var firstUserLink = cell . querySelector ( 'a[data-user-card]' ) ;
104
- if ( firstUserLink )
105
- {
106
- // Get the value of the data-user-card attribute
107
- var userCardValue = firstUserLink . getAttribute ( 'data-user-card' ) ;
108
-
109
- // Create a new <div> element to display the user card value
110
- var userCardDiv = document . createElement ( 'div' ) ;
111
- userCardDiv . textContent = userCardValue ;
112
- userCardDiv . style . fontSize = '12px' ; // Optional: make the text smaller
113
- userCardDiv . style . marginTop = '4px' ; // Optional: add some space above the text
114
-
115
- // Insert the new <div> below the first image
116
- firstUserLink . parentNode . insertBefore ( userCardDiv , firstUserLink . nextSibling ) ;
117
- }
118
- } ) ;
151
+ topicRows . forEach ( row => {
152
+ // Find the first 'a' element inside the 'posters topic-list-data' cell that does not have the 'latest' class (Original Poster)
153
+ let firstPosterLink = row . querySelector ( 'td.posters.topic-list-data a:not(.latest)' ) ;
154
+
155
+ // If there is no such element, it might be a single poster with 'latest single' class
156
+ if ( ! firstPosterLink ) {
157
+ firstPosterLink = row . querySelector ( 'td.posters.topic-list-data a.latest.single' ) ;
158
+ }
159
+
160
+ if ( firstPosterLink ) {
161
+ // Extract the username from the 'data-user-card' attribute for the original poster
162
+ const originalPosterUsername = firstPosterLink . getAttribute ( 'data-user-card' ) ;
163
+
164
+ // Find the topic creation date from the title attribute in the activity column
165
+ const activityCell = row . querySelector ( 'td.activity' ) ;
166
+ const titleText = activityCell ? activityCell . getAttribute ( 'title' ) : '' ;
167
+ const creationDateMatch = titleText . match ( / C r e a t e d : ( .+ ?) (?: \n | $ ) / ) ;
168
+
169
+ let creationDateFormatted = 'Unknown' ; // Default to "Unknown" if no date is found
170
+ if ( creationDateMatch ) {
171
+ const creationDateStr = creationDateMatch [ 1 ] ;
172
+ const creationDate = new Date ( creationDateStr ) ;
173
+ creationDateFormatted = formatDateString ( creationDate ) ;
174
+ }
175
+
176
+ // Find the 'link-bottom-line' element to insert the original poster's name and creation date before it
177
+ const linkBottomLine = row . querySelector ( 'td.main-link .link-bottom-line' ) ;
178
+ if ( linkBottomLine && ! row . querySelector ( '.original-poster-span' ) ) {
179
+ // Create a new span element for the original poster's username and creation date
180
+ const originalPosterSpan = document . createElement ( 'span' ) ;
181
+ originalPosterSpan . textContent = originalPosterUsername + "," + creationDateFormatted ;
182
+ originalPosterSpan . className = 'original-poster-span' ; // Adding a class to prevent duplication
183
+ originalPosterSpan . style . display = 'block' ; // Ensure it's placed as a block element
184
+
185
+ // Insert the original poster span before the link-bottom-line
186
+ linkBottomLine . parentNode . insertBefore ( originalPosterSpan , linkBottomLine ) ;
187
+ }
188
+ }
189
+
190
+ // Find the most recent poster (always marked with 'latest')
191
+ const latestPosterLink = row . querySelector ( 'td.posters.topic-list-data a.latest' ) ;
192
+ if ( latestPosterLink ) {
193
+ // Extract the username from the 'data-user-card' attribute
194
+ const latestPosterUsername = latestPosterLink . getAttribute ( 'data-user-card' ) ;
195
+
196
+ // Find the 'post-activity' element
197
+ const postActivity = row . querySelector ( 'td.activity .post-activity' ) ;
198
+ if ( postActivity && ! row . querySelector ( '.latest-poster-span' ) ) {
199
+ // Create a new span element for the latest poster's username
200
+ const latestPosterSpan = document . createElement ( 'span' ) ;
201
+ latestPosterSpan . textContent = latestPosterUsername ;
202
+ latestPosterSpan . className = 'latest-poster-span' ; // Adding a class to prevent duplication
203
+ latestPosterSpan . style . display = 'block' ; // Ensure it's placed as a block element
204
+
205
+ // Insert the latest poster span before the <a> tag, placing it outside the link
206
+ postActivity . parentNode . insertBefore ( latestPosterSpan , postActivity ) ;
207
+ }
208
+ }
209
+ } ) ;
119
210
}
120
211
121
- function FixPostActivityTime ( )
212
+ function FixPostActivityTime ( )
122
213
{
123
- document . querySelectorAll ( '.relative-date' ) . forEach ( function ( el ) {
124
- const dataTime = parseInt ( el . getAttribute ( 'data-time' ) , 10 ) ;
125
- if ( ! dataTime ) return ;
214
+ document . querySelectorAll ( '.relative-date' ) . forEach ( function ( el )
215
+ {
216
+ const dataTime = parseInt ( el . getAttribute ( 'data-time' ) , 10 ) ;
217
+ if ( ! dataTime ) return ;
126
218
127
- const date = new Date ( dataTime ) ;
128
- const now = new Date ( ) ;
129
- const diffInHours = Math . floor ( ( now - date ) / ( 1000 * 60 * 60 ) ) ;
219
+ const date = new Date ( dataTime ) ;
220
+ const now = new Date ( ) ;
221
+ const diffInMinutes = Math . floor ( ( now - date ) / ( 1000 * 60 ) ) ;
222
+ const diffInHours = Math . floor ( diffInMinutes / 60 ) ;
130
223
131
- let timeString ;
132
- if ( diffInHours > 6 ) {
133
- timeString = formatDateString ( date ) ;
224
+ let timeString ;
225
+ if ( diffInHours >= 1 ) {
226
+ const remainingMinutes = diffInMinutes % 60 ;
227
+ if ( remainingMinutes > 0 ) {
228
+ timeString = `${ diffInHours } hour${ diffInHours !== 1 ? 's' : '' } ${ remainingMinutes } minute${ remainingMinutes !== 1 ? 's' : '' } ago` ;
134
229
} else {
135
- const diffInMinutes = Math . floor ( ( now - date ) / ( 1000 * 60 ) ) ;
136
- timeString = `${ diffInMinutes } minute${ diffInMinutes !== 1 ? 's' : '' } ago` ;
230
+ timeString = `${ diffInHours } hour${ diffInHours !== 1 ? 's' : '' } ago` ;
137
231
}
232
+ } else if ( diffInMinutes >= 1 ) {
233
+ timeString = `${ diffInMinutes } minute${ diffInMinutes !== 1 ? 's' : '' } ago` ;
234
+ } else {
235
+ timeString = `just now` ;
236
+ }
138
237
139
- el . textContent = timeString ;
140
- } ) ;
238
+ el . textContent = timeString ;
239
+ } ) ;
141
240
}
142
241
143
242
243
+ // POST VIEW
144
244
245
+ function PostViewShowOriginalPosterInfo ( )
246
+ {
247
+ // Select all elements that contain the avatar with a data-user-card attribute
248
+ document . querySelectorAll ( '.trigger-user-card.main-avatar' ) . forEach ( function ( avatar ) {
249
+ // Get the user name from the data-user-card attribute
250
+ var userName = avatar . getAttribute ( 'data-user-card' ) ;
251
+
252
+ // Create a new anchor element to wrap the user name and link to the profile
253
+ var userLink = document . createElement ( 'a' ) ;
254
+ userLink . className = 'user-name' ;
255
+ userLink . href = 'https://discussions.unity.com/u/' + userName ;
256
+ userLink . textContent = userName ;
257
+
258
+ // Insert the user name link before the avatar image
259
+ avatar . parentNode . insertBefore ( userLink , avatar ) ;
260
+ } ) ;
261
+ }
262
+
263
+
264
+
265
+ // HELPER METHODS
145
266
146
267
function formatDate ( date )
147
268
{
148
- const options = { hour : '2-digit' , minute : '2-digit' , hour12 : false } ;
149
- return date . toLocaleTimeString ( 'en-GB' , options ) ; // Format as "HH:MM"
269
+ const options = { hour : '2-digit' , minute : '2-digit' , hour12 : false } ;
270
+ return date . toLocaleTimeString ( 'en-GB' , options ) ; // Format as "HH:MM"
150
271
}
151
272
152
273
function formatDateString ( date )
153
274
{
154
- const today = new Date ( ) ;
155
- const yesterday = new Date ( today ) ;
156
- yesterday . setDate ( today . getDate ( ) - 1 ) ;
157
- const oneWeekAgo = new Date ( today ) ;
158
- oneWeekAgo . setDate ( today . getDate ( ) - 7 ) ;
159
-
160
- if ( date >= today . setHours ( 0 , 0 , 0 , 0 ) ) { // Today
161
- return `Today at ${ formatDate ( date ) } ` ;
162
- } else if ( date >= yesterday . setHours ( 0 , 0 , 0 , 0 ) ) { // Yesterday
163
- return `Yesterday at ${ formatDate ( date ) } ` ;
164
- } else if ( date >= oneWeekAgo ) { // Within the past week
165
- const dayName = date . toLocaleDateString ( 'en-GB' , { weekday : 'long' } ) ;
166
- return `${ dayName } at ${ formatDate ( date ) } ` ;
167
- } else { // Older than one week
168
- return date . toLocaleDateString ( 'en-GB' , { day : '2-digit' , month : 'short' , year : 'numeric' } ) ;
169
- }
170
- }
275
+ const today = new Date ( ) ;
276
+ const yesterday = new Date ( today ) ;
277
+ yesterday . setDate ( today . getDate ( ) - 1 ) ;
278
+ const oneWeekAgo = new Date ( today ) ;
279
+ oneWeekAgo . setDate ( today . getDate ( ) - 7 ) ;
171
280
172
- function NavBar ( )
173
- {
174
- // remove "try the" text
175
- document . querySelectorAll ( 'div[title="Try the Product Areas"] .name' ) . forEach ( el => {
176
- if ( el . textContent . trim ( ) === "Try the Product Areas" ) {
177
- el . textContent = "Product Areas" ;
281
+ if ( date >= today . setHours ( 0 , 0 , 0 , 0 ) ) { // Today
282
+ return `Today at ${ formatDate ( date ) } ` ;
283
+ } else if ( date >= yesterday . setHours ( 0 , 0 , 0 , 0 ) ) { // Yesterday
284
+ return `Yesterday at ${ formatDate ( date ) } ` ;
285
+ } else if ( date >= oneWeekAgo ) { // Within the past week
286
+ const dayName = date . toLocaleDateString ( 'en-GB' , { weekday : 'long' } ) ;
287
+ return `${ dayName } at ${ formatDate ( date ) } ` ;
288
+ } else { // Older than one week
289
+ return date . toLocaleDateString ( 'en-GB' , { day : '2-digit' , month : 'short' , year : 'numeric' } ) ;
178
290
}
179
- } ) ;
180
291
}
0 commit comments